/*
 * dcc.c: Things dealing client to client connections. 
 *
 * Written By Troy Rollo <troy@cbme.unsw.oz.au> 
 *
 * Copyright(c) 1991 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 * Heavily modified Colten Edwards 1996-97
 */

#include "irc.h"
#include <sys/stat.h>
#include <stdarg.h>
#include <stdio.h>
#include <dirent.h>


#include "ctcp.h"
#include "crypt.h"
#include "cdcc.h"
#include "dcc.h"
#include "hook.h"
#include "ircaux.h"
#include "lastlog.h"
#include "newio.h"
#include "output.h"
#include "parse.h"
#include "server.h"
#include "status.h"
#include "vars.h"
#include "whois.h"
#include "window.h"
#include "ircterm.h"
#include "hook.h"
#include "misc.h"
#include "tcl_bx.h"
#include "screen.h"
#include "userlist.h"
#include "hash2.h"

#include <float.h>
#ifdef WINNT
#include <windows.h>
#endif


static	off_t		filesize = 0;

DCC_list *	ClientList = NULL;

static	char		DCC_current_transfer_buffer[BIG_BUFFER_SIZE/4];
extern	int		dgets_errno;


static	void    	add_to_dcc_buffer _((DCC_list *, char *));
static	void 		dcc_add_deadclient _((register DCC_list *));
static	void 		dcc_close _((char *, char *));
static	void		dcc_getfile _((char *, char *));
	int		dcc_open _((DCC_list *));
static	void 		dcc_really_erase _((void));
static	void		dcc_rename _((char *, char *));
static	void		dcc_send_raw _((char *, char *));
static	void 		output_reject_ctcp _((char *, char *));
static	void		process_incoming_chat _((register DCC_list *));
static	void		process_incoming_listen _((register DCC_list *));
static	void		process_incoming_raw _((register DCC_list *));
static	void		process_outgoing_file _((register DCC_list *, int));
static	void		process_incoming_file _((register DCC_list *));
static	void		DCC_close_filesend _((DCC_list *, char *));
static	void		update_transfer_buffer _((char *format, ...));

#ifdef NON_BLOCKING_CONNECTS
static	void	dcc_got_connected _((DCC_list *));
#endif

static  void    dcc_update_stats _((DCC_list *));
static	void	dcc_set_paths _((char *, char *));
static  void	dcc_set_quiet _((char *, char *));
static  void	dcc_tog_auto _((char *, char *));
static	void	dcc_show_active _((char *, char *));
static	void	dcc_help1 _((char *, char *));

static unsigned char byteordertest _((void));
static void dcc_reject_notify _((char *, char *, char *));
static	int	get_to_from _((char *));

static	char *strip_path _((char *));
static	void dcc_tog_rename _((char *, char *)); 
static	char *check_paths _((char *));
static	void dcc_overwrite_toggle _((char *, char *));

static	void dcc_ftpget _((char *, char *));
static	void dcc_ftpsend _((char *, char *));
static	void dcc_nick _((char *, char *));

static	void process_outgoing_ftp _((DCC_list *));
static	void process_incoming_ftp _((register DCC_list *));
#ifdef FTP_XMIT
static	void dcc_xmitget _((char *, char *, char *, char *));
#endif

#ifdef MIRC_BROKEN_DCC_RESUME
	void		dcc_getfile_resume _((char *, char *));
static 	void 		dcc_getfile_resume_demanded _((char *user, char *filename, char *port, char *offset));
static	void		dcc_getfile_resume_start _((char *nick, char *filename, char *port, char *offset));
#endif



typedef void (*dcc_function) _((char *, char *));
typedef struct
{
	char	*	name;
	dcc_function 	function;
} DCC_commands;

DCC_commands	*dcc_dllcommands = { NULL };

DCC_commands	dcc_commands[] =
{
	{ "ACTIVE",	dcc_show_active },
	{ "AUTO",	dcc_tog_auto },
	{ "AUTO_RENAME",dcc_tog_rename },
	{ "BOT",	dcc_chatbot },
	{ "CHAT",	dcc_chat },
	{ "LIST",	dcc_list },
	{ "GLIST",	dcc_glist },

	{ "TSEND",	dcc_filesend },
	{ "SEND",	dcc_filesend },
	{ "RESEND",	dcc_resend }, 
	{ "TRESEND",	dcc_resend }, 

	{ "GET",	dcc_getfile },
	{ "TGET",	dcc_getfile },
	{ "REGET",	dcc_regetfile },
	{ "TREGET",	dcc_regetfile },

	{ "CLOSE",	dcc_close },
	{ "RENAME",	dcc_rename },

#ifdef MIRC_BROKEN_DCC_RESUME
	{ "RESUME",	dcc_getfile_resume },
#endif

	{ "RAW",	dcc_send_raw },
	{ "QUIET",	dcc_set_quiet },
	{ "STATS",	dcc_stats },
	{ "PATHS",	dcc_set_paths },
	{ "OVERWRITE",	dcc_overwrite_toggle },
	{ "FTP",	dcc_ftpopen },

	{ "XRECV",	dcc_ftpget },
	{ "XSEND",	dcc_ftpsend },
	{ "EXEMPT",	dcc_nick },

	{ "?",		dcc_help1 },
	{ "HELP",	dcc_help1 },
	{ NULL,		(dcc_function) NULL }
};

#define BAR_LENGTH 50
static	int	dcc_count = 1;
	int	dcc_active_count = 0;
static	int	doing_multi = 0;
static	int	dcc_quiet = 0;
static	int	dcc_paths = 0;
static	int	dcc_overwrite_var = 0;
	double	dcc_bytes_in = 0;
	double	dcc_bytes_out = 0;
	double  dcc_max_rate_in = 0.0;
static 	double  dcc_min_rate_in = DBL_MAX;
	double  dcc_max_rate_out = 0.0;
static 	double  dcc_min_rate_out = DBL_MAX;

 	unsigned int	send_count_stat = 0;
	unsigned int	get_count_stat = 0;

	char	*last_chat_req = NULL;

#define DCC_HASHSIZE 11
HashEntry dcc_no_flood[DCC_HASHSIZE];


char	*dcc_types[] =
{
	"<none>",
	"CHAT",
	"SEND",
	"GET",
	"RAW_LISTEN",
	"RAW",
	"RESEND",
	"REGET",
	"BOT",
	"FTP",
	"FTPGET",
	"FTPSEND",
	"XMITSEND",
	"XMITRECV",
	NULL
};

struct	deadlist
{
	DCC_list *it;
	struct deadlist *next;
}	*deadlist = NULL;



static int dccBlockSize(void)
{
register int BlockSize;
	BlockSize = get_int_var(DCC_BLOCK_SIZE_VAR);
	if (BlockSize > MAX_DCC_BLOCK_SIZE)
	{
		BlockSize = MAX_DCC_BLOCK_SIZE;
		set_int_var(DCC_BLOCK_SIZE_VAR, MAX_DCC_BLOCK_SIZE);
	}
	else if (BlockSize < 16)
	{
		BlockSize = 16;
		set_int_var(DCC_BLOCK_SIZE_VAR, 16);
	}
	return (BlockSize);
}
/*
 * dcc_searchlist searches through the dcc_list and finds the client
 * with the the flag described in type set.
 */
DCC_list *dcc_searchlist(char *name, char *user, int type, int flag, char *othername, char *userhost, int active)
{
	register DCC_list **Client = NULL, *NewClient = NULL;
	
	int dcc_num = 0;
	if (user && *user == '#' && my_atol(user+1)) 
	{
		/* we got a number so find that instead */
		char *p;
		p = user;
		p++;
		dcc_num = my_atol(p);
		if (dcc_num > 0)
		{
			for(Client = (&ClientList); *Client; Client = (&(**Client).next))
				if (dcc_num && ((**Client).dccnum == dcc_num))
					return *Client;
		}
	} 
	else
	{
	for (Client = (&ClientList); *Client ; Client = (&(**Client).next))
	{
		/* 
		 * The following things have to be true:
		 * -> The types have to be the same
		 * -> One of the following things is true:
		 *      -> `name' is NULL or `name' is the same as the 
		 *	   entry's description
		 *          *OR*
		 *	-> `othername' is the same as the entry's othername
		 * -> `user' is the same as the entry's user-perr
		 * -> One of the following is true:
		 *	-> `active' is 1 and the entry is active
		 *	-> `active' is 0 and the entry is not active
		 *	-> `active' is -1 (dont care)
		 */
		if (
		     ( ((**Client).flags & DCC_TYPES) == type ) &&
		     ( (!name || ((**Client).description && !my_stricmp(name, (**Client).description))) ||
		       (othername && (**Client).othername && !my_stricmp(othername, (**Client).othername))   ) &&
		     ( user && !my_stricmp(user, (**Client).user) ) &&
		     (  (active == -1) ||
		       ((active == 0) && !((**Client).flags & DCC_ACTIVE)) ||
		       ((active == 1) && ((**Client).flags & DCC_ACTIVE))
		     )
		   )
			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;
	malloc_strcpy(&NewClient->description, name);
	malloc_strcpy(&NewClient->user, user);
	malloc_strcpy(&NewClient->othername, othername);
	malloc_strcpy(&NewClient->userhost, userhost);
	NewClient->packets_total = filesize ? (filesize / dccBlockSize() + 1) : 0;
	get_time(&NewClient->lasttime);
	if (dcc_count == 0) dcc_count = 1;
	NewClient->dccnum = dcc_count++;
	return NewClient;
}

/*
 * Added by Chaos: Is used in edit.c for checking redirect.
 */
extern int	dcc_active (char *user)
{
	return (dcc_searchlist("chat", user, DCC_CHAT, 0, NULL, NULL, 1)) ? 1 : 0;
}

extern int	dcc_activebot (char *user)
{
	return (dcc_searchlist("chat", user, DCC_BOTMODE, 0, NULL, NULL, 1)) ? 1 : 0;
}

extern int	dcc_activeraw (char *user)
{
	return (dcc_searchlist(/*"RAW"*/NULL, user, DCC_RAW, 0, NULL, NULL, 1)) ? 1 : 0;
}


static	void dcc_add_deadclient(register 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
 */
int dcc_erase(DCC_list *Element)
{
	register DCC_list	**Client;
	int		erase_one = 0;
	dcc_count = 1;
		
	for (Client = &ClientList; *Client; Client = &(**Client).next)
	{
		if (*Client == Element)
		{
			*Client = Element->next;
			if (Element->write > -1)
			{
				FD_CLR(Element->write, &writables);
				close(Element->write);
			}
			if (Element->read > -1)
			{
				FD_CLR(Element->read, &readables);
				close(Element->read);
			}
			if (Element->file > -1)
				close(Element->file);

			new_free(&Element->othername);
			new_free(&Element->description);
			new_free(&Element->userhost);
			new_free(&Element->user);
			new_free(&Element->buffer);
			new_free(&Element->encrypt);
			new_free(&Element->cksum);
			if (Element->dcc_handler)
				(Element->dcc_handler)(NULL, NULL);
			new_free((char **)&Element);
			erase_one++;
			break;
		}
	}
	for (Client = &ClientList; *Client; Client = &(**Client).next)
		(*Client)->dccnum = dcc_count++;
	if (erase_one)
	{
		*DCC_current_transfer_buffer = 0;
	}
	return erase_one;
}

static void dcc_really_erase _((void))
{
	register struct deadlist *dies;
	for (;(dies = deadlist) != NULL;)
	{
		deadlist = deadlist->next;
		dcc_erase(dies->it);
	}
}

/*
 * Set the descriptor set to show all fds in Client connections to
 * be checked for data.
 */
extern void	set_dcc_bits (fd_set *rd, fd_set *wd)
{
	register DCC_list	*Client;
	unsigned int flag;
	unsigned int fast_dcc = get_int_var(DCC_FAST_VAR);
		
	for (Client = ClientList; Client != NULL; Client = Client->next)
	{

		flag = Client->flags & DCC_TYPES;
		if (Client->write != -1)
		{
#ifdef DCC_CNCT_PEND
			if (Client->flags & DCC_CNCT_PEND)
				FD_SET(Client->write, wd);
#endif
#ifdef NON_BLOCKING_CONNECTS
			else if (((flag == DCC_FILEOFFER) || 
				(flag == DCC_RESENDOFFER) ||
				(flag == DCC_FTPSEND)) && !Client->eof && fast_dcc)
				FD_SET(Client->write, wd);
#else
				;
#endif
		}
		if (Client->read != -1)
			FD_SET(Client->read, rd);
	}
	if  (!ClientList && dcc_active_count) 
		dcc_active_count = 0; /* A HACK */
}

#ifdef NON_BLOCKING_CONNECTS
static	void dcc_got_connected(DCC_list *client)
{
struct sockaddr_in	remaddr = {0};
int	rl = sizeof(remaddr);

	if (getpeername(client->read, (struct sockaddr *) &remaddr, &rl) != -1)
	{

		if (client->flags & DCC_OFFER)
		{
			client->flags &= ~DCC_OFFER;

			if ((client->flags & DCC_TYPES) != DCC_RAW)
			{

				if (do_hook(DCC_CONNECT_LIST, "%s %s %s %d", client->user, 
					dcc_types[client->flags&DCC_TYPES],inet_ntoa(remaddr.sin_addr),
					ntohs(remaddr.sin_port))) 
				{
					if (!dcc_quiet)
						put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
						"%s %s %s %s %s %d", update_clock(GET_TIME), 
						dcc_types[client->flags&DCC_TYPES], client->user, 
						client->userhost?client->userhost:"u@h", inet_ntoa(remaddr.sin_addr), (int) ntohs(remaddr.sin_port)));
				}

			}
		}
		get_time(&client->starttime);
		client->flags &= ~DCC_CNCT_PEND;
		if (((client->flags & DCC_TYPES) == DCC_REGETFILE))
		{
		        /* send a packet to the sender with transfer resume instructions */
			put_it("%s", convert_output_format("$G %RDCC %YTelling uplink we want to start at%n: $0", "%l", client->transfer_orders.byteoffset));
	       	        send(client->read, (const char *)&client->transfer_orders, sizeof(struct transfer_struct), 0);
		} 
		else if (((client->flags & DCC_TYPES) == DCC_FTPOPEN))
		{
			if ((dcc_printf(client->read, "user %s\n", client->othername) < 0) || (dcc_printf(client->read, "pass %s\n", client->encrypt) < 0))
			{
				client->flags |= DCC_DELETE;
				put_it(convert_output_format("%gFTP%n command failed write", NULL, NULL));
				new_free(&client->encrypt);
				return;
			}
			client->flags &= ~DCC_WAIT;
			client->flags |= DCC_ACTIVE;
			dcc_printf(client->read, "type i\n");
			new_free(&client->encrypt);
			return;
		}
		if (!get_int_var(DCC_FAST_VAR) && !(client->flags & DCC_TDCC))
		{
			set_blocking(client->read);
			if (client->read != client->write)
				set_blocking(client->write);
		}
	}
	
}
#endif

/*
 * Check all DCCs for data, and if they have any, perform whatever
 * actions are required.
 */
extern void	dcc_check (fd_set *Readables, fd_set *Writables)
{
	register DCC_list	**Client;
	struct	timeval	timeout;
	int	previous_server;
	int	lastlog_level;
	register int	flags;
#ifdef NON_BLOCKING_CONNECTS
	int	fast_dcc = get_int_var(DCC_FAST_VAR);
#endif
		
	previous_server = from_server;
	from_server = (-1);
	timeout.tv_sec = timeout.tv_usec = 0;
	message_from(NULL, LOG_DCC);
	lastlog_level = set_lastlog_msg_level(LOG_DCC);

	for (Client = (&ClientList); *Client != NULL;)
	{
		flags = (*Client)->flags;
#ifdef NON_BLOCKING_CONNECTS
		if (flags & DCC_CNCT_PEND)
			dcc_got_connected(*Client);
		if (fast_dcc && ((flags & DCC_TYPES) != DCC_RAW_LISTEN) && (*Client)->write != -1 && FD_ISSET((*Client)->write, Writables))
		{
			FD_CLR((*Client)->write, Writables);
			switch(flags & DCC_TYPES)
			{

				case DCC_FTPSEND:
					process_outgoing_ftp(*Client);
					break;
				case DCC_RESENDOFFER:
				case DCC_FILEOFFER:
					process_outgoing_file(*Client, 0);
			}
		}
#endif
		if (!(flags & DCC_DELETE) && (*Client)->read != -1)
		{
			if (FD_ISSET((*Client)->read, Readables))
			{
				FD_CLR((*Client)->read, Readables);
				switch(flags&DCC_TYPES)
				{
					case DCC_BOTMODE:
					case DCC_CHAT:
						from_server = previous_server;
						process_incoming_chat(*Client);
						from_server = -1;
						break;
					case DCC_RAW_LISTEN:
						process_incoming_listen(*Client);
						break;
					case DCC_RAW:
						process_incoming_raw(*Client);
						break;
					case DCC_FILEOFFER:
					case DCC_RESENDOFFER:
						process_outgoing_file(*Client, 1);
						break;
					case DCC_FILEREAD:
					case DCC_REGETFILE:
						process_incoming_file(*Client);
						break;
					case DCC_FTPOPEN:
					case DCC_FTPGET:
						process_incoming_ftp(*Client);
						break;
					case DCC_FTPSEND:
						process_outgoing_ftp(*Client);
						break;
					case DCC_XMITSEND:
					case DCC_XMITRECV:
						break;
				}
			}
		}
		if (flags & DCC_DELETE)
			dcc_add_deadclient(*Client);
		Client = (&(**Client).next);
	}

	message_from(NULL, LOG_CRAP);
	(void) set_lastlog_msg_level(lastlog_level);
	dcc_really_erase();
	from_server = previous_server;
}

/*
 * Process a DCC command from the user.
 */
extern void	process_dcc(char *args)
{
	char	*command;
	int	i;
	int	lastlog_level;

	if (!(command = next_arg(args, &args)))
		return;
	for (i = 0; dcc_commands[i].name != NULL; i++)
	{
		if (!my_stricmp(dcc_commands[i].name, command))
		{
			message_from(NULL, LOG_DCC);
			lastlog_level = set_lastlog_msg_level(LOG_DCC);
			dcc_commands[i].function(dcc_commands[i].name, args);
			message_from(NULL, LOG_CRAP);
			(void) set_lastlog_msg_level(lastlog_level);
			return;
		}
	}
	put_it("%s", convert_output_format("$G Unknown %RDCC%n command: $0", "%s", command));
}

int dcc_open _((DCC_list *Client))
{
char    		*user,
			*Type;

struct	in_addr 	myip;

#ifndef NON_BLOCKING_CONNECTS
struct  host_ent	*temp;
struct	in_addr		temp1;	
struct	sockaddr_in	remaddr;
int			rl = sizeof(remaddr);
#endif

int			old_server;

	user = Client->user;
	old_server = from_server;

	if (from_server == -1)
		from_server = get_window_server(0);

	myip.s_addr = server_list[from_server].local_addr.s_addr;


	if (myip.s_addr == htonl(0x00000000) || myip.s_addr == htonl(0x7f000001))
		myip.s_addr = MyHostAddr.s_addr;
		
	Type = dcc_types[Client->flags & DCC_TYPES];
	if (Client->flags & DCC_TDCC)
	{
		switch(Client->flags & DCC_TYPES)
		{
			case DCC_FILEOFFER:
				Type = "TSEND";
				break;
			case DCC_FILEREAD:
				Type = "TGET";
				break;
			case DCC_RESENDOFFER:
				Type = "TRESEND";
				break;
			case DCC_REGETFILE:
				Type = "TREGET";
				break;
		}
	}


	if (Client->flags & DCC_OFFER)
	{
		message_from(NULL, LOG_DCC);
#ifdef DCC_CNCT_PEND
		Client->flags |= DCC_CNCT_PEND;
#endif
	/*BLAH!*/
		Client->remport = ntohs(Client->remport);
#ifdef WINNT
		if ((Client->write = connect_by_number(inet_ntoa(Client->remote), &Client->remport, SERVICE_CLIENT, PROTOCOL_TCP, 0)) < 0)
#else
		if ((Client->write = connect_by_number(inet_ntoa(Client->remote), &Client->remport, SERVICE_CLIENT, PROTOCOL_TCP, 1)) < 0)
#endif
		{
			put_it("%s", convert_output_format("$G %RDCC%n Unable to create connection: $0-", "%s", errno ? strerror(errno) : "Unknown Host"));
			message_from(NULL, LOG_CRAP);
			dcc_erase(Client);
			from_server = old_server;
			return 0;
		}
	/* BLAH! */
		Client->remport = htons(Client->remport);
		Client->read = Client->write;
		FD_SET(Client->read, &readables);
		Client->flags |= DCC_ACTIVE;
#ifndef NON_BLOCKING_CONNECTS
		Client->flags &= ~DCC_OFFER;

		(void) getpeername(Client->read, (struct sockaddr *) &remaddr, &rl);

		if (get_to_from(Type) != -1)
			dcc_active_count++;
		if ((Client->flags & DCC_TYPES) != DCC_RAW)
		{
                        if (do_hook(DCC_CONNECT_LIST,"%s %s %s %d",
       	                        user, Type, inet_ntoa(remaddr.sin_addr),
               	                ntohs(remaddr.sin_port)))
				if (!dcc_quiet)
					put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
						"%s %s %s %s %s %d", update_clock(GET_TIME), Type, user, Client->userhost?Client->userhost:"u@h", 
						inet_ntoa(remaddr.sin_addr), (int)ntohs(remaddr.sin_port)));
		}
#endif	
		message_from(NULL, LOG_CRAP);
		get_time(&Client->starttime);
		Client->bytes_read = Client->bytes_sent = 0L;
		from_server = old_server;
		return 1;
	}
	else
	{
		unsigned short portnum = 0;

#ifdef DCC_CNCT_PEND
		Client->flags |= DCC_WAIT|DCC_CNCT_PEND;
#else
		Client->flags |= DCC_WAIT;
#endif
		message_from(NULL, LOG_DCC);
		if (Client->remport)
			portnum = Client->remport;
#ifdef WINNT
		if ((Client->read = connect_by_number(NULL, &portnum, SERVICE_SERVER, PROTOCOL_TCP, 0)) < 0)
#else
		if ((Client->read = connect_by_number(NULL, &portnum, SERVICE_SERVER, PROTOCOL_TCP, 1)) < 0)
#endif
		{
			put_it("%s", convert_output_format("$G %RDCC%n Unable to create connection: $0-", "%s", errno ? strerror(errno) : "Unknown Host"));
			message_from(NULL, LOG_CRAP);
			dcc_erase(Client);
			from_server = old_server;
			return 0;
		}
		if (get_to_from(Type) != -1)
			dcc_active_count++;
		if (Client->flags & DCC_TWOCLIENTS)
		{
			/* patch to NOT send pathname accross */
			char	*nopath;

#ifdef WINNT
			if ( ((Client->flags & DCC_FILEOFFER)== DCC_FILEOFFER) && ((nopath = strrchr(Client->description, '\\')) || (nopath = strrchr(Client->description, '/')) ))
#else
			if (((Client->flags & DCC_FILEOFFER)== DCC_FILEOFFER) && (nopath = strrchr(Client->description, '/')))
#endif
				nopath++;
			else
				nopath = Client->description;

			if (Client->filesize)
				send_ctcp(CTCP_PRIVMSG, user, CTCP_DCC,
					 "%s %s %lu %u %lu", Type, nopath,
					 (u_long) ntohl(myip.s_addr),
					 (u_short) portnum,
					 Client->filesize);
			else if ((Client->flags & DCC_TYPES) == DCC_BOTMODE)
				send_ctcp(CTCP_NOTICE, user, CTCP_BOTLINK,
					 "%s %s %lu %u %s", Type, nopath,
					 (u_long) ntohl(myip.s_addr),
					 (u_short) portnum, Client->encrypt?Client->encrypt:"");
			else
				send_ctcp(CTCP_PRIVMSG, user, CTCP_DCC,
					 "%s %s %lu %u", Type, nopath,
					 (u_long) ntohl(myip.s_addr),
					 (u_short) portnum);
			message_from(NULL, LOG_DCC);
			if (!doing_multi && !dcc_quiet)
				put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_DCC_CHAT_FSET), 
					"%s %s %s", update_clock(GET_TIME), Type, user));
			message_from(NULL, LOG_CRAP);
		}
#ifdef MIRC_BROKEN_DCC_RESUME
		malloc_sprintf(&Client->othername, "%d", (int)portnum);
#endif
		FD_SET(Client->read, &readables);
		Client->starttime.tv_sec = Client->starttime.tv_usec = 0;
		from_server = old_server;
		return 2;
	}
	message_from(NULL, LOG_CRAP);
}
void add_userhost_to_dcc(WhoisStuff *stuff, char *nick, char *args)
{
DCC_list *Client;
	Client = dcc_searchlist("chat", nick, DCC_CHAT, 0, NULL, NULL, -1);
	if (!stuff || !nick || !stuff->nick || !stuff->user || !stuff->host || !strcmp(stuff->user, "<UNKNOWN>"))
		return;
	if (Client)	
		Client->userhost = m_sprintf("%s@%s", stuff->user, stuff->host);
	return;
}

void	dcc_chat (char *command, char *args)
{
	char	*user;
	char	*passwd = NULL;
	char	*port 	= NULL;
	char	*equal_user = NULL;
	DCC_list	*Client;
	
	if ((user = next_arg(args, &args)) == NULL)
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must supply a nickname for DCC chat", NULL, NULL));
		return;
	}
	while (args && *args)
	{
		char *argument = next_arg(args, &args);
		if (argument && strlen(argument) >= 2)
		{
			if (argument[0] == '-' || argument[1] == 'e')
				passwd = next_arg(args, &args);
			else if (argument[0] == '-' || argument[1] == 'p')
				port = next_arg(args, &args);
		}
	}

	Client = dcc_searchlist("chat", user, DCC_CHAT, 1, NULL, NULL, -1);

	if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous DCC chat to $0 exists", "%s", user));
		return;
	}
	if (port)
		Client->remport = atol(port);
	Client->flags |= DCC_TWOCLIENTS;
	if (passwd)
		Client->encrypt = m_strdup(passwd);

	equal_user = m_sprintf("=%s", user);
	addtabkey(equal_user, "msg", 0);
	new_free(&equal_user);

	dcc_open(Client);
	add_to_userhost_queue(user, add_userhost_to_dcc, "%d %s", Client->read, user);
}


void	dcc_chatbot (char *command, char *args)
{
	char	*user;
	char	*passwd = NULL;
	DCC_list	*Client;
	char	*port 	= NULL;
	
	if ((user = next_arg(args, &args)) == NULL)
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must supply a nickname for DCC bot", NULL, NULL));
		return;
	}
	while (args && *args)
	{
		char *argument = next_arg(args, &args);
		if (argument && argument[0] == '-' && strlen(argument) >=2)
		{
			if ((*(argument+1)) == 'e')
				passwd = next_arg(args, &args);
			else if ((*(argument+1)) == 'p')
				port = next_arg(args, &args);
		}
	}
		
	Client = dcc_searchlist("chat", user, DCC_BOTMODE, 1, NULL, NULL, -1);

	if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous DCC Bot to $0 exists", "%s", user));
		return;
	}
	Client->flags |= DCC_TWOCLIENTS;
	if (passwd)
		Client->encrypt = m_strdup(passwd);
	if (port)
		Client->remport = atol(port);
	dcc_open(Client);
	add_to_userhost_queue(user, add_userhost_to_dcc, "%d %s", Client->read, user);
}

extern char	*dcc_raw_listen (int port)
{
	DCC_list	*Client;
	char	*PortName;
	struct	sockaddr_in locaddr;
	char	*RetName = NULL;
	int	size;
	int	lastlog_level;

	lastlog_level = set_lastlog_msg_level(LOG_DCC);
	if (port && port < 1025)
	{
		put_it("%s", convert_output_format("$G %RDCC%n Cannot bind to a privileged port", NULL, NULL));
		(void) set_lastlog_msg_level(lastlog_level);
		return NULL;
	}
	PortName = ltoa(port);
	Client = dcc_searchlist("raw_listen", PortName, DCC_RAW_LISTEN, 1, NULL, NULL, -1);
	if (Client->flags & DCC_ACTIVE)
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous Raw Listen on $0 exists", "%s", PortName));
		set_lastlog_msg_level(lastlog_level);
		return RetName;
	}
	memset((char *) &locaddr, 0, sizeof(locaddr));
	locaddr.sin_family = AF_INET;
	locaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	locaddr.sin_port = htons(port);
	if (0 > (Client->read = socket(AF_INET, SOCK_STREAM, 0)))
	{
		dcc_erase(Client);
		put_it("%s", convert_output_format("$G %RDCC%n socket() failed: $0-", "%s", strerror(errno)));
		(void) set_lastlog_msg_level(lastlog_level);
		return RetName;
	}
	FD_SET(Client->read, &readables);
	set_socket_options(Client->read);
	if (bind(Client->read, (struct sockaddr *) &locaddr, sizeof(locaddr)) == -1)
	{
		dcc_erase(Client);
		put_it("%s", convert_output_format("$G %RDCC%n Could not bind port: $0-", "%s", strerror(errno)));
		(void) set_lastlog_msg_level(lastlog_level);
		return RetName;
	}
	listen(Client->read, 4);
	size = sizeof(locaddr);
	get_time(&Client->starttime);
	getsockname(Client->read, (struct sockaddr *) &locaddr, &size);
	Client->write = ntohs(locaddr.sin_port); 
	Client->flags |= DCC_ACTIVE;
	malloc_strcpy(&Client->user, ltoa(Client->write));
	malloc_strcpy(&RetName, Client->user);
	(void) set_lastlog_msg_level(lastlog_level);
	return RetName;
}

extern char	*dcc_raw_connect(char *host, u_short port)
{
	DCC_list	*Client;
	char	*PortName;
	struct	in_addr	address;
	struct	hostent	*hp;
	int	lastlog_level;

	lastlog_level = set_lastlog_msg_level(LOG_DCC);
	if ((address.s_addr = inet_addr(host)) == -1)
	{
		hp = gethostbyname(host);
		if (!hp)
		{
			put_it("%s", convert_output_format("$G %RDCC%n Unknown host: $0-", "%s", host));
			set_lastlog_msg_level(lastlog_level);
			return m_strdup(empty_string);
		}
		memcpy(&address, hp->h_addr, sizeof(address));
	}
	Client = dcc_searchlist(host, ltoa(port), DCC_RAW, 1, NULL, NULL, -1);
	if (Client->flags & DCC_ACTIVE)
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous DCC raw to $0 on $1 exists", "%s %d", host, port));
		set_lastlog_msg_level(lastlog_level);
		return m_strdup(empty_string);
	}
	/* Sorry. The missing 'htons' call here broke $connect() */
	Client->remport = htons(port);
	memcpy((char *) &Client->remote, (char *) &address, sizeof(address));
	Client->flags = DCC_OFFER | DCC_RAW;
	if (!dcc_open(Client))
		return m_strdup(empty_string);
	PortName = ltoa(Client->read);
	Client->user = m_strdup(PortName);
	if (do_hook(DCC_RAW_LIST, "%s %s E %d", PortName, host, port))
        	if (do_hook(DCC_CONNECT_LIST,"%s RAW %s %d",PortName, host, port))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
				"%s %s %s %s %s %d", update_clock(GET_TIME), "RAW", host, 
				Client->userhost?Client->userhost:"u@h", PortName, port));
	(void) set_lastlog_msg_level(lastlog_level);
	return m_strdup(ltoa(Client->read));
}

void real_dcc_filesend(char *filename, char *real_file, char *user, char *passwd, int type, int portnum)
{
	DCC_list *Client;
	struct	stat	stat_buf;
	char *nick = NULL;
	
	stat_file(filename, &stat_buf);
#ifdef S_IFDIR
	if (stat_buf.st_mode & S_IFDIR)
	{
		put_it("%s", convert_output_format("$G %RDCC%n Cannot send a directory", NULL, NULL));
		return;
	}
#endif
#ifndef WINNT
	if (!strncmp(filename, "/etc/", 5))
	{
		put_it("%s", convert_output_format("$G %RDCC%n Send request for /etc rejected", NULL, NULL));
		return;
	}
#endif
	filesize = stat_buf.st_size;
	while ((nick = next_in_comma_list(user, &user)))
	{
		if (!nick || !*nick)
			break;
		Client = dcc_searchlist(filename, nick, type, 1, real_file, NULL, -1);
		filesize = 0;
		if (!(Client->flags & DCC_ACTIVE) && !(Client->flags & DCC_WAIT))
		{
			Client->flags |= DCC_TWOCLIENTS;
			Client->remport = portnum;
			dcc_open(Client);
		} else
			put_it("%s", convert_output_format("$G %RDCC%n A previous DCC send:$0 to $1 exists", "%s %s", filename, nick));
	}
}

void dcc_resend(char *command, char *args)
{
	char	*user = NULL;
	char	*filename = NULL,
		*fullname = NULL;
	char	*passwd = NULL;
	char	*FileBuf = NULL;
	int	portnum = 0;
	int	tdcc = 0;
	
	if (!(user = next_arg(args, &args)) || !(filename = next_arg(args, &args)))
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must supply a nickname and a filename for DCC resend", NULL, NULL));
		return;
	}
#ifdef WINNT
	if (*filename == '\\' || *filename == '/')
#else
	if (*filename == '/')
#endif
	{
		malloc_strcpy(&FileBuf, filename);
	}
	else if (*filename == '~')
	{
		if (!(fullname = expand_twiddle(filename)))
		{
			put_it("%s", convert_output_format("$G %RDCC%n Unable to expand: $0", "%s", filename));
			return;
		}
		malloc_strcpy(&FileBuf, fullname);
		new_free(&fullname);
	}
	else
	{
		char current_dir[BIG_BUFFER_SIZE+1];
		getcwd(current_dir, sizeof(current_dir) - strlen(filename) - 4);
		malloc_sprintf(&FileBuf, "%s/%s", current_dir, filename); 
	}

	if (access(FileBuf, R_OK))
	{
		put_it("%s", convert_output_format("$G %RDCC%n Cannot access: $0", "%s", FileBuf));
		new_free(&FileBuf);
		return;
	}
	if (command)
		tdcc = !my_stricmp(command, "TRESEND");	

	while (args && *args)
	{
		char *argument = next_arg(args, &args);
		if (argument && strlen(argument) >=2)
		{
			if (*argument == '-' || *(argument+1) == 'e')
				passwd = next_arg(args, &args);
			else if (*argument == '-' || *(argument+1) == 'p')
			{
				char *v = next_arg(args, &args);
				portnum = (v) ? my_atol(v) : 0;
			}
		}
	}

	real_dcc_filesend(FileBuf,filename, user, passwd, tdcc?DCC_TDCC|DCC_RESENDOFFER:DCC_RESENDOFFER, portnum);
	new_free(&FileBuf);
}

void		dcc_filesend (char *command, char *args)
{
	char	*user = NULL;
	char	*filename = NULL,
		*fullname = NULL;
	char	FileBuf[BIG_BUFFER_SIZE + 1];
	char	*passwd = NULL;
	int	portnum = 0;
	int	tdcc = 0;		

	if (!(user = next_arg(args, &args)) || !(filename = next_arg(args, &args)))
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must supply a nickname and a filename for DCC send", NULL, NULL));
		return;
	}
	if (command)
		tdcc = !my_stricmp(command, "TSEND");	

#ifdef WINNT
	if (*filename == '\\' || *filename == '/')
#else
	if (*filename == '/')
#endif
		strcpy(FileBuf, filename);

	else if (*filename == '~')
	{
		if (0 == (fullname = expand_twiddle(filename)))
		{
			put_it("%s", convert_output_format("$G %RDCC%n Unable to expand: $0", "%s", filename));
			return;
		}
		strcpy(FileBuf, fullname);
		new_free(&fullname);
	}
	else
	{
		getcwd(FileBuf, sizeof(FileBuf));
		strcat(FileBuf, "/");
		strcat(FileBuf, filename);
	}

	while (args && *args)
	{
		char *argument = next_arg(args, &args);
		if (argument && strlen(argument) >= 2)
		{
			if (*argument == '-' && *(argument+1) == 'e')
				passwd = next_arg(args, &args);
			else if (*argument == '-' && *(argument+1) == 'p')
			{
				char *v = next_arg(args, &args);
				portnum = (v) ? my_atol(v) : 0;
			}
		}
	}

#ifdef WINNT
	convert_unix(FileBuf);
#endif
	if (!strchr(FileBuf, '*') && access(FileBuf, R_OK))
	{
		put_it("%s", convert_output_format("$G %RDCC%n Cannot access: $0", "%s", FileBuf));
		return;
	} 
	else 
	if (strchr(FileBuf, '*'))
	{
		char *path = NULL;
		char *thefile;
		DIR *dp;
		struct dirent *dir;
		struct stat stbuf;
		char *filebuf = NULL;
		char *expand = NULL;
		int count = 0;
		malloc_strcpy(&path, FileBuf);

		if ((thefile = strrchr(path, '/')) != NULL)
			*thefile++ = '\0';
		else {
			malloc_strcpy(&path, "~");
			thefile = FileBuf;
		}
		expand = expand_twiddle(path);
		
		if ((dp = opendir(expand)) == NULL)
		{
			put_it("%s", convert_output_format("$G %RDCC%n Cannot access directory: $0", "%s", expand));
			new_free(&expand);
			new_free(&path);
			return;
		}
		
		while(1)
		{
			dir = readdir(dp);
			if (!dir) 
				break;
			if (dir->d_ino == 0)
				continue;
			if (!match(thefile, dir->d_name))
				continue;
			malloc_sprintf(&filebuf, "%s/%s", expand, dir->d_name);
			stat(filebuf, &stbuf);
			if (S_ISDIR(stbuf.st_mode))
				continue;
			count++;
			real_dcc_filesend(filebuf, dir->d_name, user, passwd, tdcc?DCC_TDCC|DCC_FILEOFFER:DCC_FILEOFFER, 0);
		}
		new_free(&filebuf);
		if (!dcc_quiet)
		{
			if (count)
				put_it("%s", convert_output_format("$G %RDCC%n Sent DCC SEND request to $0 for files $1", "%s %s", user, FileBuf));
			else
				put_it("%s", convert_output_format("$G %RDCC%n No Files found matching $0", "%s", FileBuf));
		}
		new_free(&path);
		new_free(&expand);
		return;
	}
	real_dcc_filesend(FileBuf, filename, user, passwd, tdcc?DCC_TDCC|DCC_FILEOFFER:DCC_FILEOFFER, portnum);
}

void multiget(char *usern, char *filen)
{
	DCC_list	*dccList;
	char		*newbuf = NULL;
	char 		*expand = NULL;	
	char		*q = NULL;
	doing_multi = 1;
	if ((q = strchr(filen, ' ')))
		*q = 0;
	for (dccList = ClientList; dccList ; dccList = dccList->next)
        {
        	if((match(dccList->description, filen) || match(filen, dccList->description))&& 
			!my_stricmp(usern, dccList->user) && 
			(dccList->flags & DCC_TYPES) == DCC_FILEREAD)
		{
			if (get_string_var(DCC_DLDIR_VAR)) 
			{
				expand = expand_twiddle(get_string_var(DCC_DLDIR_VAR));
				malloc_sprintf(&newbuf, "%s %s/%s", usern, expand, dccList->description);
			} else
				malloc_sprintf(&newbuf, "%s %s", usern, dccList->description);
			if (!dcc_quiet)
				put_it("%s", convert_output_format("$G %RDCC%n Attempting DCC get: $0", "%s", dccList->description));
			dcc_getfile(NULL, newbuf);
			new_free(&expand);
			new_free(&newbuf);
		}
	}
	doing_multi = 0;
}
 

static void dcc_getfile (char *command, char *args)
{
	char	*user;
	char	*filename = NULL;
	char	*tmp = NULL;
	DCC_list	*Client;
	char	*fullname = NULL;
	char	*passwd = NULL;
	char	*argument = NULL;
	int	tdcc = 0;
			
	if (0 == (user = next_arg(args, &args)))
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must supply a nickname for DCC get", NULL, NULL));
		return;
	}
	if (command)
		tdcc = !my_stricmp(command, "TGET");	
	if (args && *args)
	{
		if (args && strlen(args) >= 2)
		{
			if (*args != '-' && *(args+1) != 'e')
				filename = next_arg(args, &args);
			else if (args && *args == '-' && *(args+1) == 'e')
			{
				argument = next_arg(args, &args);
				passwd = next_arg(args, &args);
			}
		}
	}

#ifdef WINNT
	if (filename)
		convert_unix(filename);
#endif
	if (0 == (Client = dcc_searchlist(filename, user, DCC_FILEREAD, 0, NULL, NULL, 0)))
	{
		if (filename)
			put_it("%s", convert_output_format("$G %RDCC%n No file ($0) offered in SEND mode by $1", "%s %s", filename, user));
		else
			put_it("%s", convert_output_format("$G %RDCC%n No file offered in SEND mode by $0", "%s", user));
		return;
	}
	if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous DCC get:$0 to $0 exists", "%s %s", filename, user));
		return;
	}
	Client->flags |= DCC_TWOCLIENTS;
	Client->bytes_sent = Client->bytes_read = 0L;

	if (passwd)
		Client->encrypt = m_strdup(passwd);

	if (!dcc_open(Client))
		return;
	if (get_string_var(DCC_DLDIR_VAR))
		malloc_sprintf(&tmp, "%s/%s", get_string_var(DCC_DLDIR_VAR), Client->description);
	else
		malloc_sprintf(&tmp, "%s", Client->description);
	if (!(fullname = expand_twiddle(tmp)))
		malloc_strcpy(&fullname, tmp);

#ifdef WINNT
	if (-1 == (Client->file = open(fullname, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY/*, 0644*/)))
#else
	if (-1 == (Client->file = open(fullname, O_WRONLY | O_TRUNC | O_CREAT, 0644)))
#endif
	{
		put_it("%s", convert_output_format("$G %RDCC%n Unable to open $0: $1-", "%s %s", Client->description, errno?strerror(errno):"Unknown"));
		if (dcc_active_count) dcc_active_count--;
		Client->flags |= DCC_DELETE;
	}
	new_free(&fullname);
	new_free(&tmp);
}


void dcc_regetfile(char *command, char *args)
{
	char	*user;
	char	*tmp = NULL;
	char	*filename;
	DCC_list	*Client;
	char	*fullname = NULL;
	struct stat buf;
	int tdcc = 0;
	
	if (0 == (user = next_arg(args, &args)))
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must supply a nickname for DCC get", NULL, NULL));
		return;
	}
	if (command)
		tdcc = !my_stricmp(command, "TREGET");	
	filename = next_arg(args, &args);

#ifdef WINNT
	if (filename)
		convert_unix(filename);
#endif
	if (!(Client = dcc_searchlist(filename, user, DCC_REGETFILE, 0, NULL, NULL, 0)))
	{
		if (filename)
			put_it("%s", convert_output_format("$G %RDCC%n No file ($0) offered in RESEND mode by $1", "%s %s", filename, user));
		else
			put_it("%s", convert_output_format("$G %RDCC%n No file offered in RESEND mode by $0", "%s", user));
		return;
	}
	if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous DCC reget:$0 to $0 exists", "%s %s", filename, user));
		return;
	}
	Client->flags |= DCC_TWOCLIENTS;
	Client->bytes_sent = Client->bytes_read = 0L;

	if (get_string_var(DCC_DLDIR_VAR))
		malloc_sprintf(&tmp, "%s/%s", get_string_var(DCC_DLDIR_VAR), Client->description);
	else
		malloc_sprintf(&tmp, "%s", Client->description);
	if (!(fullname = expand_twiddle(tmp)))
		malloc_strcpy(&fullname, tmp);

	if (!dcc_open(Client))
		return;
#ifdef WINNT
	if (-1 == (Client->file = open(fullname, O_WRONLY | O_CREAT | O_BINARY/*, 0644*/)))
#else
	if (-1 == (Client->file = open(fullname, O_WRONLY | O_CREAT, 0644)))
#endif
	{
		put_it("%s", convert_output_format("$G %RDCC%n Unable to open $0: $1-", "%s %s", Client->description, errno?strerror(errno):"Unknown"));
		Client->flags |= DCC_DELETE;
		if (dcc_active_count) dcc_active_count--;
		return;
	}

	/* seek to the end of the file about to be resumed */
	lseek(Client->file, 0, SEEK_END);

	/* get the size of our file to be resumed */
	fstat(Client->file, &buf);

	Client->transfer_orders.packet_id = DCC_PACKETID;
	Client->transfer_orders.byteoffset = buf.st_size;
	Client->transfer_orders.byteorder = byteordertest();
#ifndef NON_BLOCKING_CONNECTS
	/* send a packet to the sender with transfer resume instructions */
	send(Client->read, (const char *)&(Client->transfer_orders), sizeof(struct transfer_struct), 0);
#endif
	new_free(&fullname);
	new_free(&tmp);
}

extern void	register_dcc_offer (char *user, char *type, char *description, char *address, char *port, char *size, char *extra, char *userhost)
{
	DCC_list *	Client;
	int		CType;
	char *		c = NULL;
	u_long		TempLong;
	unsigned	TempInt;
	int		do_auto = 0;	/* used in dcc chat collisions */
	int		do_autog = 1;
	long		packets = 0;
		
#ifdef WINNT
	if ( (c = strrchr(description, '/')) || (c = strrchr(description, '\\')))
#else
	if ((c = strrchr(description, '/')))
#endif
		description = c + 1;
	if ('.' == *description)
		*description = '_';

	message_from(NULL, LOG_DCC);
	if (size && *size)
	{
		filesize = my_atol(size);
		packets = filesize / dccBlockSize() + 1;
	}
	else
		packets = filesize = 0;

	if (!my_stricmp(type, "CHAT"))
		CType = DCC_CHAT;
	else if (!my_stricmp(type, "BOT"))
		CType = DCC_BOTMODE;
	else if (!my_stricmp(type, "SEND"))
		CType = DCC_FILEREAD;
	else if (!my_stricmp(type, "RESEND"))
		CType = DCC_REGETFILE;
	else if (!my_stricmp(type, "TRESEND"))
		CType = DCC_TDCC|DCC_REGETFILE;
	else if (!my_stricmp(type, "TSEND"))
		CType = DCC_TDCC|DCC_FILEREAD;
#ifdef FTP_XMIT
	else if (!my_stricmp(type, "XMIT"))
	{
		dcc_xmitget(user, description, address, port);
		return;
	}
#endif
#ifdef MIRC_BROKEN_DCC_RESUME
	else if (!my_stricmp(type, "RESUME"))
	{
		/* 
		 * Dont be deceieved by the arguments we're passing it.
		 * The arguments are "out of order" because MIRC doesnt
		 * send them in the traditional order.  Ugh.
		 */
		dcc_getfile_resume_demanded(user, description, address, port);
		return;
	}
	else if (!my_stricmp(type, "ACCEPT"))
	{
		/*
		 * See the comment above.
		 */
		dcc_getfile_resume_start (user, description, address, port);
		return;
	}
#endif
        else
        {
		put_it("%s", convert_output_format("$G %RDCC%n Unknown DCC $0 ($1) recieved from $2", "%s %s %s", type, description, user));
		message_from(NULL, LOG_CRAP);
                return;
        }

	Client = dcc_searchlist(description, user, CType, 1, NULL, NULL, -1);
	filesize = 0;

	if (extra && *extra)
		Client->cksum = m_strdup(extra);

	if (Client->flags & DCC_WAIT)
	{
		Client->flags |= DCC_DELETE;
		if (DCC_CHAT == CType)
		{
			Client = dcc_searchlist(description, user, CType, 1, NULL, NULL, -1);
			do_auto = 1;
		}
		else
		{
			
			put_it("%s", convert_output_format("$G %RDCC%n $0 collision for $1:$2", "%s %s %s", type, user, description));
			send_ctcp(CTCP_NOTICE, user, CTCP_DCC, "DCC %s collision occured while connecting to %s (%s)", type, nickname, description);
			message_from(NULL, LOG_CRAP);
			return;
		}
	}
	if (Client->flags & DCC_ACTIVE)
	{
		put_it("%s", convert_output_format("$G %RDCC%n Recieved DCC $0 request from $1 while previous session active", "%s %s", type, user));
		message_from(NULL, LOG_CRAP);
		return;
	}
	Client->flags |= DCC_OFFER;

	TempLong = strtoul(address, NULL, 10);
	Client->remote.s_addr = htonl(TempLong);
	TempInt = (unsigned) strtoul(port, NULL, 10);
	Client->remport = htons(TempInt);

	if (TempInt < 1024)
	{
		put_it("%s", convert_output_format("$G %RDCC%n $0 ($1) request from $2 rejected", "%s %s %s", type, description, user));
		Client->flags |= DCC_DELETE;
		message_from(NULL, LOG_CRAP);
		return;
	}
	if (userhost)
		Client->userhost = m_strdup(userhost);
#ifdef HACKED_DCC_WARNING
	/* This right here compares the hostname from the userhost stamped
	 * on the incoming privmsg to the address stamped in the handshake.
	 * We do not automatically reject any discrepencies, but warn the
	 * user instead to be cautious.
	 */
	{
		char tmpbuf[128];
		char *fromhost;
		u_32int_t compare, compare2;
		u_32int_t compare3, compare4;
		struct hostent *hostent_fromhost;

		strncpy(tmpbuf, FromUserHost, 127);
		fromhost = strchr(tmpbuf, '@');
		fromhost++;
		alarm(1);	/* dont block too long... */
		hostent_fromhost = gethostbyname(fromhost);
		alarm(0);
		if (!hostent_fromhost)
		{
			yell("Incoming handshake has an address [%s] that could not be figured out!", fromhost);
			yell("Please use caution in deciding whether to accept it or not");
		}
		else
		{
			compare = *((u_32int_t *)hostent_fromhost->h_addr_list[0]);
			compare2 = inet_addr(fromhost);
			compare3 = htonl(0x00000000);
			compare4 = htonl(0xffffffff);
			if (((compare != Client->remote.s_addr) &&
				(compare2 != Client->remote.s_addr)))
			{
				say("WARNING: Fake dcc handshake detected! [%x]",Client->remote.s_addr);
				say("Unless you know where this dcc request is coming from");
				say("It is recommended you ignore it!");
				do_auto = do_autog = 0;
			}
			if (compare3 == Client->remote.s_addr || compare4 == Client->remote.s_addr)
			{
				yell("WARNING: Fake dcc handshake detected! [%x]",Client->remote.s_addr);
				Client->flags = DCC_DELETE;
				message_from(NULL, LOG_CRAP);
				return;
			}
		}
	}
#endif
	if ((u_long) 0 == TempLong || 0 == Client->remport)
	{
		yell("DCC handshake from %s ignored becuase it had an null port or address", user);
		Client->flags = DCC_DELETE;
		message_from(NULL, LOG_CRAP);
		return;
	}
	if (((Client->flags & DCC_TYPES) == DCC_FILEREAD) && ((Client->flags & DCC_TYPES) != DCC_REGETFILE))
	{
		struct stat statit;
		char *tmp = NULL, *p;
		malloc_sprintf(&tmp, "%s/%s", get_string_var(DCC_DLDIR_VAR), Client->description);
		p = expand_twiddle(tmp);
		if ( !dcc_overwrite_var && get_int_var(DCC_AUTOGET_VAR) && ((do_autog = stat(p, &statit)) == 0) )
		{
			if (!get_int_var(DCC_AUTORENAME_VAR))
			/* the file exists. warning is generated */
				put_it("%s", convert_output_format("$G %RDCC%n Warning: File $0 exists: use /DCC rename if you dont want to overwrite", "%s", p));
			else
			{
				rename_file(p, &Client->description);
				put_it("%s", convert_output_format("$G %RDCC%n Warning: File $0 renamed: $1", "%s %s", p, Client->description));
				description = Client->description;
				do_autog = 1;
			}
		}
		new_free(&tmp); new_free(&p);
	}
  	if (do_auto)
  	{
                if (do_hook(DCC_CONNECT_LIST,"%s CHAT %s",user, "already requested connecting"))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
				"%s %s %s %s %s %d", update_clock(GET_TIME), "CHAT", user, 
				" ", "already requested connecting...", 0));
  		dcc_chat(NULL, user);
  	}
        else if (do_hook(DCC_REQUEST_LIST,"%s %s %s %d",user, type, description, Client->filesize))
	{
		if (!dcc_quiet)
		{
	
			char buf[40];
			sprintf(buf, "%2.4g",_GMKv(Client->filesize));
			if (Client->filesize)
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_REQUEST_FSET), 
					"%s %s %s %s %s %s %d %s %s", 
					update_clock(GET_TIME),type,description,user, FromUserHost,
					inet_ntoa(Client->remote),ntohs(Client->remport), 
					_GMKs(Client->filesize), buf));
			else
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_REQUEST_FSET), 
					"%s %s %s %s %s %s %d", update_clock(GET_TIME),type,description,
					user, FromUserHost,inet_ntoa(Client->remote),ntohs(Client->remport)));
			
		}
		if (CType == DCC_CHAT)
		{
			if (get_int_var(BOT_MODE_VAR) && find_user_level(Client->user, FromUserHost, "*"))
			{
				dcc_open(Client);
				message_from(NULL, LOG_CRAP);
				return;
			}
			else
			{
				bitchsay("Type /chat to answer or /nochat to close");
				malloc_sprintf(&last_chat_req, "%s", user);
			}
		}
	}

	if (CType == DCC_BOTMODE && get_string_var(BOT_PASSWD_VAR))
	{
		if (((Client->encrypt && !strcmp(get_string_var(BOT_PASSWD_VAR), Client->encrypt))) || !Client->encrypt)
			dcc_open(Client);
		message_from(NULL, LOG_CRAP);
		return;
	}
	if ((get_int_var(DCC_AUTOGET_VAR) || (!get_int_var(DCC_AUTOGET_VAR) && find_name_in_genericlist(user, dcc_no_flood, DCC_HASHSIZE, 0))) && 
	    (Client->filesize/1024 < get_int_var(DCC_MAX_AUTOGET_SIZE_VAR))
	    && ((CType & DCC_TYPES) == DCC_FILEREAD || (CType & DCC_TYPES) == DCC_REGETFILE))
	{
		char *thebuf = NULL;
		if (!Client->filesize)
		{
			put_it("%s", convert_output_format("$G %RDCC Caution Filesize is 0!! No Autoget", NULL, NULL));
			message_from(NULL, LOG_CRAP);
			return;
		}
		malloc_sprintf(&thebuf, "%s %s", user, description);
		if ((((CType & DCC_TYPES) == DCC_REGETFILE) || do_autog) && !dcc_quiet)
		{
			char *prompt;
			prompt = m_strdup(convert_output_format(get_string_var(CDCC_PROMPT_VAR), NULL, NULL));
			put_it("%s", convert_output_format("$3- Auto-$0ting file %C$1%n from %K[%C$2%K]", "%s %s %s %s", ((CType & DCC_TYPES)== DCC_REGETFILE) ? (CType & DCC_TDCC?"treget":"reget"):(CType & DCC_TDCC?"tget":"get"), description, user, prompt));
			new_free(&prompt);
		}
		if (do_autog)
		{
			if ((CType & DCC_TYPES) == DCC_REGETFILE)
				dcc_regetfile(type, thebuf);
			else 
				dcc_getfile(type, thebuf);
		}
		new_free(&thebuf);
	}
	message_from(NULL, LOG_CRAP);
	return;
}

static void process_incoming_chat (DCC_list *Client)
{
	struct	sockaddr_in	remaddr;
	int	sra;
	char	tmp[MAX_DCC_BLOCK_SIZE + 1];
	char	*s = NULL, *bufptr = NULL;
	char	*buf = NULL;
	u_32int_t	bytesread;
	int	old_timeout;
	int	len = 0;
	int	type = Client->flags & DCC_TYPES;
	
	if (Client->flags & DCC_WAIT)
	{
		sra = sizeof(struct sockaddr_in);
		Client->write = accept(Client->read, (struct sockaddr *) &remaddr, &sra);
		FD_CLR(Client->read, &readables);
		close(Client->read);
		Client->read = -1;
		if ((Client->read = Client->write) > 0)
			FD_SET(Client->read, &readables);
		else
		{
			put_it("%s", convert_output_format("$G %RDCC error: accept() failed. punt!!", NULL, NULL));
			Client->flags |= DCC_DELETE;
			return;
		}
		Client->flags &= ~DCC_WAIT;
		Client->flags |= DCC_ACTIVE;
                if (do_hook(DCC_CONNECT_LIST, "%s CHAT %s %d", Client->user,
                         inet_ntoa(remaddr.sin_addr)), ntohs(remaddr.sin_port))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
				"%s %s %s %s %s %d", update_clock(GET_TIME),
				type == DCC_BOTMODE ? "BOT":"CHAT", 
				Client->user, Client->userhost?Client->userhost:"u@h",
				inet_ntoa(remaddr.sin_addr),ntohs(remaddr.sin_port)));
		if ((type == DCC_BOTMODE) && Client->encrypt)
			new_free(&Client->encrypt);
		get_time(&Client->starttime);
		return;
	}
	old_timeout = dgets_timeout(1);
        s = Client->buffer;
        bufptr = tmp;
        if (s && *s)
        {
                len = strlen(s);
		if (len > (MAX_DCC_BLOCK_SIZE / 2) - 1)
		{
			put_it("%s", convert_output_format("$G %RDCC buffer overrun. Data lost", NULL, NULL));
			new_free(&(Client->buffer));
		} 
		else
		{ 
	                strncpy(tmp, s, len);
			bufptr += len;
		}
        }
	bytesread = dgets(bufptr, dccBlockSize() - len, Client->read, NULL);
	(void) dgets_timeout(old_timeout);
	switch (bytesread)
	{
		case -1:
        	        add_to_dcc_buffer(Client, tmp);
                	return;
		case 0:
		{
			char *real_tmp = ((dgets_errno == -1) ? "Remote End Closed Connection" : strerror(dgets_errno));
	                if (do_hook(DCC_LOST_LIST, "%s CHAT %s", Client->user, real_tmp))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), 
					"%s %s %s %s", update_clock(GET_TIME), 
					type == DCC_BOTMODE? "BOT":"CHAT", 
					Client->user, real_tmp));
			Client->flags |= DCC_DELETE;
			return;
		}
		default:
		{
			char userhost[BIG_BUFFER_SIZE+1];
			char equal_nickname[NICKNAME_LEN+4];
		
			new_free(&Client->buffer);
			tmp[strlen(tmp) - 1]='\0';
			my_decrypt(tmp, strlen(tmp), Client->encrypt);
			Client->bytes_read += bytesread;
			message_from(Client->user, LOG_DCC);
			malloc_strcpy(&buf, tmp);

			if ((Client->flags & DCC_TYPES) == DCC_BOTMODE)
			{
				if (Client->read > -1)
					handle_dcc_bot(Client->read, buf);
			}
			else 
			{
				if (*buf == '.')
				{
					if (!check_tcl_dcc(buf+1, Client->user, Client->userhost?Client->userhost:"", Client->read, Client))
						dcc_printf(Client->write, "Unknown command\n");
				}
				else 
				{
					if (!Client->userhost)
					{
						strcpy(userhost, "Unknown@");
						strcat(userhost, inet_ntoa(remaddr.sin_addr));
						FromUserHost = userhost;
					}
					else
						FromUserHost = Client->userhost;

					strncpy(equal_nickname, "=", NICKNAME_LEN);
					strncat(equal_nickname, Client->user, NICKNAME_LEN);
					if (!strncmp(tmp, "CTCP_MESSAGE ", strlen("CTCP_MESSAGE ")))
						do_ctcp(equal_nickname, get_server_nickname(from_server), stripansicodes(tmp + strlen("CTCP_MESSAGE ")));
					else if (!strncmp(tmp, "CTCP_REPLY ", strlen("CTCP_REPLY ")))
						do_notice_ctcp(equal_nickname, get_server_nickname(from_server), stripansicodes(tmp + strlen("CTCP_REPLY ")));
					else if (Client->in_dcc_chat)
						handle_tcl_chan(Client->read, Client->user, Client->userhost, tmp);
					else if (do_hook(DCC_CHAT_LIST, "%s %s", Client->user, tmp))
					{
						addtabkey(equal_nickname, "msg", 0);
						put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CHAT_FSET), 
							"%s %s %s %s", update_clock(GET_TIME), Client->user, Client->userhost?Client->userhost:"u@h", tmp));
						add_last_type(&last_dcc[0], MAX_LAST_MSG, Client->user, NULL, FromUserHost, tmp);
					}
					FromUserHost = empty_string;
				}
			}
		}
	}
	message_from(NULL, LOG_CRAP);
	new_free(&buf);
}

static	void		process_incoming_listen (DCC_list *Client)
{
	struct	sockaddr_in	remaddr;
	int	sra;
	char	FdName[10];
	DCC_list	*NewClient;
	int	new_socket;
	struct	hostent	*hp;
#if defined(__linux__) || defined(__sgi)
	const char	*Name;
#else
	char	*Name;
#endif

	sra = sizeof(struct sockaddr_in);
	new_socket = accept(Client->read, (struct sockaddr *) &remaddr, &sra);
	if (new_socket < 0)
	{
		put_it("%s", convert_output_format("$G %RDCC error: accept() failed. punt!!", NULL, NULL));
		return;
	}
	if (0 != (hp = gethostbyaddr((char *)&remaddr.sin_addr,
	    sizeof(remaddr.sin_addr), remaddr.sin_family)))
		Name = hp->h_name;
	else
		Name = inet_ntoa(remaddr.sin_addr);
	sprintf(FdName, "%d", new_socket);
	NewClient = dcc_searchlist((char *)Name, FdName, DCC_RAW, 1, NULL, NULL, 0);
	get_time(&NewClient->starttime);
	NewClient->read = NewClient->write = new_socket;
	FD_SET(NewClient->read, &readables);
	NewClient->remote = remaddr.sin_addr;
	NewClient->remport = remaddr.sin_port;
	NewClient->flags |= DCC_ACTIVE;
	NewClient->bytes_read = NewClient->bytes_sent = 0L;
	if (do_hook(DCC_RAW_LIST, "%s %s N %d", NewClient->user,
						NewClient->description,
						Client->write))
	        if (do_hook(DCC_CONNECT_LIST,"%s RAW %s %d", NewClient->user,
        	                                             NewClient->description,
                	                                     Client->write))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
				"%s %s %s %s %s %d", update_clock(GET_TIME), 
				"RAW", NewClient->user, 
				Client->userhost? Client->userhost:"u@h", 
				NewClient->description, Client->write));
}

static	void		process_incoming_raw (DCC_list *Client)
{
	char	tmp[MAX_DCC_BLOCK_SIZE + 1];
	char 	*s, *bufptr;
	u_32int_t bytesread;
	int     old_timeout;
	int	len =  0;
	
        s = Client->buffer;
        bufptr = tmp;
        if (s && *s)
        {
		len = strlen(s);
		if (len > MAX_DCC_BLOCK_SIZE - 1)
		{
			put_it("%s", convert_output_format("$G %RDCC raw buffer overrun. Data lost", NULL, NULL));
			new_free(&Client->buffer);
		}
		else
		{ 
	                strncpy(tmp, s, len);
        	        bufptr += len;
		}
        }
        old_timeout = dgets_timeout(1);
	switch(bytesread = dgets(bufptr, dccBlockSize() - len, Client->read, NULL))
	{
		case -1:
		{
                	add_to_dcc_buffer(Client, tmp);
	                return;
		}
		case 0:
		{
			if (do_hook(DCC_RAW_LIST, "%s %s C", Client->user, Client->description))
	       			if (do_hook(DCC_LOST_LIST,"%s RAW %s", Client->user, Client->description))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), "RAW", Client->user, Client->description));
			Client->flags |= DCC_DELETE;
			(void) dgets_timeout(old_timeout);
			return;
		}
		default:
		{
			new_free(&Client->buffer);
			tmp[strlen(tmp) - 1] = '\0';
			Client->bytes_read += bytesread;
			if (Client->dcc_handler)
				(Client->dcc_handler)(Client, tmp);
			else
			{
				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);
			}
			dgets_timeout(old_timeout);
			return;
		}
	}
}


/* Now that ive mucked this function up, i should go back and fix it. */
static void	process_outgoing_file (DCC_list *Client, int readwaiting)
{
	struct	sockaddr_in	remaddr;
	int			sra;
	char			tmp[MAX_DCC_BLOCK_SIZE+1];
	u_32int_t		bytesrecvd = 0;
	int			bytesread = 0;
	char			*Type;
	unsigned char 		packet[BIG_BUFFER_SIZE/8];
	struct  transfer_struct *received;
	int			tdcc = 0;
		
	Type = dcc_types[Client->flags & DCC_TYPES];
	tdcc = Client->flags & DCC_TDCC;
	if (Client->flags & DCC_WAIT)
	{
		sra = sizeof(struct sockaddr_in);
		Client->write = accept(Client->read, (struct sockaddr *) &remaddr, &sra);
		if ((Client->flags & DCC_RESENDOFFER) == DCC_RESENDOFFER)
		{
			recv(Client->write, packet, sizeof(struct transfer_struct), 0);
			received = (struct transfer_struct *)packet;
			if (byteordertest() != received->byteorder)
			{
				/* the packet sender orders bytes differently than us,
				 * reverse what they sent to get the right value 
				 */
				Client->transfer_orders.packet_id = 
					((received->packet_id & 0x00ff) << 8) | 
					((received->packet_id & 0xff00) >> 8);

				 Client->transfer_orders.byteoffset = 
					((received->byteoffset & 0xff000000) >> 24) |
					((received->byteoffset & 0x00ff0000) >> 8) |
					((received->byteoffset & 0x0000ff00) << 8) |
					((received->byteoffset & 0x000000ff) << 24);
			}
			else
				memcpy(&Client->transfer_orders,packet,sizeof(struct transfer_struct));

			if (Client->transfer_orders.packet_id != DCC_PACKETID)
				put_it("%s", convert_output_format("$G %RDCC%n reget packet is invalid!!", NULL, NULL));
			else
				put_it("%s", convert_output_format("$G %RDCC%n reget starting at $0", "%d", Client->transfer_orders.byteoffset));
		}

		FD_CLR(Client->read, &readables);
		close(Client->read);

		if ((Client->read = Client->write) >= 0)
		{
			FD_SET(Client->read, &readables);
			FD_SET(Client->write, &writables);
		}
		else
		{
			put_it("%s", convert_output_format("$G %RDCC error: accept() failed. punt!!", NULL, NULL));

			Client->flags |= DCC_DELETE;
			if (get_to_from(Type) != -1 && dcc_active_count)
				dcc_active_count--;
			return;
		}
#if defined(NON_BLOCKING_CONNECTS)
		if (get_int_var(DCC_FAST_VAR) || (Client->flags & DCC_TDCC))
			set_non_blocking(Client->write);
#endif
		Client->flags &= ~DCC_WAIT;
		Client->flags |= DCC_ACTIVE;
		Client->eof = 0;
		get_time(&Client->starttime);
#ifdef WINNT
		if ((Client->file = open(Client->description, O_RDONLY | O_BINARY)) == -1)
#else
		if ((Client->file = open(Client->description, O_RDONLY)) == -1)
#endif
		{
			put_it("%s", convert_output_format("$G %RDCC%n Unable to open $0: $1-", "%s %s", Client->description, errno ? strerror(errno) : "Unknown Host"));
			FD_CLR(Client->read, &readables);
			FD_CLR(Client->write, &writables);
			if (get_to_from(Type) != -1 && dcc_active_count)
				dcc_active_count--;
			close(Client->read);
			Client->read = Client->write = (-1);
			Client->flags |= DCC_DELETE;
			return;
		}
		if ((Client->flags & DCC_RESENDOFFER) == DCC_RESENDOFFER)		
		{
			lseek(Client->file, Client->transfer_orders.byteoffset, SEEK_SET);
			errno = 0;
			if (do_hook(DCC_CONNECT_LIST,"%s %s %s %d",Client->user, tdcc?"TRESEND":"SEND",
				inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port)))
				if (!dcc_quiet)
					put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
						"%s %s %s %s %s %d %d", update_clock(GET_TIME), tdcc?"TRESEND":"RESEND", 
						Client->user, Client->userhost?Client->userhost:"u@h", inet_ntoa(remaddr.sin_addr),
						ntohs(remaddr.sin_port), Client->transfer_orders.byteoffset));
		}
		else
		{
			if (do_hook(DCC_CONNECT_LIST,"%s %s %s %d %s %d",Client->user, tdcc?"TSEND":"SEND", 
				inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port), Client->description, Client->filesize))
				if (!dcc_quiet)
					put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_CONNECT_FSET), 
						"%s %s %s %s %s %d %d", update_clock(GET_TIME), tdcc?"TSEND":"SEND", 
						Client->user, Client->userhost?Client->userhost:"u@h", inet_ntoa(remaddr.sin_addr),
						ntohs(remaddr.sin_port), Client->transfer_orders.byteoffset));
			if (Client->transfer_orders.byteoffset)
				lseek(Client->file, Client->transfer_orders.byteoffset, SEEK_SET);
		}
	}
	else if (readwaiting)
	{ 
		if (!(Client->flags & DCC_TDCC))
		{
			if (recv(Client->read, (char *)&bytesrecvd, sizeof(u_32int_t), 0) < sizeof(u_32int_t))
			{
                	        if (do_hook(DCC_LOST_LIST,"%s SEND %s CONNECTION LOST",
                        	        Client->user, Client->description))
					put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), "SEND", Client->user, Client->description));
				if (get_to_from(Type) != -1 && dcc_active_count)
					dcc_active_count--;
				Client->flags |= DCC_DELETE;
				dcc_update_stats(Client);
				return;
			}
			else if (ntohl(bytesrecvd) != Client->bytes_sent)
				return;
		}
		else 
			return;
/* CDE do we just return here? */
	}
	if ((bytesread = read(Client->file, tmp, dccBlockSize())) > 0)
	{
		my_encrypt(tmp, bytesread, Client->encrypt);
		send(Client->write, tmp, bytesread, 0);
		Client->packets_transfer++;
		Client->bytes_sent += bytesread;
		update_transfer_buffer("");
		if (!(Client->packets_transfer % 20))
			update_all_status(curr_scr_win, NULL, 0);
	} 
	else if (!readwaiting)
	{
		if (!(Client->flags & DCC_TDCC))
			Client->eof = 1;
		else 
/* fuck this shit. when done sending data. close the socket. If Virc can't
 * handle the close, then maybe a new winsock is needed.
		if (read(Client->write, tmp, dccBlockSize()) == -1 && errno)
*/
			DCC_close_filesend(Client, "TSEND");
	} else 
		DCC_close_filesend(Client, tdcc?"TSEND":"SEND");
}

static	void		process_incoming_file (DCC_list *Client)
{
	char		tmp[MAX_DCC_BLOCK_SIZE+1];
	u_32int_t	bytestemp;
	int		bytesread;
	char		*Type;
	int		tdcc = 0;

	tdcc = Client->flags & DCC_TDCC;	
	Type = dcc_types[Client->flags & DCC_TYPES];

	if ((bytesread = read(Client->read, tmp, MAX_DCC_BLOCK_SIZE)) <= 0)
	{
		if (Client->bytes_read + Client->transfer_orders.byteoffset < Client->filesize)
			put_it("%s", convert_output_format("$G %RDCC get to $0 lost: Remote peer closed connection", "%s", Client->user));
		DCC_close_filesend(Client, tdcc?"TGET":"GET");
	}
	else
	{
		my_decrypt(tmp, bytesread, Client->encrypt);
		write(Client->file, tmp, bytesread);

		Client->bytes_read += bytesread;
		if (!(Client->flags & DCC_TDCC))
		{
			bytestemp = htonl(Client->bytes_read);
			send(Client->write, (char *)&bytestemp, sizeof(u_32int_t), 0);
		}
		Client->packets_transfer++;

/* TAKE THIS OUT IF IT CAUSES PROBLEMS */
		if (Client->filesize) 
		{
			if (Client->bytes_read > Client->filesize)
			{
				put_it("%s", convert_output_format("$G %RDCC%n Warning: incoming file is larger than the handshake said", NULL, NULL));
				put_it("%s", convert_output_format("$G %RDCC%n Warning: GET: closing connection", NULL, NULL));
				if (dcc_active_count)
					dcc_active_count--;
				Client->flags |= DCC_DELETE;
				dcc_update_stats(Client);
				update_all_status(curr_scr_win, NULL, 0);
				return;
			} 
			else if (Client->bytes_read == Client->filesize)
			{
				DCC_close_filesend(Client, tdcc?"TGET":"GET");
				return;
			}
		}
		update_transfer_buffer("");
		if (!(Client->packets_transfer % 20))
			update_all_status(curr_scr_win, NULL, 0);
	}
}

/* flag == 1 means show it.  flag == 0 used by redirect (and /ctcp) */

extern void	dcc_message_transmit (char *user, char *text, char *text_display, int type, int flag, char *cmd, int check_host)
{
	DCC_list	*Client;
	char	tmp[MAX_DCC_BLOCK_SIZE+1];
	int	lastlog_level;
	int	list = 0;
	int 	len = 0;
	char	*host = NULL;
	char	thing = 0;
	*tmp = 0;
	switch(type)
	{
		case DCC_CHAT:
			thing = '=';
			host = "chat";
			list = SEND_DCC_CHAT_LIST;
			break;
		case DCC_RAW:
			if (check_host)
			{
				if (!(host = next_arg(text, &text)))
				{
					put_it("%s", convert_output_format("$G %RDCC%n No host specified for DCC RAW", NULL, NULL));
					return;
				}
			}
			break;
	}
	if (!(Client = dcc_searchlist(host, user, type, 0, NULL, NULL, 1)) || !(Client->flags & DCC_ACTIVE))
	{
		put_it("%s", convert_output_format("$G %RDCC No active $0:$1 connection for $2", "%s %s %s", dcc_types[type], host?host:"(null)", user));
		return;
	}
	lastlog_level = set_lastlog_msg_level(LOG_DCC);
	message_from(Client->user, LOG_DCC);

	/*
	 * Check for CTCPs... whee.
	 */
	if (cmd && *text == CTCP_DELIM_CHAR)
	{
		if (!strcmp(cmd, "PRIVMSG"))
			strmcpy(tmp, "CTCP_MESSAGE ", dccBlockSize());
		else
			strmcpy(tmp, "CTCP_REPLY ", dccBlockSize());
	}

	strmcat(tmp, text, dccBlockSize()-3);
	strmcat(tmp, "\n", dccBlockSize()-2); 

	len = strlen(tmp);
	my_encrypt(tmp, len, Client->encrypt);
	write(Client->write, tmp, len);
	Client->bytes_sent += len;

	if (flag && type != DCC_RAW)
	{
		if (!my_strnicmp(tmp, ".chat", strlen(tmp)))
			Client->in_dcc_chat = 1;
		if (do_hook(list, "%s %s", Client->user, text_display ? text_display : text))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_SEND_DCC_CHAT_FSET), "%c %s %s", thing, Client->user, text_display?text_display:text));
	}
	set_lastlog_msg_level(lastlog_level);
	message_from(NULL, LOG_CRAP);
	return;
}

extern void	dcc_chat_transmit (char *user, char *text, char *orig, char *type)
{
	dcc_message_transmit(user, text, orig, DCC_CHAT, 1, type, 0);
}

extern void	dcc_raw_transmit (char *user, char *text, char *type)
{
	dcc_message_transmit(user, text, NULL, DCC_RAW, 0, type, 0);
}

extern void	dcc_bot_transmit (char *user, char *text, char *type)
{
	dcc_message_transmit(user, text, NULL, DCC_BOTMODE, 0, type, 1);
}

extern void dcc_chat_transmit_quiet (char *user, char *text, char *type)
{
	dcc_message_transmit(user, text, NULL, DCC_CHAT, 0, type, 0);
}

extern void dcc_chat_crash_transmit (char *user, char *text)
{
char buffer[20000];
	DCC_list	*Client;
	char	*host = "chat";

	memset(buffer, ' ', 20000-10);
	buffer[20000-9] = '\n';
	buffer[20000-8] = '\0';	
	if (!(Client = dcc_searchlist(host, user, DCC_CHAT, 0, NULL, NULL, 1)) || !(Client->flags&DCC_ACTIVE))
	{
		put_it("%s", convert_output_format("$G %RDCC%n No active DCC $0:$1 connection for $2", "%s %s %s", dcc_types[DCC_CHAT], host?host:"(null)", user));
		return;
	}
	send(Client->write, buffer, strlen(buffer), 0);
	Client->bytes_sent += strlen(buffer);
	return;
}

static	void		dcc_send_raw (char *command, char *args)
{
	char	*name;

	if (!(name = next_arg(args, &args)))
	{
		put_it("%s", convert_output_format("$G %RDCC%n No name specified for DCC raw", NULL, NULL));
		return;
	}
	dcc_message_transmit(name, args, NULL, DCC_RAW, 1, NULL, 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.
 */
char *dcc_time (time_t time)
{
	struct	tm	*btime;
	char	*buf = NULL;
	static	char	*months[] = 
	{
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};

	btime = localtime(&time);
	if (time)
		malloc_sprintf(&buf, "%-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;
}

static char *get_dcc_type (unsigned long flag)
{
static char ret[20];
	if (flag & DCC_TDCC)
	{
		switch(flag & DCC_TYPES)
		{
			case DCC_FILEOFFER:
				strcpy(ret, "TSEND");
				break;
			case DCC_FILEREAD:
				strcpy(ret, "TGET");
				break;
			case DCC_RESENDOFFER:
				strcpy(ret, "TRESEND");
				break;
			case DCC_REGETFILE:
				strcpy(ret, "TREGET");
				break;
		}
	}
	else
		strcpy(ret, dcc_types[flag & DCC_TYPES]);
	return ret;
}

extern void dcc_list (char *command, char *args)
{
	DCC_list	*Client;
	static	char	*format =
			"%-5.5s%-3.3s %-9.9s %-8.8s %-20.20s %-8.8s %-8.8s %s";
	unsigned	flags;
	int count = 0;
	char *filename = NULL;
		
	for (Client = ClientList ; Client != NULL ; Client = Client->next)
	{
		char	completed[9];
		char	size[9];
		char	*stime = NULL;

		if (Client->filesize)
		{
			sprintf(completed, "%ld%%",(unsigned long) (Client->bytes_sent?Client->bytes_sent:Client->bytes_read) * 100 / Client->filesize);
			sprintf(size, "%ld", (unsigned long)Client->filesize);
		}
		else
		{
			sprintf(completed, "%ldK", (unsigned long) (Client->bytes_sent ? Client->bytes_sent : Client->bytes_read) / 1024);
			strcpy(size, empty_string);
		}
		stime = Client->starttime.tv_sec ? dcc_time(Client->starttime.tv_sec): "";
		flags = Client->flags;

		if (!dcc_paths)
			filename = strrchr(Client->description, '/');

		if (!filename)
			filename = Client->description;

		if (!count)
			put_it(format, "Type", " ", "Nick", "Status", "Start time", "Size", "Complete", "Arguments");
		put_it(format,  
				get_dcc_type(flags & DCC_TYPES),
				Client->encrypt ? "[E]" : "",
				Client->user,
				flags&DCC_DELETE ? "Closed" :
				flags&DCC_ACTIVE ? "Active" : 
				flags&DCC_WAIT ? "Waiting" :
				flags&DCC_OFFER ? "Offered" : "Unknown",
				stime, size, completed, filename);

		if (stime && *stime)
			new_free(&stime);
		count++;
	}
}

void dcc_glist(char *command, char *args)
{
DCC_list	*Client = ClientList;

#if 0
static	char	*format =
		"$[3]0%W³%n $[5]1$2%W³%n $[10]3%W³%n $[8]4$[16]5%W³%n $[6]6%W³%n $7";
#endif

static	char	*dformat =
		"#$[2]0 $[6]1%Y$2%n $[11]3 $[25]4 $[7]5 $6";
static	char	*d1format =
		"#$[2]0 $[6]1%Y$2%n $[11]3 $4 $[11]5 $[10]6 $[7]7 $8-";
static	char	*c1format =
		"#$[2]0 $[6]1%Y$2%n $[11]3 $4 $[-4]5 $[-4]6 $[-3]7 $[-3]8  $[7]9 $10";

	unsigned	flags;
	unsigned	count= 0;
	int 		size = 0;
	double		barsize = 0.0, perc = 0.0;
	int 		barlen = BAR_LENGTH;
	char		spec[BIG_BUFFER_SIZE+1];
	char		*Type;
		
	barlen = BAR_LENGTH;
	barsize = 0.0;
	memset(spec, 0, sizeof(spec)-1);
	
	if (ClientList && do_hook(DCC_HEADER_LIST, "%s %s %s %s %s %s %s", "Dnum","Type","Nick", "Status", "K/s", "File","Encrypt"))
	{
#ifdef ONLY_STD_CHARS

		put_it("%s", convert_output_format("%G#  %W|%n %GT%gype  %W|%n %GN%gick      %W|%n %GP%gercent %GC%gomplete        %W|%n %GK%g/s   %W|%n %GF%gile", NULL, NULL));
		put_it("%s", convert_output_format("%W---------------------------------------------------------------------------", NULL, NULL));

#else
		put_it("%s", convert_output_format("%G#  %W³%n %GT%gype  %W³%n %GN%gick      %W³%n %GP%gercent %GC%gomplete        %W³%n %GK%g/s   %W³%n %GF%gile", NULL, NULL));
		put_it("%s", convert_output_format("%KÄÄ%nÄ%WÄ%nÄ%KÄÄÄÄÄ%nÄ%WÄ%nÄ%KÄÄÄÄÄÄÄÄÄ%nÄ%WÄ%nÄ%KÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ%nÄ%WÄ%nÄ%KÄÄÄÄÄ%nÄ%WÄ%nÄ%KÄÄÄÄÄ%nÄ%WÄ%nÄ%KÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ", NULL, NULL));
#endif
	}
	for (Client = ClientList ; Client != NULL ; Client = Client->next)
	{
		flags = Client->flags & DCC_TYPES;
		if ((flags == DCC_FILEOFFER || flags == DCC_FILEREAD || flags == DCC_RESENDOFFER || flags == DCC_REGETFILE || flags == DCC_FTPGET || flags == DCC_FTPSEND))
			if ((Client->flags & DCC_ACTIVE))
				continue;
		Type = get_dcc_type(flags & DCC_TYPES);
		if (do_hook(DCC_STAT_LIST, "%d %s %s %s %s %s %s", 
				Client->dccnum, Type, 
/*				dcc_types[flags & DCC_TYPES],*/
				Client->user,
				Client->flags & DCC_OFFER ? "Offer" :
				Client->flags & DCC_DELETE ? "Close" :
				Client->flags & DCC_ACTIVE ? "Active" :
				Client->flags & DCC_WAIT ? "Wait" :
#ifdef DCC_DCNT_PEND
				Client->flags & DCC_CNCT_PEND ?	"Connect" :
#endif
				"Unknown",
				"N/A", Client->description, Client->encrypt?"E":""))
		{				
			time_t u_time = (time_t) time_diff(Client->starttime, get_time(NULL));
			if ((((flags & DCC_TYPES) == DCC_CHAT) || ((flags & DCC_TYPES) == DCC_RAW) || ((flags & DCC_TYPES) == DCC_BOTMODE) || ((flags & DCC_TYPES) == DCC_FTPOPEN)) && (Client->flags & DCC_ACTIVE))
			{
				put_it("%s", convert_output_format(c1format, "%d %s %s %s %s %s %s %s", 
					Client->dccnum, 
					Type, 
/*					dcc_types[flags & DCC_TYPES],*/
					Client->encrypt ? "E" : "ÿ",
					Client->user,
					"Active",
					convert_time(u_time),
					"N/A",
					check_paths(Client->description)));
			}
			else
			{
				put_it("%s", convert_output_format(dformat, "%d %s %s %s %s %s %s", 
					Client->dccnum, 
/*					dcc_types[flags & DCC_TYPES],*/
					Type, 
					Client->encrypt ? "E" : "ÿ",
					Client->user,
				
					Client->flags & DCC_OFFER ?     "Offer" :
					Client->flags & DCC_DELETE ?    "Close" :
					Client->flags & DCC_ACTIVE ?    "Active" :
					Client->flags & DCC_WAIT ?      "Wait" :
#ifdef DCC_DCNT_PEND
					Client->flags & DCC_CNCT_PEND ?	"Connect" :
#endif
								"Unknown",
					"N/A", 
					check_paths(Client->description)));
			}
		}
			
		count++;
	}
	for (Client = ClientList ; Client != NULL ; Client = Client->next)
	{
		char	kilobytes[100];
		time_t	xtime = time(NULL) - Client->starttime.tv_sec;
		double	bytes = Client->bytes_read + Client->bytes_sent + Client->transfer_orders.byteoffset, 
			sent;
		char stats[80];
		int seconds = 0, minutes = 0;
		int iperc = 0; 
#ifdef ONLY_STD_CHARS
		char *_dcc_offer[12] = {"%K-.........%n",		/*  0 */
					"%K-.........%n",		/* 10 */
					"%K-=........%n",		/* 20 */
					"%K-=*.......%n",		/* 30 */
					"%K-=*%1%K=%0%K......%n",	/* 40 */
					"%K-=*%1%K=-%0%K.....%n",	/* 50 */
					"%K-=*%1%K=-.%0%K....%n",	/* 60 */
					"%K-=*%1%K=-. %0%K...%n",	/* 70 */
					"%K-=*%1%K=-. %R.%0%K..%n",	/* 80 */
					"%K-=*%1%K=-. %R.-%0%K.%n",	/* 90 */
					"%K-=*%1%K=-. %R.-=%n",		/* 100 */
					""};
#else
		char *_dcc_offer[12] = {"%K±°°°°°°°°°%n",		/*  0 */
					"%K±°°°°°°°°°%n",		/* 10 */
					"%K±²°°°°°°°°%n",		/* 20 */
					"%K±²Û°°°°°°°%n",		/* 30 */
					"%K±²Û%1%K²%0%K°°°°°°%n",	/* 40 */
					"%K±²Û%1%K²±%0%K°°°°°%n",	/* 50 */
					"%K±²Û%1%K²±°%0%K°°°°%n",	/* 60 */
					"%K±²Û%1%K²±°ÿ%0%K°°°%n",	/* 70 */
					"%K±²Û%1%K²±°ÿ%R°%0%K°°%n",	/* 80 */
					"%K±²Û%1%K²±°ÿ%R°±%0%K°%n",	/* 90 */
					"%K±²Û%1%K²±°ÿ%R°±²%n",		/* 100 */
					""};
#endif
		char *bar_end;
		*stats = 0;
		*kilobytes = 0;
		perc = 0.0;		
		sent = bytes - Client->transfer_orders.byteoffset;
		flags = Client->flags & DCC_TYPES;

		if ((Client->flags & DCC_WAIT || Client->flags & DCC_OFFER) || ((flags & DCC_FILEOFFER)==0) || ((flags & DCC_FILEREAD) == 0) || ((flags & DCC_RESENDOFFER) == 0) || ((flags & DCC_REGETFILE) == 0))
			continue;
			
		sent /= (double)1024.0;
		if (xtime <= 0)
			xtime = 1;
		sprintf(kilobytes, "%2.4g", sent/(double)xtime);
	
		if ((bytes >= 0) && (Client->flags & DCC_ACTIVE)) 
		{
			if (bytes && Client->filesize >= bytes)
			{
				perc = (100.0 * ((double)bytes)   / (double)(Client->filesize));
				if ( perc > 100.0) perc = 100.0;
				else if (perc < 0.0) perc = 0.0;
				seconds = (int) (( (Client->filesize - bytes) / (bytes / xtime)) + 0.5);
				minutes = seconds / 60;
				seconds = seconds - (minutes * 60);
				if (minutes > 999) {
					minutes = 999;
					seconds = 59;
				}
				if (seconds < 0) seconds = 0;
			} else
				seconds = minutes = perc = 0;
				
			iperc = ((int)perc) / 10;
			barsize = ((double) (Client->filesize)) / (double) barlen;

			size = (int) ((double) bytes / (double)barsize);
	
			if (Client->filesize == 0)
				size = barlen;
			sprintf(stats, "%4.1f", perc);
			sprintf(spec, "%s %s%s %02d:%02d", _dcc_offer[iperc], stats, "%%", minutes, seconds);
			sprintf(spec, "%s", convert_output_format(spec, NULL, NULL));
		}
		Type = get_dcc_type(flags & DCC_TYPES);		
		if (do_hook(DCC_STATF_LIST, "%d %s %s %s %s %s %s", 
			Client->dccnum, Type,
			/* dcc_types[flags &DCC_TYPES],*/
			Client->user,
			Client->flags & DCC_OFFER ? "Offer" :
			Client->flags & DCC_DELETE ? "Close" :
			Client->flags & DCC_ACTIVE ? "Active" :
			Client->flags & DCC_WAIT ? "Wait" :
#ifdef DCC_DCNT_PEND	
			Client->flags & DCC_CNCT_PEND ?	"Connect" :
#endif
			"Unknown",
			kilobytes, check_paths(Client->description), Client->encrypt?"E":""))
		{
			char *s;
			if (get_int_var(DISPLAY_ANSI_VAR))
				if (!get_int_var(DCC_BAR_TYPE_VAR))
					s = d1format;
				else
					s = dformat;
			else
				s = dformat;
			put_it("%s", convert_output_format(s, "%d %s %s %s %s %s %s", 
				Client->dccnum, Type, 
				/*dcc_types[flags & DCC_TYPES],*/
				Client->encrypt ? "E":"ÿ",
				Client->user,
				Client->flags & DCC_OFFER ? "Offer" :
				Client->flags & DCC_DELETE ? "Close" :
				Client->flags & DCC_ACTIVE ? ((get_int_var(DISPLAY_ANSI_VAR) && !get_int_var(DCC_BAR_TYPE_VAR)) ? spec : "Active"):
				Client->flags & DCC_WAIT ? "Wait" :
#ifdef DCC_DCNT_PEND
				Client->flags & DCC_CNCT_PEND ?	"Connect" :
#endif	
				"?????",
				kilobytes, 
				strip_path(Client->description)));
		}

		if (do_hook(DCC_STATF1_LIST, "%s %ld %ld %d %d", 
			stats,(unsigned long)bytes, (unsigned long)Client->filesize, 
			minutes, seconds) && (!get_int_var(DISPLAY_ANSI_VAR) || get_int_var(DCC_BAR_TYPE_VAR)))
		{
			sprintf(stats, "%4.1f%% (%ld of %ld bytes)", perc, (unsigned long)bytes, (unsigned long)Client->filesize);
			strcpy( spec, "\002[\026");
			sprintf(spec+3, "%*s", size+1, " ");
			bar_end = spec + (strlen(spec));
			sprintf(bar_end, "%*s", barlen-size+1, " ");
			strcat(spec, "\026\002]\002 ETA %02d:%02d");
			memcpy((spec+(((BAR_LENGTH+2) / 2) - (strlen(stats) / 2))), stats, strlen(stats));
			if (size < barlen)
			{
				memmove(bar_end+1, bar_end, strlen(bar_end));
				*bar_end = '\002';
			}
			put_it(spec, minutes, seconds);	
		}
		count++;
	}
	if (ClientList && count)
		do_hook(DCC_POST_LIST, "%s %s %s %s %s %s %s", "DCCnum","Type","Nick", "Status", "K/s", "File","Encrypt");
	if (count == 0)
		put_it("%s", convert_output_format("$G %RDCC%n Nothing on DCC list.", NULL, NULL));
}

static char DCC_reject_type[12];
static char DCC_reject_description[40];

static	void 	output_reject_ctcp (char *notused, char *nicklist)
{
	if (nicklist && *nicklist && *DCC_reject_description)
		send_ctcp(CTCP_NOTICE, nicklist, CTCP_DCC,
			"REJECT %s %s", DCC_reject_type, DCC_reject_description);
	strcpy(DCC_reject_description, empty_string);
	strcpy(DCC_reject_type, empty_string);
}

/* added by Patch */
void dcc_close_client_num(unsigned int closenum)
{
	DCC_list	*Client, *next;
	unsigned	flags;
	char		*Type;
	int		to_from_idx;

	for (Client = ClientList ; Client != NULL ; Client = next)
	{
		flags = Client->flags;
		Type = get_dcc_type(flags & DCC_TYPES);
		/*dcc_types[flags & DCC_TYPES];*/
		to_from_idx = get_to_from(Type);
		next = Client->next;

		if (Client->dccnum == closenum)
		{
			if (flags & DCC_DELETE)
				return;
			if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
			{
				if (flags & DCC_ACTIVE && to_from_idx != -1)
					dcc_update_stats(Client);
				if (to_from_idx != -1 && dcc_active_count) dcc_active_count--;
				if (Client->read > -1) FD_CLR(Client->read, &readables);
				close(Client->read);
				if (Client->file)
					close(Client->file);
			}

			if (do_hook(DCC_LOST_LIST, "%s %s %s", Client->user, Type, 
				Client->description? Client->description : "<any>"))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), Type, Client->user, Client->description));
			dcc_reject_notify(Client->description, Client->user, Type);
			dcc_erase(Client);
			update_transfer_buffer("");
			update_all_status(curr_scr_win, NULL, 0);
			return;
		}
	}

	put_it("%s", convert_output_format("%RDCC%n CLOSE number $0 does not exist","%d", closenum));
}

/* added by Patch */
void
dcc_close_all(void)
{
	DCC_list	*Client, *next;
	unsigned	flags;
	char		*Type;
	int		to_from_idx;

	for (Client = ClientList ; Client != NULL ; Client = next)
	{
		flags = Client->flags;
		Type = get_dcc_type(flags & DCC_TYPES);
		/*dcc_types[flags & DCC_TYPES];*/
		to_from_idx = get_to_from(Type);
		next = Client->next;
		
		if (flags & DCC_DELETE)
			continue;
		if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
		{
			if (flags & DCC_ACTIVE && to_from_idx != -1)
				dcc_update_stats(Client);
			if (to_from_idx != -1 && dcc_active_count) dcc_active_count--;
		}

		if (do_hook(DCC_LOST_LIST, "%s %s %s", Client->user, Type, 
			Client->description? Client->description : "<any>"))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), Type, Client->user, Client->description));
		dcc_reject_notify(Client->description, Client->user, Type);
		dcc_erase(Client);
		update_transfer_buffer("");
		update_all_status(curr_scr_win, NULL, 0);
	}
}

/* added by Patch */
void
dcc_close_type_all(char *typestr)
{
	DCC_list	*Client, *next;
	unsigned	flags;
	char		*Type;
	int		to_from_idx;

	to_from_idx = get_to_from(typestr);

	for (Client = ClientList ; Client != NULL ; Client = next)
	{
		flags = Client->flags;
		Type = get_dcc_type(flags & DCC_TYPES);
		/*dcc_types[flags & DCC_TYPES];*/
		next = Client->next;
		
		if (my_stricmp(Type,typestr) == 0)
		{
			if (flags & DCC_DELETE)
				return;
			if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
			{
				if (flags & DCC_ACTIVE && to_from_idx != -1)
					dcc_update_stats(Client);
				if (to_from_idx != -1 && dcc_active_count) dcc_active_count--;
				if (Client->read > -1) FD_CLR(Client->read, &readables);
				close(Client->read);
				if (Client->file)
					close(Client->file);
			}
			if (do_hook(DCC_LOST_LIST, "%s %s %s", Client->user, Type, 
				Client->description? Client->description : "<any>"))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), Type, Client->user, Client->description));
			dcc_reject_notify(Client->description, Client->user, Type);
			dcc_erase(Client);
			update_transfer_buffer("");
			update_all_status(curr_scr_win, NULL, 0);
		}
	}
}

/* added by Patch */
void
dcc_close_nick_all(char *nickstr)
{
	DCC_list	*Client, *next;
	unsigned	flags;
	char		*Type;
	int		to_from_idx;

	for (Client = ClientList ; Client != NULL ; Client = next)
	{
		flags = Client->flags;
		Type = get_dcc_type(flags & DCC_TYPES);
		/*dcc_types[flags & DCC_TYPES];*/
		to_from_idx = get_to_from(Type);
		next = Client->next;
		
		if (my_stricmp(Client->user,nickstr) == 0)
		{
			if (flags & DCC_DELETE)
				return;
			if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
			{
				if (flags & DCC_ACTIVE && to_from_idx != -1)
					dcc_update_stats(Client);
				if (to_from_idx != -1 && dcc_active_count) dcc_active_count--;
				FD_CLR(Client->read, &readables);
				close(Client->read);
				if (Client->file)
					close(Client->file);
			}

			if (do_hook(DCC_LOST_LIST, "%s %s %s", Client->user, Type, 
				Client->description? Client->description : "<any>"))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), Type, Client->user, Client->description));
			dcc_reject_notify(Client->description, Client->user, Type);
			dcc_erase(Client);
			update_transfer_buffer("");
			update_all_status(curr_scr_win, NULL, 0);
		}
	}
}

/* added by Patch */
void
dcc_close_type_nick_all(char *typestr, char *nickstr)
{
	DCC_list	*Client, *next;
	unsigned	flags;
	char		*Type;
	int		to_from_idx;

	to_from_idx = get_to_from(typestr);

	for (Client = ClientList ; Client != NULL ; Client = next)
	{
		flags = Client->flags;
		Type = get_dcc_type(flags & DCC_TYPES);
		/*dcc_types[flags & DCC_TYPES];*/
		next = Client->next;
		
		if (my_stricmp(Type,typestr) == 0 &&
			my_stricmp(Client->user,nickstr) == 0)
		{
			if (flags & DCC_DELETE)
				return;
			if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
			{
				if (flags & DCC_ACTIVE && to_from_idx != -1)
					dcc_update_stats(Client);
				if (to_from_idx != -1 && dcc_active_count) dcc_active_count--;
				FD_CLR(Client->read, &readables);
				close(Client->read);
				if (Client->file)
					close(Client->file);
			}
			if (do_hook(DCC_LOST_LIST, "%s %s %s", Client->user, Type, 
				Client->description? Client->description : "<any>"))
				put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), Type, Client->user, Client->description));
			dcc_reject_notify(Client->description, Client->user, Type);
			dcc_erase(Client);
			update_transfer_buffer("");
			update_all_status(curr_scr_win, NULL, 0);
		}
	}
}

/* added by Patch */
void
dcc_close_filename(char *filename, char *user, char *Type, int CType)
{
	DCC_list	*Client;
	unsigned	flags;
	int		to_from_idx;

	to_from_idx = get_to_from(Type);
	if ((Client = dcc_searchlist(filename, user, CType, 0, filename, NULL, -1)))
	{
		flags = Client->flags;
		if (flags & DCC_DELETE)
			return;
		if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
		{
			if (flags & DCC_ACTIVE && to_from_idx != -1)
				dcc_update_stats(Client);
			if (to_from_idx != -1 && dcc_active_count) dcc_active_count--;
		}

		if (do_hook(DCC_LOST_LIST, "%s %s %s", Client->user, Type, 
			Client->description? Client->description : "<any>"))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), Type, Client->user, Client->description));
		dcc_reject_notify(Client->description, Client->user, Type);
		dcc_erase(Client);
		update_transfer_buffer("");
		update_all_status(curr_scr_win, NULL, 0);
	}
	else
		put_it("%s", convert_output_format("$G %RDCC%n No DCC $0:$1 to $2 found","%s %s %s", Type, filename?filename:"(null)",user));
}

/* completely rewritten by Patch */
void dcc_close(char *command, char *args)
{
	char		*Type;
	char		*user;
	char		*description;
	int		CType;
	unsigned int 	closenum;

	Type = next_arg(args, &args);
	user = next_arg(args, &args);
	description = next_arg(args, &args);

/*****************************************************************************
 DCC CLOSE #all or #number
*****************************************************************************/

	if (!Type)
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must specify a type|dcc_num for DCC close", NULL, NULL));
		return;
	}

	if ((Type[0] == '#' && !user && *(Type+1) && my_atol(Type+1)) || !my_stricmp(Type, "-all"))
	{
		if (!my_stricmp(Type,"-all"))
		{
			dcc_close_all();
			dcc_active_count = 0;
			update_all_status(curr_scr_win, NULL, 0);
			return;
		}

		closenum = atol(Type+1);

		if (closenum == 0)
		{
			put_it("%s", convert_output_format("$G %RDCC%n close invalid number", NULL, NULL));
			return;
		}

		dcc_close_client_num(closenum);
		update_all_status(curr_scr_win, NULL, 0);
		return;
	}

/*****************************************************************************
 DCC CLOSE SEND|GET|RESEND|REGET|nick #all
*****************************************************************************/

	if (!user)
	{
		put_it("%s", convert_output_format("$G %RDCC%n specify a type and a nick for DCC close", NULL, NULL));
		return;
	}

	for (CType = 0; dcc_types[CType] != NULL; CType++)
		if (!my_stricmp(Type, dcc_types[CType])) break;

	if (user[0] == '#' && !description && (my_atol(user+1) || my_stricmp(user+1, "all")==0))
	{
		if (my_stricmp(user+1,"all") == 0)
		{
			if (!dcc_types[CType])
				dcc_close_nick_all(Type);
			else
				dcc_close_type_all(Type);
			update_all_status(curr_scr_win, NULL, 0);
			return;
		}
		put_it("%s", convert_output_format("$G %RDCC%n close invalid number", NULL, NULL));
		return;
	}

/*****************************************************************************
 DCC CLOSE SEND|GET|RESEND|REGET nick #all|filename
*****************************************************************************/

	if (description && *description == '-')
	{
		if (!my_stricmp(description,"-all"))
			dcc_close_type_nick_all(Type,user);
		else 
		put_it("%s", convert_output_format("$G %RDCC%n CLOSE invalid description", NULL, NULL));
		return;
	}

	if (dcc_types[CType])
		dcc_close_filename(description, user, Type, CType);
	else
		put_it("%s", convert_output_format("$G %RDCC%n Unknown type [$0]", "%s", dcc_types[CType]));
}

void dcc_reject_notify(char *description, char *user, char *Type)
{
	strcpy(DCC_reject_description, description ? description : "(null)");
	if (!my_stricmp(Type, "TSEND"))
		strcpy(DCC_reject_type, "TGET");
	else if (!my_stricmp(Type, "TGET"))
		strcpy(DCC_reject_type, "TSEND");
	else if (!my_stricmp(Type, "TREGET"))
		strcpy(DCC_reject_type, "TRESEND");
	else if (!my_stricmp(Type, "TRESEND"))
		strcpy(DCC_reject_type, "TREGET");
	else if (!my_stricmp(Type, "SEND"))
		strcpy(DCC_reject_type, "GET");
	else if (!my_stricmp(Type, "GET"))
		strcpy(DCC_reject_type, "SEND");
	else if (!my_stricmp(Type, "RESEND"))
		strcpy(DCC_reject_type, "REGET");
	else if (!my_stricmp(Type, "REGET"))
		strcpy(DCC_reject_type, "RESEND");
	else
		strcpy(DCC_reject_type, Type);
	if (*user == '=')
		user++;
	add_ison_to_whois (user, output_reject_ctcp);
}

extern void dcc_reject (char *from, char *type, char *args)
{
	DCC_list	*Client;
	char	*description;
	int	CType;
	int	tdcc = 0;
	
	upper(type);
	if (*type == 'T' && *(type+1))
		tdcc = 1;
	for (CType = 0; dcc_types[CType] != NULL; CType++)
		if (!strcmp(type+tdcc, dcc_types[CType]))
			break;

	if (!dcc_types[CType])
		return;
	if (tdcc)
		CType |= DCC_TDCC;
		
	description = next_arg(args, &args);

	if ((Client = dcc_searchlist(NULL, from, CType, 0, description, NULL, -1)))
	{
		if (Client->flags & DCC_DELETE)
			return;
                if (do_hook(DCC_LOST_LIST,"%s %s %s REJECTED", from, type, description ? description : "<any>"))
			put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_ERROR_FSET), "%s %s %s %s", update_clock(GET_TIME), type, Client->user, Client->description));
		update_transfer_buffer("");
		Client->flags |= DCC_DELETE;
		update_all_status(curr_scr_win, NULL, 0);
		dcc_sendfrom_queue();
	}
}

static	void		dcc_rename (char *command, char *args)
{
	DCC_list	*Client;
	char	*user;
	char	*description;
	char	*newdesc;
	char	*temp;
	
	if (!(user = next_arg(args, &args)) || !(temp = next_arg(args, &args)))
	{
		put_it("%s", convert_output_format("$G %RDCC%n You must specify a nick and a new filename", NULL, NULL));
		return;
	}
	if ((newdesc = next_arg(args, &args)) != NULL)
		description = temp;
	else
	{
		newdesc = temp;
		description = NULL;
	}

	if ((Client = dcc_searchlist(description, user, DCC_FILEREAD, 0, NULL, NULL, 0)))
	{
		/* Is this needed now? */
		if (!(Client->flags & DCC_OFFER))
		{
			put_it("%s", convert_output_format("$G %RDCC Too late to rename that file", NULL, NULL));
			return;
		}
		new_free(&(Client->description));
		malloc_strcpy(&(Client->description), newdesc);
		put_it("%s", convert_output_format("$G %RDCC File $0 from $1 rename to $2", "%s %s %s", description?description:"(null)", user, newdesc));
	}
	else
		put_it("%s", convert_output_format("$G %RDCC No file $0 from $1 found, or it's too late to rename", "%s %s", description?description:"(null)", user));
}

/*
 * 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..
 */
extern void close_all_dcc _((void))
{
	DCC_list *Client, *last;

	for (Client = ClientList; Client; Client = last)
	{
		last = Client->next;
		dcc_erase(Client);
	}
	dcc_active_count = 0;
}


static void add_to_dcc_buffer(DCC_list *Client, char *buf)
{
       if (buf && *buf)
       {
               if (Client->buffer)
                       malloc_strcat(&Client->buffer, buf);
               else
                       malloc_strcpy(&Client->buffer, buf);
       }
}

/* Looks for the dcc transfer that is "current" (last recieved data)
 * and returns information for it
 */
extern char *DCC_get_current_transfer _((void))
{
	return DCC_current_transfer_buffer;
}

BUILT_IN_COMMAND(chat)
{
int no_chat = 0;
	if (!my_strnicmp(command, "NOC", 3))
		no_chat = 1;
	if (args && *args)
	{
		char *tmp = NULL;
		if (no_chat)
			malloc_sprintf(&tmp, "CLOSE CHAT %s", args);
		else
			malloc_sprintf(&tmp, "CHAT %s", args);
		process_dcc(tmp);
		new_free(&tmp);
	}
	else if (last_chat_req)
	{
		DCC_list *chat_req = NULL;
		if ((chat_req = dcc_searchlist("chat", last_chat_req, DCC_CHAT, 0, NULL, NULL, no_chat?-1:0)))
		{
			if (no_chat)
			{
				dcc_close_filename(NULL, last_chat_req, "CHAT", DCC_CHAT);
				new_free(&last_chat_req);
			}
			else	
			{
				char nick[BIG_BUFFER_SIZE];
				dcc_open(chat_req);
				sprintf(nick, "=%s", chat_req->user);
				addtabkey(nick, "msg", 0);
			}
		}
		else
			bitchsay("Error occurred");
	} else 
		userage(command, helparg);
}


static void DCC_close_filesend (DCC_list *Client, char *type)
{
	char	lame_ultrix[30];	/* should be plenty */
	char	lame_ultrix2[30];
	char	lame_ultrix3[30];
	char	buffer[200];
	char 	*tofrom = NULL;
	time_t xtime;
	double xfer;

	xtime = time_diff(Client->starttime, get_time(NULL));
	xfer = (double)(Client->bytes_sent ? Client->bytes_sent : Client->bytes_read);

	if (xfer <= 0)
		xfer = 1;
	if (xtime <= 0)
		xtime = 1;
	sprintf(lame_ultrix, "%2.4g", (xfer/ 1024.0 / xtime));
	/* Cant pass %g to put_it (lame ultrix/dgux), fix suggested by sheik. */
	sprintf(lame_ultrix2, "%2.4g%s", _GMKv(xfer), _GMKs(xfer));
	sprintf(lame_ultrix3, "%2.4g", (double)xtime);
	sprintf(buffer, "%%s %s %%s %%s TRANSFER COMPLETE", type);

	switch(get_to_from(dcc_types[Client->flags&DCC_TYPES]))
	{
		case 0:
		case 1:
			tofrom = "to";
			break;
		case 2:
		case 3:
			tofrom = "from";
			break;
		default:
			tofrom = "to";
			break;
	}
	if(do_hook(DCC_LOST_LIST,buffer,Client->user, check_paths(Client->description), lame_ultrix))
		put_it("%s", convert_output_format(fget_string_var(FORMAT_DCC_LOST_FSET),
		"%s %s %s %s %s %s %s %s %s", update_clock(GET_TIME), type, 
		check_paths(Client->description), lame_ultrix2, tofrom, Client->user, 
		lame_ultrix3, lame_ultrix, "Kb"));

	dcc_update_stats(Client);
	if (get_to_from(dcc_types[Client->flags&DCC_TYPES]) != -1 && dcc_active_count)
		dcc_active_count--;
	FD_CLR(Client->read, &readables);  
	close(Client->read);
	FD_CLR(Client->write, &writables);
	if (Client->read != Client->write)
		close (Client->write);
	close(Client->file);
	Client->file = Client->read = Client->write = -1;
	Client->flags |= DCC_DELETE;
	*DCC_current_transfer_buffer = 0;
	update_transfer_buffer("");
	update_all_status(curr_scr_win, NULL, 0);
}

char transfer_buffer[BIG_BUFFER_SIZE];

static void update_transfer_buffer (char *format, ...)
{
	register DCC_list	*Client = ClientList;
	unsigned	count= 0;
	double		perc = 0.0;
	char temp_str[60];
		
	errno = 0;
	*transfer_buffer = 0;
	for (Client = ClientList ; Client; Client = Client->next)
	{
		double	bytes;
		unsigned long flags = Client->flags & DCC_TYPES;
		
		if ((flags == DCC_RAW) || (flags == DCC_RAW_LISTEN) || (flags == DCC_CHAT) || (flags == DCC_BOTMODE) || (flags == DCC_FTPOPEN))
			continue;
		if ((Client->flags & DCC_WAIT) || (Client->flags == DCC_DELETE))
			continue;
		bytes = Client->bytes_read + Client->bytes_sent + Client->transfer_orders.byteoffset;
		if (bytes >= 0) 
		{
			if (Client->filesize >= bytes)
			{
				perc = (100.0 * ((double)bytes)   / (double)(Client->filesize));
				if ( perc > 100.0) perc = 100.0;
				else if (perc < 0.0) perc = 0.0;

			}				
			sprintf(temp_str,"%d%%,",(int) perc);
			strcat(transfer_buffer,temp_str);
		}
		if (count++ > 9)
			break;
	}
	if (count)
	{
		chop(transfer_buffer, 1);
		if (fget_string_var(FORMAT_DCC_FSET))
		{
			sprintf(DCC_current_transfer_buffer, convert_output_format(fget_string_var(FORMAT_DCC_FSET), "%s", transfer_buffer));
			chop(DCC_current_transfer_buffer, 4);
		}
		else
			sprintf(DCC_current_transfer_buffer, "[%s]", transfer_buffer);
	}
	else
		*DCC_current_transfer_buffer = 0;
}

int get_to_from(char *Type) {
	if (!my_stricmp(Type, "SEND"))
		return 0;
	else if (!my_stricmp(Type, "RESEND"))
		return 1;
	else if (!my_stricmp(Type, "GET"))
		return 2;
	else if (!my_stricmp(Type, "REGET"))
		return 3;
	else if (!my_stricmp(Type, "TSEND"))
		return 4;
	else if (!my_stricmp(Type, "TRESEND"))
		return 5;
	else if (!my_stricmp(Type, "TGET"))
		return 6;
	else if (!my_stricmp(Type, "TREGET"))
		return 7;
	else 
		return -1;
}

static void dcc_help1 (char *command, char *args)
{
	put_it("%s", convert_output_format("$G %RDCC%n help -", NULL, NULL));
	put_it("%s", convert_output_format("   Active    Stats    List     GList   ?", NULL, NULL));
	put_it("%s", convert_output_format("   Resend/Reget    Send/Get    Chat    Raw   Bot   Close  Rename", NULL, NULL));	
	put_it("%s", convert_output_format("   Auto   Quiet   Paths   Quiet   Overwrite  Auto_Rename  Exempt", NULL, NULL));
#ifdef MIRC_BROKEN_DCC_RESUME
	put_it("%s", convert_output_format("   Resume", NULL, NULL));
#endif
}

static void dcc_show_active(char *command, char * args) 
{
	put_it("%s", convert_output_format("$G %RDCC%n  DCC Active = \002$0\002, Limit = \002$1\002", 
		"%d %d", dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR)));
}

static void dcc_set_quiet(char *command, char *args ) 
{
	dcc_quiet ^=1;
	put_it("%s", convert_output_format("$G %RDCC%n  DCC Quiet = \002$0\002", "%s", on_off(dcc_quiet)));
}

/* returns the string without the path (if present) */
static char * strip_path(char *str)
{
	char *ptr;

	ptr = strrchr(str,'/');
	if (ptr == NULL)
		return str;
	else
		return ptr+1;
}

/* returns the string without the path (if present) */
static char *check_paths(char *str)
{
	if (dcc_paths == 0)
		return strip_path(str);
	else
		return str;
}

static void dcc_set_paths(char *command, char *args)
{
	dcc_paths ^= 1;
	
	put_it("%s", convert_output_format("$G %RDCC%n  DCC paths is now \002$0\002", "%s", on_off(dcc_paths)));
}	

static void dcc_tog_rename(char *command, char *args)
{
int	arename = get_int_var(DCC_AUTORENAME_VAR);
	arename ^= 1;
	set_int_var(DCC_AUTORENAME_VAR, arename);
	put_it("%s", convert_output_format("$G %RDCC%n  DCC auto rename is now \002$0\002", "%s", on_off(get_int_var(DCC_AUTORENAME_VAR))));
}	

static void dcc_overwrite_toggle(char *command, char *args)
{
	dcc_overwrite_var ^= 1;
	
	put_it("%s", convert_output_format("  DCC overwrite is now \002$0\002", "%s", on_off(dcc_overwrite_var)));
}	

void dcc_tog_auto(char *command, char *args)
{
	int dcc_auto = get_int_var(DCC_AUTOGET_VAR);
	dcc_auto ^= 1;
	set_int_var(DCC_AUTOGET_VAR, dcc_auto);	
	put_it("%s", convert_output_format("  DCC autoget is now \002$0\002", "%s", on_off(dcc_auto)));
}	

void dcc_stats (char *command, char *unused)
{
char max_rate_in[20];
char min_rate_in[20];
char max_rate_out[20];
char min_rate_out[20];

	sprintf(max_rate_in, "%6.2f", dcc_max_rate_in/1024.0);
	sprintf(min_rate_in, "%6.2f", ((dcc_min_rate_in != DBL_MAX )?dcc_min_rate_in/1024.0: 0.0));
	sprintf(max_rate_out, "%6.2f", dcc_max_rate_out/1024.0);
	sprintf(min_rate_out, "%6.2f", ((dcc_min_rate_out != DBL_MAX) ? dcc_min_rate_out/1024.0: 0.0));
	if (do_hook(DCC_TRANSFER_STAT_LIST, "%lu %s %s %lu %s %s %lu %u %u %s %s %s %s", 
		(unsigned long)dcc_bytes_in, max_rate_in, min_rate_in,
		(unsigned long)dcc_bytes_out, max_rate_out, min_rate_out,
		send_count_stat+get_count_stat, dcc_active_count, get_int_var(DCC_SEND_LIMIT_VAR),
		on_off(get_int_var(DCC_AUTOGET_VAR)), on_off(dcc_paths), 
		on_off(dcc_quiet), on_off(dcc_overwrite_var)))
	{
		char in[50], out[50];
		sprintf(in,  "%3.2f%s", _GMKv(dcc_bytes_in),  _GMKs(dcc_bytes_in));
		sprintf(out, "%3.2f%s", _GMKv(dcc_bytes_out), _GMKs(dcc_bytes_out));

#ifdef ONLY_STD_CHARS
		put_it("%s",convert_output_format("       %G========================%K[%Cdcc transfer stats%K]%G=======================", NULL));
		put_it("%s",convert_output_format("       %G|                                                                 |", NULL));
		put_it("%s",convert_output_format("       %G|%g|-%K[%Cx%cferd %Ci%cn%K]%g-|-%K[%Cx%cferd %Co%cut%K]%g-|-%K[%Ct%cotal %Cf%ciles%K]%g-|-%K[%Ca%cctive%K]%g-|-[%Cl%cimit%K]%g-|%G|", NULL));
		put_it("%s",convert_output_format("       %G|%g| %W$[-10]0 %g|  %W$[-10]1 %g|    %W$[-10]2 %g| %W$[-8]3 %g| %W$[-7]4 %g|%G|", "%s %s %d %d %d", in, out,send_count_stat+get_count_stat,dcc_active_count,get_int_var(DCC_SEND_LIMIT_VAR)));
		put_it("%s",convert_output_format("       %G|%g|------------|-------------|---------------|----------|---------|%G|", NULL));
		put_it("%s",convert_output_format("       %G|                                                                 |", NULL));
		put_it("%s",convert_output_format("       %g|----%K[%Ci%cn %Cs%ctats%K]%g---|---%K[%Co%cut %Cs%ctats%K]%g---|----------%K[%Ct%coggles%K]%g----------|", NULL));
		put_it("%s",convert_output_format("       %g| %Cm%nax: %W$[-6]0%n%Rkb/s %g| %Cm%nax: %W$[-6]1%n%Rkb/s %g|   %Ca%nutoget: %W$[-3]2%n   %Cp%naths: %W$[-3]3 %g|", "%s %s %s %s", max_rate_in, max_rate_out, on_off(get_int_var(DCC_AUTOGET_VAR)),on_off(dcc_paths)));
		put_it("%s",convert_output_format("       %g| %Cm%nin: %W$[-6]0%n%Rkb/s %g| %Cm%nin: %W$[-6]1%n%Rkb/s %g| %Co%nverwrite: %W$[-3]2%n   %Cq%nuiet: %W$[-3]3 %g|", "%s %s %s %s", min_rate_in, min_rate_out, on_off(dcc_overwrite_var), on_off(dcc_quiet)));
		put_it("%s",convert_output_format("       %g|-----------------|-----------------|-----------------------------|", NULL));

#else

		put_it("%s",convert_output_format("       %GÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ%K[%Cdcc transfer stats%K]%GÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¸", NULL));
		put_it("%s",convert_output_format("       %G³                                                                 ³", NULL));
		put_it("%s",convert_output_format("       %G³%gÖÄ%K[%Cx%cferd %Ci%cn%K]%gÄÖ-%K[%Cx%cferd %Co%cut%K]%gÄ·Ä%K[%Ct%cotal %Cf%ciles%K]%gÄÖÄ%K[%Ca%cctive%K]%gÄ·Ä[%Cl%cimit%K]%gÄ·%G³", NULL));
		put_it("%s",convert_output_format("       %G³%gº %W$[-10]0 %gº  %W$[-10]1 %gº    %W$[-10]2 %gº %W$[-8]3 %gº %W$[-7]4 %gº%G³", "%s %s %d %d %d", in, out,send_count_stat+get_count_stat,dcc_active_count,get_int_var(DCC_SEND_LIMIT_VAR)));
		put_it("%s",convert_output_format("       %G³%gÓÄÄÄÄÄÄÄÄÄÄÄÄ½ÄÄÄÄÄÄÄÄÄÄÄÄÄÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ½ÄÄÄÄÄÄÄÄÄÄÓÄÄÄÄÄÄÄÄÄ½%G³", NULL));
		put_it("%s",convert_output_format("       %G³                                                                 ³", NULL));
		put_it("%s",convert_output_format("       %gÖÄÄÄÄ%K[%Ci%cn %Cs%ctats%K]%gÄÄÄÖÄÄÄ%K[%Co%cut %Cs%ctats%K]%gÄÄÄ·ÄÄÄÄÄÄÄÄÄÄ%K[%Ct%coggles%K]%gÄÄÄÄÄÄÄÄÄÄ·", NULL));
		put_it("%s",convert_output_format("       %gº %Cm%nax: %W$[-6]0%n%Rkb/s %gº %Cm%nax: %W$[-6]1%n%Rkb/s %gº   %Ca%nutoget: %W$[-3]2%n   %Cp%naths: %W$[-3]3 %gº", "%s %s %s %s", max_rate_in, max_rate_out, on_off(get_int_var(DCC_AUTOGET_VAR)),on_off(dcc_paths)));
		put_it("%s",convert_output_format("       %gº %Cm%nin: %W$[-6]0%n%Rkb/s %gº %Cm%nin: %W$[-6]1%n%Rkb/s %gº %Co%nverwrite: %W$[-3]2%n   %Cq%nuiet: %W$[-3]3 %gº", "%s %s %s %s", min_rate_in, min_rate_out, on_off(dcc_overwrite_var), on_off(dcc_quiet)));
		put_it("%s",convert_output_format("       %gÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ½ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÓÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ½", NULL));

#endif

	}
}

/*
 * only call this on dcc finish
 */
static void dcc_update_stats (DCC_list *Client)
{
time_t	xtime = time_diff(Client->starttime, get_time(NULL));
	
	dcc_bytes_in += Client->bytes_read;
	dcc_bytes_out += Client->bytes_sent;
	if (xtime <= 0)
		xtime = 1;
	if (Client->bytes_read)
	{
		get_count_stat++;
		if ((double)Client->bytes_read/(double)xtime > dcc_max_rate_in)
			dcc_max_rate_in = (double)Client->bytes_read/(double)xtime;
		if ((double)Client->bytes_read/ (double)xtime < dcc_min_rate_in)
			dcc_min_rate_in = (double)Client->bytes_read/(double)xtime;	
	}
	if (Client->bytes_sent)
	{
		send_count_stat++;
		if ((double)Client->bytes_sent/(double)xtime > dcc_max_rate_out)
			dcc_max_rate_out = (double)Client->bytes_sent/(double)xtime;
		if ((double)Client->bytes_sent/(double)xtime < dcc_min_rate_out)
			dcc_min_rate_out = (double)Client->bytes_sent/ (double)xtime;	
	}
}

unsigned char byteordertest(void)
{
	unsigned short test = DCC_PACKETID;

	if (*((unsigned char *)&test) == ((DCC_PACKETID & 0xff00) >> 8))
		return 0;

	if (*((unsigned char *)&test) == (DCC_PACKETID & 0x00ff))
		return 1;
	return 0;
}

/*
 * This stuff doesnt conform to the protocol.
 * Thanks mirc for disregarding the protocol.
 */
#ifdef MIRC_BROKEN_DCC_RESUME
extern  int doing_msg, doing_notice;

/*
 * Usage: /DCC RESUME <nick> [file] [-e passkey]
 */
void	dcc_getfile_resume (char *command, char *args)
{
	char		*user, *nick;
	char		*filename = NULL;
	char		*fullname = NULL;
	char		*tmp = NULL;
	DCC_list	*Client;
	char		*passwd = NULL;
	struct stat	sb;
	char		buf[10];

	if (!(user = next_arg(args, &args)))
	{
		say("You must supply a nickname for DCC RESUME");
		return;
	}

	if (args && *args)
	{
		/* Leeme lone, Yoshi. :P */
		if (args[0] != '-' || args[1] != 'e')
			filename = next_arg(args, &args);

		if (args && args[0] == '-' && args[1] == 'e')
		{
			next_arg(args, &args);
			passwd = next_arg(args, &args);
		}
	}


	while ((nick = next_in_comma_list(user, &user)))
	{
		if (!nick || !*nick)
			break;
		if (!(Client = dcc_searchlist(filename, nick, DCC_FILEREAD, 0, NULL, NULL, 0)))
		{
			if (filename)
				say("No file (%s) offered in SEND mode by %s", filename, nick);
			else
				say("No file offered in SEND mode by %s", nick);
			return;
		}

		if (get_string_var(DCC_DLDIR_VAR))
			malloc_sprintf(&tmp, "%s/%s", get_string_var(DCC_DLDIR_VAR), Client->description);
		else
			malloc_sprintf(&tmp, "%s", Client->description);

		if (!(fullname = expand_twiddle(tmp)))
			malloc_strcpy(&fullname, tmp);
		/*
		 * This has to be done by hand, we cant use send_ctcp,
		 * because this violates the protocol, and send_ctcp checks
		 * for that.  Ugh.
		 */

		if (stat(fullname, &sb) == -1)
		{
			/* File doesnt exist.  Sheesh. */
			say("DCC RESUME: Cannot use DCC RESUME if the file doesnt exist. [%s|%s]", fullname, strerror(errno));
			return;
		}

		if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
		{
			say("A previous DCC GET:%s to %s exists", filename?filename:"<any>", nick);
			return;
		}

		if (passwd)
			Client->encrypt = m_strdup(passwd);
		Client->bytes_sent = 0L;
		Client->transfer_orders.byteoffset = sb.st_size;

		sprintf(buf, "%hd", ntohs(Client->remport));
		malloc_strcpy(&Client->othername, buf);

		malloc_strcpy(&Client->othername, ltoa((long)ntohs(Client->remport)));

		/* Just in case we have to fool the protocol enforcement. */
		doing_privmsg = doing_notice = in_ctcp_flag = 0;
		send_ctcp(CTCP_PRIVMSG, nick, CTCP_DCC, "RESUME %s %d %d", Client->description, ntohs(Client->remport), sb.st_size);
	}
	
	new_free(&tmp);
	new_free(&fullname);
	/* Then we just sit back and wait for the reply. */
}

/*
 * When the peer demands DCC RESUME
 * We send out a DCC ACCEPT
 */
static void dcc_getfile_resume_demanded (char *user, char *filename, char *port, char *offset)
{
	DCC_list	*Client;

	if (!(Client = dcc_searchlist(filename, user, DCC_FILEOFFER, 0, port, NULL, 0)))
		return;		/* Its a fake. */

	if (!offset)
		return;		/* Its a fake */

	Client->transfer_orders.byteoffset = my_atol(offset);
	Client->bytes_read = 0L;

	doing_privmsg = doing_notice = in_ctcp_flag = 0;
	send_ctcp(CTCP_PRIVMSG, user, CTCP_DCC, "ACCEPT %s %s %s", filename, port, offset);

	/* Wait for them to open the connection */
}


/*
 * When we get the DCC ACCEPT
 * We start the connection
 */
static	void	dcc_getfile_resume_start (char *nick, char *filename, char *port, char *offset)
{
	DCC_list	*Client;
	char		*fullname = NULL;
	char		*tmp = NULL;
	
	if (!(Client = dcc_searchlist(filename, nick, DCC_FILEREAD, 0, port, NULL, 0)))
		return;		/* Its fake. */

	Client->flags |= DCC_TWOCLIENTS;
	if (!dcc_open(Client))
		return;

	if (get_string_var(DCC_DLDIR_VAR))
		malloc_sprintf(&tmp, "%s/%s", get_string_var(DCC_DLDIR_VAR), Client->description);
	else
		malloc_sprintf(&tmp, "%s", Client->description);
	if (0 == (fullname = expand_twiddle(tmp)))
		malloc_strcpy(&fullname, tmp);

#ifdef WINNT
	if (!(Client->file = open(fullname, O_WRONLY | O_APPEND | O_BINARY/*, 0644*/)))
#else
	if (!(Client->file = open(fullname, O_WRONLY | O_APPEND, 0644)))
#endif
	{
		put_it("%s", convert_output_format("$G %RDCC%n Unable to open $0: $1-", "%s %s", Client->description, errno?strerror(errno):"Unknown"));
/*		say("Unable to open %s: %s", Client->description, errno ? strerror(errno) : "<No Error>");*/
		Client->flags |= DCC_DELETE;
	}

	new_free(&fullname);
	new_free(&tmp);
}

#endif




void dcc_check_idle _((void))
{
register DCC_list *Client = NULL, *tmpClient;

time_t dcc_idle_time = get_int_var(_CDCC_CLOSE_IDLE_SENDS_TIME_VAR);
static char *last_notify = NULL;
int minidlecheck = get_int_var(_CDCC_MINSPEED_TIME_VAR);

	if (dcc_idle_time)
	{
		int this_idle_time = dcc_idle_time;
		int erase_it;
		for (Client = ClientList; Client;)
		{
			time_t client_idle = time(NULL) - Client->lasttime.tv_sec;
			if (client_idle <= 0 ) client_idle = 1;
			erase_it = 0;
			tmpClient = Client->next;
			switch (Client->flags & DCC_TYPES)
			{
				case DCC_FILEOFFER:
				case DCC_FILEREAD:
				case DCC_RESENDOFFER:
				case DCC_REGETFILE:
					this_idle_time = dcc_idle_time * 3;
					break;
				default:
					this_idle_time = dcc_idle_time;
					break;
			}
			if ((client_idle > this_idle_time) && !(Client->flags&DCC_ACTIVE))
			{
				put_it("%s", convert_output_format("$G %RDCC%n Auto-closing idle dcc $0 to $1", "%s %s", dcc_types[Client->flags&DCC_TYPES], Client->user));
				if (!last_notify || !match(Client->user,last_notify))
				{
					send_to_server("NOTICE %s :Dcc %s Auto Closed", Client->user, dcc_types[Client->flags&DCC_TYPES]);
					malloc_strcpy(&last_notify, Client->user);
				}
				if ((get_to_from(dcc_types[Client->flags & DCC_TYPES]) != -1))
					if (dcc_active_count)
						dcc_active_count--;
				erase_it = 1;
			}
			if (Client->flags&DCC_ACTIVE)
			{
				switch (Client->flags & DCC_TYPES)
				{
					case DCC_FILEOFFER:
					case DCC_RESENDOFFER:
						if (cdcc_minspeed && minidlecheck && ((client_idle % minidlecheck) == 0))
						{
							u_32int_t sent = Client->bytes_sent / 1024;
							double this_speed = 0.0;
							char lame_ultrix1[20];
							char lame_ultrix[20];
							this_speed = (double)((double) sent / (double)client_idle);

							if (this_speed < cdcc_minspeed)
							{
								sprintf(lame_ultrix, "%2.4g", (double)(sent / client_idle));
								sprintf(lame_ultrix1,"%2.4g", (double)cdcc_minspeed);
								put_it("%s", convert_output_format("$G %RDCC%n Auto-closing Slow dcc $0 to $1 require $2KB/s got $3KB/s", "%s %s %s %s", dcc_types[Client->flags&DCC_TYPES], Client->user, lame_ultrix1, lame_ultrix));
								if (!last_notify || !match(Client->user,last_notify))
								{
									send_to_server("NOTICE %s :CDCC Slow dcc %s Auto Closed. Require %sKB/s got %sKB/s", Client->user, dcc_types[Client->flags&DCC_TYPES], lame_ultrix1, lame_ultrix);
									malloc_strcpy(&last_notify, Client->user);
								}
								if (dcc_active_count)
									dcc_active_count--;
								erase_it = 1;
							}
						}
					default:
						break;
				}
			} 
			if (erase_it)
				dcc_erase(Client);
			Client = tmpClient;
		}
	}
	cdcc_timer_offer();
}

BUILT_IN_COMMAND(dcx)
{
char *user = NULL;
int do_chat = 0;
int do_send = 0;
int do_get = 0;
int do_all = 0;
char *nick;
	context;
	do_chat = !my_stricmp(command, "dcx");
	do_send = !my_stricmp(command, "dcs");
	do_get  = !my_stricmp(command, "dcg");
	do_all	= !my_stricmp(command, "dca");

	if (!do_all && !(user = next_arg(args, &args)))
	{
		userage(command, helparg);
		return;
	}

	if  (do_all && !user)
	{
		dcc_close_all();
		dcc_active_count = 0;
		update_all_status(curr_scr_win, NULL, 0);
		return;
	}
	while ((nick = next_in_comma_list(user, &user)))
	{
		if (!nick || !*nick)
			break;
		if (do_send)
		{
			dcc_close_type_nick_all("SEND", nick);
			dcc_close_type_nick_all("RESEND", nick);
		} 
		else if (do_get)
		{
			dcc_close_type_nick_all("GET", nick);
			dcc_close_type_nick_all("REGET", nick);
		} 
		else if (do_chat)
			dcc_close_type_nick_all("CHAT", nick);
	}
	update_all_status(curr_scr_win, NULL, 0);
	return;
}

void open_ftpsend (DCC_list *client, char *args)
{
struct sockaddr_in data_addr = { 0 };
int len = sizeof(struct sockaddr_in);
char tmp[2048];
int on = 1, data = -1, s1 = -1;
char *a, *p;
DCC_list *Client = NULL;
int old_timeout;
char *bufptr;
	if (getsockname(client->read, (struct sockaddr *)&data_addr, &len) < 0)
		return;

	data_addr.sin_port = 0;
	if ((data = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return;
	if ((setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0)
		return;
	if ((bind(data, (struct sockaddr *)&data_addr, sizeof(data_addr))) < 0)
		return;
	len = sizeof(struct sockaddr_in);
	getsockname(data, (struct sockaddr *)&data_addr, &len);

	if ((listen(data, 4)) < 0)
		return;

	a = (char *)&data_addr.sin_addr;
	p = (char *)&data_addr.sin_port;
#define UC(b) (((int)b)&0xff)
	dcc_printf(client->read, "PORT %d,%d,%d,%d,%d,%d\n", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),UC(p[0]), UC(p[1]));
#undef UC
	dcc_printf(client->read, "stor %s\n", args);

	memset(tmp, 0, sizeof(tmp));
	old_timeout = dgets_timeout(2);
	bufptr = tmp;
	while (1)
	{
		if (dgets(bufptr, sizeof(tmp)-1, client->read, NULL) == 0 && dgets_errno > 0)
			goto error_ftpsend;
		if (*bufptr == '5')
			goto error_ftpsend;
		else if (strstr(tmp, "BINARY mode data connection"))
			break;
	}
	len = sizeof(struct sockaddr_in);
	if ((s1 = accept(data, (struct sockaddr *) &data_addr, &len)) < 0)
		return;
	close(data);

	if ((Client = dcc_searchlist("ftpsend", args, DCC_FTPSEND, 1, client->user, NULL, -1)))
	{
		struct stat st;
		Client->read = Client->write = s1;
#ifdef WINNT
		Client->file = open(args, O_RDONLY | O_BINARY);
#else
		Client->file = open(args, O_RDONLY);
#endif
		fstat(Client->file, &st);
		Client->flags |= DCC_ACTIVE;
		FD_SET(Client->write, &writables);
		Client->filesize = st.st_size;
		get_time(&Client->starttime);
	}
	return;
error_ftpsend:
	close(data);
	chop(tmp, 2);
	put_it("%s", convert_output_format("$G %gFTP%n $0-", "%s", tmp));
	return;
}

void open_ftpget(DCC_list *client, char *args)
{
struct sockaddr_in data_addr = { 0 };
int len = sizeof(struct sockaddr_in);
char tmp[2048];
int on = 1, data = -1, s1 = -1;
char *a, *p, *bufptr;
DCC_list *Client = NULL;
int old_timeout;
off_t filesize = 0;
char *filename = NULL;

	if (getsockname(client->read, (struct sockaddr *)&data_addr, &len) < 0)
		return;

	data_addr.sin_port = 0;
	if ((data = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return;
	if ((setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0)
		return;
	if ((bind(data, (struct sockaddr *)&data_addr, sizeof(data_addr))) < 0)
		return;
	len = sizeof(struct sockaddr_in);
	getsockname(data, (struct sockaddr *)&data_addr, &len);

	if ((listen(data, 4)) < 0)
		return;

	a = (char *)&data_addr.sin_addr;
	p = (char *)&data_addr.sin_port;
#define UC(b) (((int)b)&0xff)
	dcc_printf(client->read, "PORT %d,%d,%d,%d,%d,%d\n", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),UC(p[0]), UC(p[1]));
#undef UC

	dcc_printf(client->read, "retr %s\n", args);

	memset(tmp, 0, sizeof(tmp));
	old_timeout = dgets_timeout(2);
	bufptr = tmp;
	while (1)
	{
		if (dgets(bufptr, sizeof(tmp)-1, client->read, NULL) == 0 && dgets_errno > 0)
			goto error_ftp;
		if (*bufptr == '5')
			goto error_ftp;
		else if (strstr(tmp, "BINARY mode data connection"))
		{
			int i = 0;
			char *q = tmp;
			for (i = 0; i < 9; i++)
				p = next_arg(q, &q);
			if (p)
			{
				p++;
				filesize = my_atol(p);
			}
			break;
		}
	}

	len = sizeof(struct sockaddr_in);
	if ((s1 = accept(data, (struct sockaddr *) &data_addr, &len)) < 0)
		return;
	close(data);

#ifdef WINNT
	if ((p = strrchr(args, '/')) || (p = strrchr(args, '\\')))
#else
	if ((p = strrchr(args, '/')))
#endif
		filename = ++p;
	else
		filename = args;

	if ((Client = dcc_searchlist("ftpget", filename, DCC_FTPGET, 1, client->user, NULL, -1)))
	{
		Client->read = Client->write = s1;
#ifdef WINNT
		Client->file = open(filename, O_WRONLY|O_CREAT|O_TRUNC | O_BINARY/*, 0644*/);
#else
		Client->file = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
#endif
		Client->flags |= DCC_ACTIVE;
		FD_SET(Client->read, &readables);
		Client->filesize = filesize;
		get_time(&Client->starttime);
		put_it("%s", convert_output_format("$G %gFTP%n Attempting to get $0", "%s", filename));
	}
	return;
error_ftp:
	close(data);
	chop(tmp, 2);
	put_it("%s", convert_output_format("$G %gFTP%n $0-", "%s", tmp));
	return;
}

int dcc_ftpcommand _((char *host, char *args))
{
DCC_list *Client;
DCC_list *in_ftp;
	Client = dcc_searchlist("ftpopen", host, DCC_FTPOPEN, 0, NULL, NULL, -1);
	if (!(in_ftp = dcc_searchlist("ftpget", NULL, DCC_FTPGET, 0, host, NULL, -1)))
		in_ftp = dcc_searchlist("ftpsend", NULL, DCC_FTPSEND, 0, host, NULL, -1);
	if (Client && !in_ftp)
	{
		char *command = next_arg(args, &args);
		char *t_host;
		if (!my_strnicmp(command, "ls",2) || !my_strnicmp(command, "dir",3))
			dcc_printf(Client->read, "stat %s\n", (args && *args) ? args : ".");
		else if (!my_strnicmp(command, "more", 3))
			dcc_printf(Client->read, "stat %s\n", (args && *args) ? "cwd" : "pwd", (args && *args) ?args:empty_string);
		else if (!my_strnicmp(command, "cd", 2))
			dcc_printf(Client->read, "%s%s%s\n", (args && *args) ? "cwd" : "pwd", (args && *args) ? " ":empty_string, (args && *args) ?args:empty_string);
		else if (!my_strnicmp(command, "get",3) && args && *args)
			open_ftpget(Client, args);
		else if (!my_strnicmp(command, "put",3) && args && *args)
			open_ftpsend(Client, args);
		else
			dcc_printf(Client->read, "%s%s%s\n", command, (args && *args) ? " ":empty_string, (args && *args) ? args:empty_string);
		t_host = m_sprintf("-%s", host);
		addtabkey(t_host, "msg", 0);
		new_free(&t_host);
	}
	else if (in_ftp)
	{
		put_it("%s", convert_output_format("$G %gFTP%n already transfering a file.", NULL, NULL));
		return 0;
	}
	else
	{
		put_it("%s", convert_output_format("$G %gFTP%n is not connected.", NULL, NULL));
		return 0;
	}
	return 1;
}

void dcc_ftpopen(char *command, char *args )
{
DCC_list *Client;
char user[] = "anonymous";
char pass[] = "- bxuser@";
char *host, *u = user, *p = pass;
char *t_host;
int port = 21;

	if (!(host = next_arg(args, &args)))
	{
		put_it ("%s /dcc ftp hostname user passwd [-p port]", convert_output_format("$G %RDCC%n", NULL, NULL));
		return;
	}
	u = user;
	p = pass;
	while (args && *args)
	{
		if (args && *args && *args == '-' && *(args+1) == 'p')	
		{
			char *t;
			t = next_arg(args, &args);
			t = next_arg(args, &args);
			if (t)
				port = my_atol(t);
			break;
		}
		if (!(u = next_arg(args, &args)))
			u = user;
		if (!(p = next_arg(args, &args)))
			p = pass;
	}
	
	Client = dcc_searchlist("ftpopen", host, DCC_FTPOPEN, 1, NULL, NULL, -1);
	if (!Client) return;
	
	if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
	{
		put_it("%s", convert_output_format("$G %GFTP%n A previous DCC FTP to $0 exists", "%s", host));
		return;
	}
	Client->remport = port;
	Client->encrypt = m_strdup(p);
	Client->user = m_strdup(host);
	Client->othername = m_strdup(u);
		
#ifdef WINNT
	if ((Client->read = connect_by_number(host, &Client->remport, SERVICE_CLIENT, PROTOCOL_TCP, 0)) < 0)
#else
	if ((Client->read = connect_by_number(host, &Client->remport, SERVICE_CLIENT, PROTOCOL_TCP, 1)) < 0)
#endif
	{
		Client->flags |= DCC_DELETE;
		put_it("%s", convert_output_format("$G %gFTP%n command failed connect", NULL, NULL));
		return;
	}
	Client->remport = ntohs(Client->remport);
#ifdef DCC_CNCT_PEND
	Client->flags |= DCC_WAIT|DCC_CNCT_PEND;
#else
	Client->flags |= DCC_WAIT;
#endif

	FD_SET(Client->read, &readables);
	t_host = m_sprintf("-%s", host);
	addtabkey(t_host, "msg", 0);
	new_free(&t_host);
}

static void dcc_ftpget (char *command, char *args)
{
char *host; char *file = NULL;
DCC_list *Client;
	if (!(host = next_arg(args, &args)))
	{
		return;
	}
	if (args && *args)
		file = next_arg(args, &args);
		
	Client = dcc_searchlist(file, host, DCC_XMITRECV, 1, NULL, NULL, -1);
	if (Client)
	{
		Client->flags &=DCC_WAIT;
		FD_SET(Client->read, &readables);
	}

}

#ifdef FTP_XMIT
static void dcc_xmitget (char *user, char *description, char *address, char *port)
{
DCC_list *Client;
unsigned long TempLong;
unsigned int TempInt;
unsigned short TempSh;

	Client = dcc_searchlist(description, user, DCC_XMITRECV, 1, NULL, NULL, -1);

	if (Client->flags & DCC_ACTIVE)
	{
		put_it("%s", convert_output_format("$G %RDCC%n Recieved DCC $0 request from $1 while previous session active", "%s %s", "XMIT", user));
		message_from(NULL, LOG_CRAP);
		return;
	}
	Client->flags |= DCC_OFFER;

	TempLong = strtoul(address, NULL, 10);
	Client->remote.s_addr = htonl(TempLong);
	TempSh = TempInt = (unsigned) strtoul(port, NULL, 10);
	Client->remport = htons(TempInt);
#ifdef WINNT
	if ((Client->read = connect_by_number(address, &TempSh, SERVICE_SERVER, PROTOCOL_TCP, 0)) < 0)
#else
	if ((Client->read = connect_by_number(address, &TempSh, SERVICE_SERVER, PROTOCOL_TCP, 1)) < 0)
#endif
	{
		Client->flags |= DCC_DELETE;
		return;
	}	
	FD_SET(Client->read, &readables);
}
#endif

static void dcc_ftpsend (char *command, char *args)
{

	char		*user = NULL;
	char		*filename = NULL, *p;
	struct	in_addr	myip;
	char		*tmp = NULL;
	DCC_list	*Client = NULL;
	unsigned short	portnum;
	char 		*fullname = NULL;
	struct stat st;
	
	if (!(user = next_arg(args, &args)) || !(filename = next_arg(args, &args)))
	{
		say("You must supply a nickname and a filename for DCC XMIT");
		return;
	}

#ifndef WINNT
	if (scanstr("/etc/", filename))
	{
		put_it("%s", convert_output_format("$G %RDCC%n Send request for /etc rejected", NULL, NULL));
		return;
	}
#endif
	fullname = expand_twiddle(filename);
	Client = dcc_searchlist(filename, user, DCC_XMITSEND, 1, fullname, NULL, -1);
	if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
	{
		put_it("%s", convert_output_format("$G %RDCC%n A previous DCC send:$0 to $1 exists", "%s %s", filename, user));
		return;
	}

#ifdef WINNT
	if ((Client->read = connect_by_number(NULL, &portnum, SERVICE_SERVER, PROTOCOL_TCP, 0)) < 0)
#else
	if ((Client->read = connect_by_number(NULL, &portnum, SERVICE_SERVER, PROTOCOL_TCP, 1)) < 0)
#endif
	{
		put_it("%s", convert_output_format("$G %RDCC%n Unable to create connection: $0-", "%s", errno ? strerror(errno) : "Unknown Host"));
		message_from(NULL, LOG_CRAP);
		dcc_erase(Client);
		return;
	}
	Client->remport = htons(portnum);
	FD_SET(Client->read, &readables);
	myip.s_addr = server_list[from_server].local_addr.s_addr;


	if (myip.s_addr == htonl(0x7f000001))
		myip.s_addr = MyHostAddr.s_addr;

#ifdef WINNT
	if ((p = strrchr(filename, '/')) || (p = strrchr(filename, '\\')))
#else		
	if ((p = strrchr(filename, '/')))
#endif
		malloc_strcpy(&tmp, ++p); 
	else
		tmp = m_strdup(filename);
	stat(fullname, &st);
	Client->filesize = st.st_size;
	send_ctcp(CTCP_PRIVMSG, user, CTCP_XMIT, "clear %ld %d %s", 
		htonl(myip.s_addr), Client->remport, tmp);
#ifdef DCC_CNCT_PEND
	Client->flags |= DCC_WAIT|DCC_CNCT_PEND;
#else
	Client->flags |= DCC_WAIT;
#endif
	new_free(&tmp);
}

static void process_incoming_ftp (register DCC_list *client)
{
char tmp[MAX_DCC_BLOCK_SIZE * 4 + 1];
register unsigned int bytesread = 0;
int len = 0;
char *bufptr, *s;
int old_timeout = 0;
	if ((client->flags & DCC_TYPES) == DCC_FTPGET)
	{
		if ((bytesread = read(client->write, &tmp, sizeof(tmp)-1)) > 0)
		{
			client->bytes_read +=bytesread;
			client->packets_transfer++;
			if ((write(client->file, tmp, bytesread)) != bytesread)
			{
				client->flags |= DCC_DELETE;
				client->read = -1;
			}
		}
		else
		{
			DCC_close_filesend(client, "FTPGET");
		}
		return;
	}
	if (client->flags & DCC_WAIT)
	{
		if ((dcc_printf(client->read, "user %s\n", client->othername) < 0) || (dcc_printf(client->read, "pass %s\n", client->encrypt) < 0))
		{
			client->flags |= DCC_DELETE;
			put_it("%s", convert_output_format("$G %gFTP%n command failed write", NULL, NULL));
			return;
		}
		dcc_printf(client->read, "type i\n");
		new_free(&client->encrypt);

		client->flags |= DCC_ACTIVE;
		client->flags &= ~DCC_WAIT;
		return;
	}
	old_timeout = dgets_timeout(1);
        s = client->buffer;
        bufptr = tmp;
        if (s && *s)
        {
                len = strlen(s);
		if (len > MAX_DCC_BLOCK_SIZE - 1)
		{
			put_it("%s", convert_output_format("$G %RFTP buffer overrun. Data lost", NULL, NULL));
			new_free(&(client->buffer));
		} 
		else
		{ 
	                strncpy(tmp, s, len);
			bufptr += len;
		}
        }
	bytesread = dgets(bufptr, dccBlockSize() - len, client->read, NULL);
	(void) dgets_timeout(old_timeout);
	switch (bytesread)
	{
		case -1:
        	        add_to_dcc_buffer(client, tmp);
                	return;
		case 0:
		{
			client->flags |= DCC_DELETE;
			return;
		}
		default:
		{
			char *num;
			char *p, *t;
			new_free(&client->buffer);
			chop(tmp, 2);
			t = p = m_strdup(tmp);
			num = next_arg(p, &p);
			message_from(client->user, LOG_DCC);
			if (isdigit(*num))
				put_it("%s", convert_output_format("%gFTP%n $0-", "%s", p));
			else
				put_it("%s", convert_output_format(tmp, NULL, NULL));
			new_free(&t);
		}
	}
	message_from(NULL, LOG_CRAP);
}

static void process_outgoing_ftp (DCC_list *client)
{
char tmp[MAX_DCC_BLOCK_SIZE * 4 + 1];
register unsigned int bytesread = 0;

	if ((bytesread = read(client->file, &tmp, sizeof(tmp)-1)) > 0)
	{
		client->bytes_sent +=bytesread;
		client->packets_transfer++;
		if ((send(client->write, tmp, bytesread, 0)) != bytesread)
			client->flags |= DCC_DELETE;
	}
	else
	{
		DCC_close_filesend(client, "FTPSEND");
	}
	return;
}

int check_dcc_list (char *name)
{
register DCC_list *Client;
int do_it = 0;
	for (Client = ClientList; Client; Client = Client->next)
	{
		if (Client->user && !my_stricmp(name, Client->user) && !(Client->flags & DCC_ACTIVE))
		{
			Client->flags |= DCC_DELETE;
			do_it++;
			if (get_to_from(dcc_types[Client->flags&DCC_TYPES]) != -1 && dcc_active_count)
				dcc_active_count--;
		}
	}
	return do_it;
}

void dcc_nick (char *command, char *args)
{
int remove;
List *nptr = NULL;
char *nick;
	if (!args || !*args)
	{
		int count = 0;
		for (nptr = next_namelist(dcc_no_flood, NULL, DCC_HASHSIZE); nptr; nptr = next_namelist(dcc_no_flood, nptr, DCC_HASHSIZE))
		{
			if (count == 0)
				put_it("%s", convert_output_format("$G %RDCC%n autoget list/no flood list", NULL, NULL));
			put_it("%s", nptr->name);
			count++;
		}
		if (count == 0)
			userage("/dcc exempt", "+nick to add, nick to remove"); 
		return;
	}
	nick = next_arg(args, &args);
	while (nick && *nick)
	{
		remove = 1;
		if (*nick == '+')
		{
			remove = 0;
			nick++;
		}
		nptr = find_name_in_genericlist(nick, dcc_no_flood, DCC_HASHSIZE, remove);
		if (remove && nptr)
		{
			new_free(&nptr->name);
			new_free((char **)&nptr);
		}
		else if (!remove && !nptr)
			add_name_to_genericlist(nick, dcc_no_flood, DCC_HASHSIZE);
		else if (remove && !nptr)
			put_it("%s", convert_output_format("$G: %RDCC%n No such nick on the exempt list %K[%W$0%K]", "%s", nick));
		nick = next_arg(args, &args);
	}
}

int dcc_exempt_save(FILE *fptr)
{
int count = 0;
List *nptr = NULL;
	if (dcc_no_flood)
	{
		fprintf(fptr, "# Dcc Exempt from autoget OFF list\n");
		fprintf(fptr, "DCC EXEMPT ");
	}
	for (nptr = next_namelist(dcc_no_flood, NULL, DCC_HASHSIZE); nptr; nptr = next_namelist(dcc_no_flood, nptr, DCC_HASHSIZE))
	{
		fprintf(fptr, "+%s ", nptr->name);
		count++;
	}
	if (dcc_no_flood)
	{
		fprintf(fptr, "\n");
		if (count && do_hook(SAVEFILE_LIST, "DCCexempt %d", count))
			bitchsay("Saved %d DccExempt entries", count);
		                        
	}
	return count;
}
