#include "uirc.h"


/* status */
#define WAITACCEPT				1
#define ACCEPTED				2
#define REQUEST					3
#define WAITACCEPTOTHERCLIENT	4
	
/* direction */
#define RECEIVE					1
#define SEND					2

struct dcc_connection
	{
	unsigned long address;/* network byte order */
	unsigned port;/* network byte order */
	char *nick;
	char *type;
	int status;
	int dcc_socketfd;

	char *filename;
	char *pathfilename;
	FILE *dccfile;
	int direction;
	unsigned long filesize;
	unsigned long bytes_send;
	unsigned long bytes_read;
	float percentage;
	long packets_send;
	long packets_read;
	char *checksum;
	unsigned char file_checksum[6];
	struct dcc_connection *nxtentr;/*next entry to table*/
	};
static struct dcc_connection *pnttab[1];/*pointer table*/


struct dcc_connection *lookup_dcc_nick(char *nick, char *type, char *filename)
{
struct dcc_connection *pa;
/*pa points to next entry*/
for(pa = pnttab[0]; pa != 0; pa = pa -> nxtentr)
	{
	if( (strcmp(pa -> nick, nick) == 0) && (strcmp(pa -> type, type) == 0) )
		{
		if(strcmp(type, "send") == 0)/* dcc send */
			{
			/* in dcc send the filename must also match, this makes it possible
			/* to have multiple file transfers with one nick at the same time,
			/* so one chat connection and unlimited file transfers per nick */
			if(strcmp(pa -> filename, filename) == 0)return(pa);/* found it */
			}
		else return(pa);/* dcc chat found it */
		}
	}
return(0);/*not found*/
}


struct dcc_connection *install_dcc_nick(\
char *nick, char *type, char *filename, char *pathfilename)
{
struct dcc_connection *pa, *lookup_dcc_nick();
char *strsave();
char *malloc();

pa = lookup_dcc_nick(nick, type, filename);

if(!pa)	/* not found */
	{
	pa = (struct dcc_connection *)malloc(sizeof(*pa));
	if(!pa)return(0);
	pa -> nick = strsave(nick);
	if(! pa -> nick) return(0);
	pa -> type = strsave(type);
	if(! pa -> type) return(0);
	pa -> filename = strsave(filename);
	if(! pa -> filename)return(0);
	pa -> pathfilename = strsave(pathfilename);
	if(! pa -> pathfilename)return(0);
	pa -> nxtentr = pnttab[0];
	pnttab[0] = pa;
	return(pa);
	}
return(0);
}


struct dcc_connection *lookup_dcc_socketfd(int dcc_socketfd)
{
struct dcc_connection *pa;
/*pa points to next entry*/
for(pa = pnttab[0]; pa != 0; pa = pa -> nxtentr)
	{
	if(pa -> dcc_socketfd == dcc_socketfd) return(pa);/* found it */
	}
return(0);/*not found*/
}


int delete_dcc_nick(char *nick)
{
struct dcc_connection *pa, *pprev;/* pa points to next entry */
int i;
char temp[80];

pa = pnttab[0];
i = 0;
if(! pa)return(0);/* no entry */
while(1)/* for all structures */
	{
	if(strcmp(pa -> nick, nick) == 0)
		{
		if(i == 0)/* first entry */
			{
			pnttab[0] = pa -> nxtentr;
			}
		else/* not first one */
			{
			pprev -> nxtentr = pa -> nxtentr;
			}
		free(pa -> nick);/* free name */
		free(pa);/* free structure */
		return(1);
		}/* end found */
	pprev = pa;/* save this pointer */
	pa = pa -> nxtentr;
	if(! pa)return(0);/* end of chain of structures */
	i++;
	}/* end while all structures */ 
return(0);/* not found */
}/* end function delete_dcc_nick */


int delete_dcc_socketfd(int dcc_socketfd)/* delete entry from table */
{
struct dcc_connection *pa, *pprev;/* pa points to next entry */
int i;

/* we want the previous one (if there is a previous one), 
/* to point past this one, to the next one (if there is a next one) .
/* if this is the only one, the entry in pnttab should be removed. 
/* if there is no previous one, pa should point to the next one.
/* if there is no next one, the pa should be set to zero.
*/

pa = pnttab[0];
i = 0;
if(! pa)return(0);/* no entry */
while(1)/* for all structures */
	{
	if(pa -> dcc_socketfd == dcc_socketfd)/* found it */
		{
		if(i == 0)/* first entry */
			{
			pnttab[0] = pa -> nxtentr;
									/* pnttab entry points to next one,
									/* this could be 0 */
			}
		else/* not first one */
			{
			pprev -> nxtentr = pa -> nxtentr;
									/* the previous one points to the next one,
									/* this could be 0 */ 
			}
		free(pa -> nick);/* free name */
		free(pa);/* free structure */
/*		to_screen("STUCTURE FREED", "");*/
		return(1);
		}/* end found */
	pprev = pa;/* save this pointer */
	pa = pa -> nxtentr;
	if(! pa)return(0);/* end of chain of structures */
	i++;
	}/* end while all structures */ 
return(0);/* not found */
}/* end function delete_entry */


int dcc_process(\
char *type, char *nick, char *mode,\
char *filename, char *new_filename, int socketfd)
{
if( (strcmp(type, "send") != 0) && (strcmp(type, "chat") != 0) )
	{
	to_screen("Invalid type, use chat | send", "");
	return(1);
	}
if(strcmp(mode, "open") == 0)
	{
	open_dcc_connection(type, nick, filename, socketfd);
	}
else if(strcmp(mode, "accept") == 0)
	{
	accept_dcc_connection(type, nick, filename, new_filename);
	}
else if(strcmp(mode, "close") == 0)
	{
	close_dcc_connection(type, nick, filename, socketfd);
	}
else
	{
	to_screen("Invalid mode, use open | accept | close", "");
	}
return(1);
}


int open_dcc_connection(\
char *type, char *nick, char *pathfilename, int socketfd)
{
struct dcc_connection *pa;
struct sockaddr_in dcc_name;
int dcc_socketfd;
char filename[512];
unsigned long filesize;
char temp[80];
char temp1[80];
unsigned long address;
unsigned long address_according_to_server;
unsigned port;
extern fd_set readfs;
extern struct sockaddr_in sa;
int length;
extern struct in_addr local_ip_address;/* network byte order */
extern struct in_addr local_ip_address_according_to_server;/* n. b. o. */
extern char ircname[];
char *strsave();
char utype[40];/* type converted to upper case */
int i, j;
char checksum_string[20];
unsigned char file_checksum[6];
FILE *sendfile;
int a, c;
int flag;
int position;

struct sockaddr_un name;
int sock;
size_t size;
                   
port = 0;                   

/* strip the filename from the pathfilename */
if(strstr(pathfilename, "/") == 0)
	{	
	strcpy(filename, pathfilename);/* not a sign of a path */
	}
else
	{
	if(pathfilename[0] != '/')
		{
		sprintf(temp, "Illegal filename %s", pathfilename);
		to_screen(temp, nick);
		return(1);
		}
	/* find last '/' in pathfilename */
	for(i = 0; i < strlen(pathfilename); i++)
		{
		c = pathfilename[i];
		if(c == '/') position = i;
		}
	/* if the first or last character is '/' its a directory */
	if( (position == 0) || (position == strlen(pathfilename) - 1 ) )
		{
		sprintf(temp, "%s is not a filename, but a directory", pathfilename);
		to_screen(temp, nick);
		return(1);
		}
	/* copy the filename part of pathfilename to filename */
	/* point to first character filename */
	position++;
	j = 0;
	for(i = position; i < strlen(pathfilename); i++)
		{
		filename[j] = pathfilename[i];
		j++;
		}	
	filename[j] = 0;/* string termination */	
	sprintf(temp, "pathfilename=%s filename=%s", pathfilename, filename);
	to_screen(temp, nick);
	}/* end a path is present */

/* prevent multiple chat connections to the same nick,
/* or multiple filetransferrs with the same filename */
if( lookup_dcc_nick(nick, type, filename) )
	{
	sprintf(temp, "A dcc %s connection with %s already exist", type, nick);
	to_screen(temp, nick);
	return(0);
	}

/* Create a socket, bind it to INADDR_ANY, port 0,
*/

/* Create a socket. */
dcc_socketfd = socket(PF_INET, SOCK_STREAM, 0);
if (dcc_socketfd < 0)
	{
	to_screen("socket: failed, error", nick);
	if(errno == EPROTONOSUPPORT) to_screen(\
	"The PROTOCOL or STYLE is not supported by the NAMESPACE specified.", nick);
	if(errno == EMFILE) to_screen(\
	"The process already has too many file descriptors open.", nick);
	if(errno == ENFILE) to_screen(\
	"The system already has too many file descriptors open.", nick);
/* EACCESS unkown define */
/*	if(errno == EACCESS)to_screen(\
/*	"The process does not have privilege to create a socket of the specified",\
	nick);
/*	to_screen("STYLE or PROTOCOL.", nick);
*/
	if(errno == ENOBUFS) to_screen(\
	"The system ran out of internal buffer space.", nick);
	return(0);
	}

/* Give the socket a name. */
bzero((char *) &dcc_name, sizeof(dcc_name));
dcc_name.sin_family = AF_INET;
/* convert port and address to network byte order */
dcc_name.sin_port = htons(port);/* and port is 0 */
dcc_name.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind (dcc_socketfd, (struct sockaddr *) &dcc_name, sizeof (dcc_name)) < 0)
	{
	to_screen("make_socket: bind returned < 0", nick);
	if(errno == EBADF) to_screen(\
	"The SOCKET argument is not a valid file descriptor.", nick);
	if(errno == ENOTSOCK) to_screen(\
	"The descriptor SOCKET is not a socket.", nick);
	if(errno == EADDRNOTAVAIL) to_screen(\
	"The specified address is not available on this machine.", nick);
	if(errno == EADDRINUSE)	to_screen(\
	"Some other socket is already using the specified address.", nick);
	if(errno == EINVAL) to_screen(\
	"The socket SOCKET already has an address.", nick);
	if(errno == EACCES)
		{
		to_screen(\
		"You do not have permission to access the requested address.", nick);
		to_screen(\
		"(In the Internet domain, only the super-user is allowed to specify",\
		nick);
		to_screen(\
		"a port number in the range 0 through PPORT_RESERVED minus one)",\
		nick);
    	}                                                                                                              	
	return(0);
	}

/* make it a listening socket */
if (listen (dcc_socketfd, 1) < 0)
	{
	to_screen("open_dcc_connection: listen: failed, error", nick);
	return(0);
	}

/* get port */
length = sizeof(dcc_name);
if(getsockname(dcc_socketfd, (struct sockaddr *) &dcc_name, &length) )
	{
	to_screen("open_dcc_connection: getsockname: returned non zero", nick);
	return(0);
	}
port = dcc_name.sin_port;

/* get ip address.
/* the first one is from the server socket after connect,
/* the second one is the result of a 'whois me'.
*/

/* default if no setup entry for get_IP_address */
address = local_ip_address.s_addr;

/* all these addresses are in network byte order */

/* print own (dynamic) ip address) */
/*
sprintf(temp, "Local IP address taken from server socket after connect is %s",\
(char *)inet_ntoa(local_ip_address.s_addr) );
to_screen(temp, "");

sprintf(temp, "Local IP address according to server after a whois %s is %s",\
ircname,  (char *)inet_ntoa(local_ip_address_according_to_server.s_addr) );
to_screen(temp, "");
*/

if( get_setup("get_IP_address_from", temp) )
	{
	sscanf(temp, "%s", temp1);
	if(strcmp(temp1, "socket") == 0)
		{
		address = local_ip_address.s_addr;
		}
	else if(strcmp(temp1, "server") == 0)
		{
		address = local_ip_address_according_to_server.s_addr;
		}
	else if(strcmp(temp1, "auto") == 0)
		{
		/* check if the addresses match */
		if(local_ip_address.s_addr !=\
		local_ip_address_according_to_server.s_addr)
			{
			/* report */
			to_screen(\
			"IP addresses differ: Using IP address obtained from server by\
			 whois", nick);
			sprintf(temp, "socket=%ld whois=%ld",\
			ntohl(local_ip_address.s_addr),\
			ntohl(local_ip_address_according_to_server.s_addr) );
			to_screen(temp, "");

			/* use server address */
			address = local_ip_address_according_to_server.s_addr;
			}
		}	
	}/* end if setup file entry for get_IP_address_from */

/* send the data to the nick via ctcp */
/* convert type to upper case */
i = 0;
while(1)
	{
	utype[i] = toupper(type[i]);
	if(utype[i] == 0)break;
	i++;
	}

/* :ircii!root@panteltje.255.255 PRIVMSG grep :DCC CHAT chat 2130706433 1248 */
/* :ircii!root@panteltje.255.255 PRIVMSG grep :DCC SEND seeflop
/* 3266534217 1087 64 1c302f5f */

if(strcmp(utype, "CHAT") == 0)
	{
	/* address and port are in network byte order */
	sprintf(temp, "PRIVMSG %s :\1DCC CHAT chat %lu %u\1\n",\
	nick, ntohl(address), ntohs(port) );
	}
if(strcmp(utype, "SEND") == 0)
	{
	/* open the file */
	sendfile = fopen(pathfilename, "rb");
	if(! sendfile)
		{
		sprintf(temp, "Cannot open file %s for DCC SEND", pathfilename);
		to_screen(temp, nick);
		return(0); 
		}	
	/* read the file to get length and checksum */	
	file_checksum[0] = 0;
	file_checksum[1] = 0;
	file_checksum[2] = 0;
	file_checksum[3] = 0;
	j = 0;
	filesize = 0;
	while(1)
		{
		c = getc(sendfile);
		while(1)
			{
			if(ferror(sendfile) ) 
				{
				perror("reading sendfile for checksum");
				continue;
				}
			break;
			}
/*		a = read( fileno(sendfile), &c, 1);
/*		if(a == 0)/* end of file or error */

		if(feof(sendfile) )
			{
			fclose(sendfile);
			break;
			}
/*		if(a < 0)
			{
			sprintf(temp, "a=%d errno=%d", a, errno);
			to_screen(temp, "");
			if(errno == EBADF) to_screen("Not a valid file descriptor", "");
			if(errno == EFBIG) to_screen("File to big", "");
			if(errno == EAGAIN) to_screen("O_NONBLOCK flag set", "");
			if(errno == EINTR)\
			to_screen("Was waiting, but interrupted by signal", "");
			if(errno == EIO) to_screen("Hardware error", "");
			if(errno == ENOSPC) to_screen("no space", "");
			if(errno == EPIPE)\
			to_screen("Writing to pipe or fifo not open for read","");
			to_screen("RETRY", "");
			continue;
			}
*/
		/* calculate checksum */
		file_checksum[j] ^= c;
		j++;
		j %= 4;
		filesize++;
		}
	sprintf(checksum_string, "%x%x%x%x",\
	file_checksum[0], file_checksum[1],\
	file_checksum[2], file_checksum[3]);
	if(filesize == 0)
		{
		to_screen("Filesize reads zero, possible error reading file", nick);
		to_screen("dcc send command cancelled", nick);
		return(1);
		}
                        
	sprintf(temp, "File %s size %lu checksum %s",\
	pathfilename, filesize, checksum_string);
	to_screen(temp, nick);

	sprintf(temp, "PRIVMSG %s :\1DCC SEND %s %lu %u %lu %s\1\n",\
	nick, filename, ntohl(address), ntohs(port), filesize, checksum_string);
	}/* end type SEND */

if(!send_to_server(socketfd, temp) )return(0);/* the handshake */

sprintf(temp,\
"Waiting for accept from %s IP address %s on port %d",\
nick, inet_ntoa(address), ntohs(port) );
to_screen(temp, nick);

pa = install_dcc_nick(nick, type, filename, pathfilename);
if(pa == 0)
	{
	to_screen("open_dcc_connection: install_dcc_nick: failed", nick);
	return(0);
	}
pa -> address = address;
pa -> port = port;
pa -> status = WAITACCEPTOTHERCLIENT;
pa -> dcc_socketfd = dcc_socketfd;
pa -> direction = SEND;/* only used in dcc send, sending file */
pa -> bytes_send = 0;/* used as a flag, if 0 causes fopen in dcc_send_send() */
pa -> filesize = filesize;
pa -> dccfile = sendfile;
pa -> checksum = strsave(checksum_string);
if(pa -> checksum == 0)
	{
	to_screen("open_dcc_connection: cannot store pa -> checksum", nick);
	return(0);
	}
/*
pa -> pathfilename = strsave(pathfilename);
if(pa -> pathfilename == 0)
	{
	to_screen("open_dcc_connection: cannot store pa -> pathfilename", nick);
	return(0);
	}
*/
pa -> percentage = 0;
return(1);
}/* end function open_dcc_conection */


int close_dcc_connection(char *type, char *nick, char *filename, int socketfd)
{
struct dcc_connection *pa;
int dcc_socketfd;
char temp[80];
extern fd_set readfs;
char txbuf[512];
extern char mynickuserhost[];

pa = lookup_dcc_nick(nick, type, filename);

if(pa == 0)
	{
	if(strcmp(type, "chat") == 0)
		{
		sprintf(temp, "No open %s connection with %s exists",\
		type, nick);
		}
	if(strcmp(type, "send") == 0)
		{
		sprintf(temp, "No open %s filetransfer for file %s with %s exists",\
		type, filename, nick);
		}	
	to_screen(temp, nick);
	return(0);
	}
	
/* check if the type is correct */
/* get dcc_socketfd */
dcc_socketfd = pa -> dcc_socketfd;

if(dcc_socketfd)
	{
	/* close the socket */
	close(dcc_socketfd);
	}

/* report and send abort command to other client */
/* rxbuf=:ircii!root@panteltje.255.255 NOTICE grep :DCC REJECT GET tests.jpg */
if(strcmp(type, "chat") == 0)
	{
	sprintf(txbuf, ":%s NOTICE %s :\1DCC REJECT chat <any>\1\n",\
	mynickuserhost, nick);
	sprintf(temp, "DCC %s connection with %s closed", type, pa -> nick);
	}
else if(strcmp(type, "send") == 0)
	{
	if( (pa -> status == WAITACCEPT)||\
	( (pa -> status == ACCEPTED) && (pa -> direction == SEND) ) )
		{
		sprintf(txbuf, ":%s NOTICE %s :\1DCC REJECT SEND %s\1\n",\
		mynickuserhost, nick, filename);
		}
	else if( (pa -> status == WAITACCEPTOTHERCLIENT) ||\
	( (pa -> status == ACCEPTED) && (pa -> direction == RECEIVE) ) )
	
		{
		sprintf(txbuf, ":%s NOTICE %s :\1DCC REJECT GET %s\1\n",\
		mynickuserhost, nick, filename);
		}
	sprintf(temp, "DCC %s connection for file %s with %s closed",\
	type, pa -> filename, pa -> nick);
	}
else
	{
	to_screen(\
	"Invalid type, use /dcc chat close or /dcc send close filename",\
	pa -> nick);
	return(1);
	}
send_to_server(socketfd, txbuf);
to_screen(temp, nick);

/* delete entry for this socketfd */
delete_dcc_socketfd(dcc_socketfd);
		
/* this macro removes dcc_socketfd from the filedescriptor set 
/* readfs */
FD_CLR(dcc_socketfd, &readfs);
return(1);
}


int handle_dcc_request(char *from, char *message, int socketfd)
{
struct dcc_connection *pa;
int a, i;
char type[512], filename_or_chat[512];
unsigned long address;
unsigned int port;
unsigned long filesize;
char checksum[40];
int args;
char temp[512];
char temp1[80];
char *strsave();
char ltype[40];
extern int get_nicks_ip_address_flag;
char txbuf[512];
extern char ircname[];
extern fd_set readfs;

/* this is what rxbuf looks like:
/* :ircii!root@panteltje.255.255 PRIVMSG grep :DCC CHAT chat 2130706433 1248 */
/* :ircii!root@panteltje.255.255 PRIVMSG grep :DCC SEND seeflop
/* 3266534217 1087 64 1c302f5f */

/*
sprintf(temp, "handle_dcc_request: message=%s", message);
to_screen(temp, from);
*/

/* Note: Some clients do not seem to send the checksum field,
/* this is checked later for zero lenght in dcc_send_send_file().
*/
checksum[0] = 0;/* mark empty, in case no checksum field is present */

/* parse the message  (it may contain zeros, but need arguments for DCC) */
args = sscanf(message, "%s %s %s %u %lu %s",\
type, filename_or_chat, temp1, &port, &filesize, checksum);
sscanf(temp1, "%lu", &address);

/*
sprintf(temp,\
"args=%d type=%s filename_or_chat=%s temp1=%s address=%lu (%s) port=%u\
filesize=%lu checksum=%s",\
args, type, filename_or_chat, temp1, address, inet_ntoa(htonl(address) ), port,\
filesize, checksum);
to_screen(temp, from);
*/

/* 
if(args < 6) to_screen("handle_dcc_request: WARNING: not enough arguments",\
from);
if(strlen(checksum) == 0)
	{
	to_screen("handle_dcc_request: WARNING parsed checksum length zero", from);
	}
*/

/* convert type to lowercase */
i = 0;
while(1)
	{
	ltype[i] = tolower(type[i]);
	if(type[i] == 0)break;
	i++;
	}

/* handle any rejects */
if(strcmp(ltype, "reject") == 0)
	{
	if(strcmp(filename_or_chat, "chat") == 0)
		{
		close_dcc_connection("chat", from, temp1, socketfd);
 		sprintf(temp, "Connection closed by %s", from);
		to_screen(temp, "");
		/* the other side will close the socket, that will end the connection
		/* end display a message */
		}
	else /* GET or SEND */
		{
		close_dcc_connection("send", from, temp1, socketfd);
 		sprintf(temp, "Connection closed by %s", from);
		to_screen(temp, "");
		}
	return(1);
	}/* end rejects */	

pa = install_dcc_nick(from, ltype, filename_or_chat, "");
if(pa == 0)
	{
	to_screen("handle_dcc_request: cannot install nick", from);
	return(0);
	}
pa -> address = htonl(address);
pa -> port = htons(port);
pa -> status = WAITACCEPT;
pa -> direction = RECEIVE;/* only used in dcc send, receiving file */

/* security */
if( get_setup("DCC_IP_security_check", temp, 0) )
	{
	sscanf(temp, "%s", temp1);
	if(strcmp(temp1, "yes") == 0)
		{
		/* ask the server for nicks ip address, want to know if that is the same
		/* as the one in the handshake.		
		/* do a whois */
		sprintf(txbuf, "WHOIS %s\n", from);
		if(! send_to_server(socketfd, txbuf) )return(1);

		/* accept_dcc_connection will wait for this flag to be reset */
		get_nicks_ip_address_flag = 1;
		}/* end if security is yes */
	}/* end if security entry in setup file */
if(strcmp(ltype, "chat") == 0)
	{
	sprintf(temp, "DCC %s request from %s IP %s port %u",\
	type, from, inet_ntoa(htonl(address)), port); 
	to_screen(temp, from);
	}
if(strcmp(ltype, "send") == 0)
	{
	sprintf(temp,\
	"DCC %s request from %s IP %s port %u for:\
	\nfile %s length %lu checksum %s",\
	type, from, inet_ntoa(htonl(address)), port,\
	filename_or_chat, filesize, checksum); 
	pa -> filesize = filesize;
	to_screen(temp, from);
/*
sprintf(temp, "HANDLE DCC REQUEST setting pa -> checksum to %s", checksum);
to_screen(temp, from);
*/
	pa -> checksum = strsave(checksum);
	if(pa -> checksum == 0)
		{
		to_screen("handle_dcc_request: cannot install checksum", from);
		return(0);
		}
	pa -> file_checksum[0] = 0;
	pa -> file_checksum[1] = 0;
	pa -> file_checksum[2] = 0;
	pa -> file_checksum[3] = 0;
	
	pa -> direction = RECEIVE;
	}
return(1);
}/* end function handle_dcc_request */


int accept_dcc_connection(\
char *type, char *from, char *filename, char *new_filename)
{ 
struct dcc_connection *pa;
struct sockaddr_in dcc_name;
int dcc_socketfd;
char temp[512];
char temp1[80];
extern fd_set readfs;
int a;
FILE *dccfile;
extern struct in_addr ip_address_according_to_server;/* network byte order */
extern int get_nicks_ip_address_flag;
extern int security_flag;

pa = lookup_dcc_nick(from, type, filename);
if(!pa)
	{
	if(strcmp(type, "chat") == 0)
		{
		to_screen("accept_dcc_connection: no such nick", "");
		}
	if(strcmp(type, "send") == 0)
		{
		to_screen("accept_dcc_connection: no such nick filename", "");
		}	
	return(0);/* no such nick */
	}

if(pa -> status != WAITACCEPT )
	{
	to_screen("accept_dcc_connection: no WAITACCEPT", from);
	return(0);/* already accepted? */
	}

/* security */
if(security_flag)/* could be reset by /security off */
	{
	/* test if result of whois valid */
	if(get_nicks_ip_address_flag)
		{
		sprintf(temp,\
		"Waiting for %s IP address via a whois for security verfication,\
		\ntry again later, or do /security off, and repeat accept command");
		to_screen(temp, "");
		return(0);/* no security check possible yet */
		}
	/* check if IP address are the same */
	/* test if setup entry is present, else no check */
	if(get_setup("DCC_IP_security_check", temp, 0) )	
		{
		sscanf(temp, "%s", temp1);
		if(strcmp(temp1, "yes") == 0)
			{
			/* both addresses are in network byte order */
			if(ip_address_according_to_server.s_addr != pa -> address)
				{
				sprintf(temp,\
				"SECURITY CHECK FAILS: handshake IP %s whois IP %s",
				inet_ntoa(pa -> address),
				inet_ntoa(ip_address_according_to_server.s_addr) );
				to_screen(temp,"");
				return(0);
				}	
			}
		}
	}/* end if security flag */
/* enable security again if it was disabled, for the next time */
security_flag = 1;

if(strcmp(pa -> type, "send") == 0)
	{
	if(strlen(new_filename) != 0)/* new filename specified */
		{
		/* if dcc send nick accept filename samefilename, overwrite */
		if(strcmp(filename, new_filename) != 0)
			{
			dccfile = fopen(new_filename, "r");
			if(dccfile)
				{
				sprintf(temp,\
			"Warning file %s exists! use: dcc send accept %s %s newfilename",\
				new_filename, from, new_filename);
				to_screen(temp, from);
				fclose(dccfile);
				return(0);
				}/* end new filename exists */
			}/* end not same filename */
		strcpy(pa -> filename, new_filename);
		}
	else/* no new filename specified */
		{
		dccfile = fopen(pa -> filename, "r");
		if(dccfile)
			{
			sprintf(temp,\
			"Warning file %s exists! use: dcc send accept %s %s newfilename",\
			pa -> filename, from, pa -> filename);
			to_screen(temp, from);
			fclose(dccfile);
			return(0);
			}
		}
	}
/* Create a socket. */
dcc_socketfd = socket(PF_INET, SOCK_STREAM, 0);
if (dcc_socketfd < 0)
	{
	to_screen("socket: failed, error", from);
	if(errno == EPROTONOSUPPORT) to_screen(\
	"The PROTOCOL or STYLE is not supported by the NAMESPACE specified.",\
	from);
	if(errno == EMFILE) to_screen(\
	"The process already has too many file descriptors open.", from);
	if(errno == ENFILE) to_screen(\
	"The system already has too many file descriptors open.", from);
/* EACCESS unkown define */
/*	if(errno == EACCESS)to_screen(\
/*	"The process does not have privilege to create a socket of the specified",
	from);
/*	to_screen("STYLE or PROTOCOL.", from);
*/
	if(errno == ENOBUFS) to_screen(\
	"The system ran out of internal buffer space.", from);
	return(0);
	}

/*
sprintf(temp, "accept_dcc_connection: socket created dcc_socketfd=%d",\
dcc_socketfd);
to_screen(temp, from);
*/

/* Give the socket a name. */
bzero((char *) &dcc_name, sizeof(dcc_name));
dcc_name.sin_family = AF_INET;
dcc_name.sin_port = pa -> port; /*htons (pa -> port);*/
dcc_name.sin_addr.s_addr = pa -> address; /*htonl(pa -> address);*/

/* connect */
a = connect(dcc_socketfd, (struct sockaddr*)&dcc_name, sizeof(dcc_name) );
if(a < 0)
	{
	close(dcc_socketfd);
	to_screen("connect: failed, error:", from);
	if(errno == EBADF) to_screen("not a valid file descriptor", from);
	if(errno == ENOTSOCK) to_screen("not a socket", from);
	if(errno == EADDRNOTAVAIL) to_screen("addres not available on remote",\
	from);
	if(errno == EAFNOSUPPORT) to_screen(\
	"namespace not supported by this socket", from);
	if(errno == EISCONN) to_screen("socket is already connected", from);
	if(errno == ETIMEDOUT) to_screen("attempt to connect timed out", from);
	if(errno == ECONNREFUSED) to_screen("connection actively refused", from);
	if(errno == ENETUNREACH) to_screen("network unreacheble", from);
	if(errno == EADDRINUSE) to_screen(\
	"socket address of given address already in use", from);
	if(errno == EINPROGRESS) to_screen(\
"socket is non blocking and connection could not be established immediatly",\
	from);
	if(errno == EALREADY) to_screen(\
	"socket is non blocking and already has a connection in progress", from);
	return(0);
	}

sprintf(temp, "DCC %s connection with %s ip %s port %d established",
type, from, inet_ntoa(dcc_name.sin_addr), ntohs(dcc_name.sin_port) );
to_screen(temp, from);

if(strcmp(pa -> type, "send") == 0)
	{
	pa -> dccfile = fopen(pa -> filename, "wb");
	if(! pa -> dccfile)
		{
		sprintf(temp, "Cannot create file %s", pa -> filename);
		to_screen(temp, from);
		return(1);/* not accepted, maybe try some other filename */
		}
	}

pa -> dcc_socketfd = dcc_socketfd;
pa -> status = ACCEPTED;
pa -> direction = RECEIVE;
pa -> bytes_read = 0;
pa -> percentage = 0;

return(1);
}/* end function accept_dcc_connection */


int dcc_chat_send(char *nick, char *txbuf)
{
struct dcc_connection *pa;
int a;
int dcc_socketfd;
char temp[512];

/*
sprintf(temp, "Looking for nick=%s", nick);
to_screen(temp, "");
*/

pa = lookup_dcc_nick(nick, "chat", "");/* last argument filename dummy */

if(pa == 0)
	{
	sprintf(temp, "No dcc chat connection to %s exists", nick);
	to_screen(temp, "");
	return(0);
	}
if(pa -> status != ACCEPTED)
	{
	to_screen("Accept dcc chat connection first", ""); 
	return(0);
	}
	
dcc_socketfd = pa -> dcc_socketfd;

a = write(dcc_socketfd, txbuf, strlen(txbuf) );     
if(a < 0)
	{
	to_screen("\nwrite: failed, error:", nick);
	if(errno == EBADF) to_screen("Not a valid file descriptor", nick);
	if(errno == EFBIG) to_screen("File to big", nick);
	if(errno == EAGAIN) to_screen("O_NONBLOCK flag set", nick);
	if(errno == EINTR) to_screen("Was waiting, but interrupted by signal",\
	nick);
	if(errno == EIO) to_screen("Hardware error", nick);
	if(errno == ENOSPC) to_screen("no space", nick);
	if(errno == EPIPE) to_screen("Writing to pipe or fifo not open for read",\
	nick);
	return(0);
	}
trace("txbuf", txbuf);
return(1);/* ok */
}


int set_all_dcc_filedescriptors()
{
struct dcc_connection *pa;
int i;
int dcc_socketfd;
extern fd_set readfs;
char temp[40];

for(pa = pnttab[0]; pa != 0; pa = pa -> nxtentr)
	{
	if(pa -> status == ACCEPTED)
		{
		if(strcmp(pa -> type, "chat") == 0)
			{	
			/* this macro adds dcc_socketfd to the file descriptor
			/* readfs */
			FD_SET(pa -> dcc_socketfd, &readfs);
			}
		if(strcmp(pa -> type, "send") == 0)
			{
/*
			sprintf(temp,\
			"set_all_filedescriptors: dcc_socketfd=%d", pa -> dcc_socketfd);
			to_screen(temp, "");
*/
			FD_SET(pa -> dcc_socketfd, &readfs);
			}
		}
	if(pa -> status == WAITACCEPTOTHERCLIENT)
		{
/*
to_screen("set_all_dcc_filedescriptors: status was waitacceptotherclient", "");
*/
		FD_SET(pa -> dcc_socketfd, &readfs);
		}
	}
return(1);
}/* end set_all_dcc_file_descriptors */


int dcc_chat_receive(char *rxbuf, int dcc_socketfd)
{
/* read from socket */
struct dcc_connection *pa;
int a, i;
char c;
char temp[512];
extern fd_set readfs;
char nick[80];

i = 0;
while(1)
	{
	a = read(dcc_socketfd, &c, 1);

	if(a == 0)/* EOF */
		{
		/* close the socket */
		close(dcc_socketfd);

		/* get the nick */
		pa = lookup_dcc_socketfd(dcc_socketfd);

		/* report */
		sprintf(temp, "DCC connection closed by %s", pa -> nick);
		to_screen(temp, pa -> nick);

		/* delete entry for this socketfd */
		delete_dcc_socketfd(dcc_socketfd);
		
		/* this macro removes dcc_socketfd from the filedescriptor set 
		/* readfs */
		FD_CLR(dcc_socketfd, &readfs);

		rxbuf[0] = 0;/* mark buffer ampty */
		return(1);
		}
	if(a < 0)
		{
		to_screen("read: failed, error:", pa -> nick);
		if(errno == EAGAIN) to_screen("", pa -> nick);
		if(errno == EBADF) to_screen(\
		"The FILEDES argument is not a valid file descriptor.", pa -> nick);
		if(errno == EINTR) to_screen(\
		"Read was interrupted by a signal while it was waiting for input.",\
		pa -> nick);
		if(errno == EIO)
			{
			to_screen(\
			"Hardware error, or background process tries to read from the",\
			pa -> nick);
			to_screen(\
			"controlling terminal, and the normal action of stopping the",\
			pa -> nick);
			to_screen(\
			"process by sending it a SIGTTIN signal isn't working",\
			pa -> nick);
			}
		close(dcc_socketfd);/* if at all possible */
		return(0);
		}
	rxbuf[i] = c;
	if(c == 10)break;
	i++;
	if(i > 511)break;
	}/* end while read all characters */
rxbuf[i] = 0;/* string terminator */

trace("rxbuf", rxbuf);

return(1);
}


int dcc_chat_read(struct dcc_connection *pa)
{
char rxbuf[512];
extern fd_set readfs;
char temp[512];

/* test if socket has data */
if( FD_ISSET(pa -> dcc_socketfd, &readfs) )
	{	
	/* read from server */
	if(! dcc_chat_receive(rxbuf, pa -> dcc_socketfd) )return(0);
	
	if(strlen(rxbuf) )
		{
		/* write to console */
		sprintf(temp, "=%s= %s", pa -> nick, rxbuf); 
		to_screen(temp, pa -> nick);
		}
	}
return(1);
}


int dcc_send_send_file(struct dcc_connection *pa)
{
int a, i;
char txbuf[1024];
char temp[512];
unsigned long bytes;
int c;
unsigned long bytes_acknowledged;
extern fd_set readfs;

/* wait for confirmation handshake */
if( FD_ISSET(pa -> dcc_socketfd, &readfs) )
	{
	/* wait for confirmation */	
	a = read(pa -> dcc_socketfd, (char *)&bytes, sizeof(unsigned long));
	if(a != sizeof(unsigned long) )
		{
		to_screen("\
	dcc_send_send_file: incorrect handshake: wrong byte count in acknowledge",\
		pa -> nick);

		/* this is fatal, abort */
		/* close the file */
		fclose(pa -> dccfile);
		
		/* close socket */
		close(pa -> dcc_socketfd);
		
		/* report */	
		sprintf(temp, "Filetransfer of file %s to %s aborted",\
		pa -> filename, pa -> nick);
		to_screen(temp, pa -> nick);

		/* delete entry for this socketfd */
		delete_dcc_socketfd(pa -> dcc_socketfd);/* pa no longer valid !! */

		/* return ready */
		return(1);
		}
	bytes_acknowledged = ntohl(bytes);

	/* calculate percentage acknowledged */
	pa -> percentage =\
	( (float)pa -> bytes_send / (float)pa -> filesize) * 100.0;

	if(bytes_acknowledged != pa -> bytes_send)
		{
/*
to_screen("dcc_send_send_file: bytes_acknowledged does not match bytes send,",\
		pa -> nick);
to_screen("block was split? looping and waiting for next handshake",\
		pa -> nick);

sprintf(temp, "bytes_send=%lu bytes_acknowledged=%lu",\
pa -> bytes_send, bytes_acknowledged);
to_screen(temp, pa -> nick);
*/

		/* loop for next block (the packet was split?) */
		return(1);
		}

	/* check if all acknowledged (file transfer complete),
	/* then close socket */
	if(bytes_acknowledged == pa -> filesize)
		{
		/* close socket, this signals to receiver that all is done */
		close(pa -> dcc_socketfd);

		/* report */	
		sprintf(temp, "Filetransfer of file %s to %s completed",\
		pa -> filename, pa -> nick);
		to_screen(temp, pa -> nick);

		/* delete entry for this socketfd */
		delete_dcc_socketfd(pa -> dcc_socketfd);/* pa no longer valid !! */

		/* return ready */
		return(1);
		}/* end acknowledged == pa -> filesize */

	/* at this point we can read and send the next block */
	/* read from file */ 
	for(i = 0; i < 1024; i++)
		{
		while(1)
			{
			c = getc(pa -> dccfile);
/*			sprintf(temp, "i=%d c=%c(%d)\n", i, c, c); */
/*			to_screen(temp, "");*/
			if(ferror(pa -> dccfile) ) 
				{
				perror("dcc_send_send_file():");
				continue;
				}
			break;
			}
		if(feof(pa -> dccfile) )
		/*if(c == EOF)*/
			{
			fclose(pa -> dccfile);
			break;
			}
		txbuf[i] = c;
		}

	/* send to the other client */
	a = write(pa -> dcc_socketfd, txbuf, i);     
	if(a < 0)
		{
		to_screen("\nwrite: failed, error:", pa -> nick);
		if(errno == EBADF) to_screen("Not a valid file descriptor",\
		pa -> nick);
		if(errno == EFBIG) to_screen("File to big", pa -> nick);
		if(errno == EAGAIN) to_screen("O_NONBLOCK flag set", pa -> nick);
		if(errno == EINTR) to_screen("Was waiting, but interrupted by signal",
		pa -> nick);
		if(errno == EIO) to_screen("Hardware error", pa -> nick);
		if(errno == ENOSPC) to_screen("no space", pa -> nick);
		if(errno == EPIPE) to_screen(\
		"Writing to pipe or fifo not open for read", pa -> nick);
		return(0);
		}

	/* calculate total bytes send */
	pa -> bytes_send += i;

	}/* end FD_ISSET */
return(1);
}


int get_dcc_send_file_info(char *dcc_send_file_info)
{
struct dcc_connection *pa;
char temp[80];
char c;

/* mark empty */
dcc_send_file_info[0] = 0;

/*pa points to next entry*/
for(pa = pnttab[0]; pa != 0; pa = pa -> nxtentr)
	{
	if(strcmp(pa -> type, "send") == 0)
		{
		if(pa -> status == ACCEPTED)
			{
			if(pa -> direction == SEND) c = '>';
			if(pa -> direction == RECEIVE) c = '<';
			sprintf(temp, "%s%c%s%c%2.1f ",\
			pa -> filename, c, pa -> nick, '%', pa -> percentage);
			strcat(dcc_send_file_info, temp);
			}
		}
	}
return(1);
}


int dcc_send_receive_file(struct dcc_connection *pa)
{
int a, i, j;
char c;
char rxbuf[2024];
char temp[2048];
extern fd_set readfs;
char reply_buffer[40];
unsigned long bytestemp;
char checksum_string[40];
    
/* do not close the socket if all bytes are read, sending side must do that, */
/* so the socket is closed when an EOF is received */

if( FD_ISSET(pa -> dcc_socketfd, &readfs) )
    {
	/* read from socket */
	a = read(pa -> dcc_socketfd, rxbuf, 1024);

	/* test for end of file */
	if(a == 0)
		{
		/* report */
		sprintf(temp, "Filetransfer of file %s from %s completed.",\
		pa -> filename, pa -> nick);
		to_screen(temp, pa -> nick);

		/* close file */
		fclose(pa -> dccfile);

		/* close socket */
		close(pa -> dcc_socketfd);
		FD_CLR(pa -> dcc_socketfd, &readfs);

		/* test checksum */
		/* some clients do not seem to send a checksum in the handshake,
		/* in that case strlen(pa -> checksum) = 0
		*/
		if(strlen(pa -> checksum) == 0)/* no checksum in handshake */
			{
			sprintf(temp,
"%s's client does not support checksum, no checksum checking could be done.",\
			pa -> nick);
			to_screen(temp, "");
			}					
	    else /* checksum in handshake */
	    	{
		    sprintf(checksum_string, "%x%x%x%x",\
		    pa -> file_checksum[0], pa -> file_checksum[1],\
		    pa -> file_checksum[2], pa -> file_checksum[3]);
			if(strcmp(checksum_string, pa -> checksum) != 0)
				{
				sprintf(temp,\
			"WARNING CHECKSUM ERROR IN FILE %s handshake was %s file was %s.",\
				pa -> filename, pa -> checksum, checksum_string);
				to_screen(temp, pa -> nick);
				}
			}/* end checksum in handshake */
		/* test the filesize */
		if(pa -> bytes_read != pa -> filesize)
			{
			sprintf(temp,\
		"WARNING BYTES RECEIVED %lu DOES NOT MATCH FILESIZE %lu IN HANDSHAKE",\
			pa -> bytes_read, pa -> filesize);
			to_screen(temp, pa -> nick);
			}

		/* delete entry for this socketfd */
		delete_dcc_socketfd(pa -> dcc_socketfd);/* pa no longer valid !! */
		return(1);
		}

	/* write to file */
	j = 0;
	for(i = 0; i < a; i++)
		{
		putc(rxbuf[i], pa -> dccfile);
		/* calculate checksum */
		if (j < a) pa -> file_checksum[0] ^= rxbuf[j];
		j++;
		if (j < a) pa -> file_checksum[1] ^= rxbuf[j];
		j++;
		if (j < a) pa -> file_checksum[2] ^= rxbuf[j];
		j++;
		if (j < a) pa -> file_checksum[3] ^= rxbuf[j];
	    j++;
		}

	/* not EOF, do not want to acknowledge if sender closes connection,
	/* since this indicates that file was received ok (or abort by sender) */
	/* calculate total bytes read */
	pa -> bytes_read += a;

	/* calculate percentage read */
	pa -> percentage =\
	( (float)pa -> bytes_read / (float)pa -> filesize) * 100.0;

	/* acknowledge packet (network byte order) */
	bytestemp = htonl(pa -> bytes_read);
	a = write(pa -> dcc_socketfd, (char *)&bytestemp, sizeof(unsigned long) );

	}/* end FD_ISSET */
return(1);
}


int dcc_read_write_all_channels()
{
int i;
struct dcc_connection *pa;
int dcc_socketfd;
extern fd_set readfs;
int new;
struct sockaddr_in clientname;
size_t size;
char temp[512];
char txbuf[1025];
int a, c;

for(pa = pnttab[0]; pa != 0; pa = pa -> nxtentr)
	{
	if(pa -> status == ACCEPTED)
		{
		if(strcmp(pa -> type, "chat") == 0)
			{
			/* read from this socket */ 
			if(! dcc_chat_read(pa) )
				{
				return(0);/* error */
				}
			}/* end type chat */
		if(strcmp(pa -> type, "send") == 0)
			{
			if(pa -> direction == RECEIVE)
				{
				if(! dcc_send_receive_file(pa) )
					{
					return(0);/* error */
					}
				}
			if(pa -> direction == SEND)
				{
				if(! dcc_send_send_file(pa) )
					{
					return(0);/* error */
					}
				} 
			}/* end type send */
		}/* end status accepted */
	if(pa -> status == WAITACCEPTOTHERCLIENT)
		{
		if( FD_ISSET(pa -> dcc_socketfd, &readfs) )
			{
			/* do an accept */
			size = sizeof (clientname);
/*
			sprintf(temp, "accepting connections on port %d socket %d",\
			ntohs(pa -> port), pa -> dcc_socketfd);
			to_screen(temp, pa -> nick);
*/
			new = accept(pa -> dcc_socketfd,\
			(struct sockaddr *) &clientname, &size);
			if (new < 0)
				{
				to_screen("accept: failed, error:", pa -> nick);
				if(errno == EBADF) to_screen(\
				"The SOCKET argument is not a valid file descriptor.",\
				pa -> nick);
				else if(errno == ENOTSOCK) to_screen(\
				"The descriptor SOCKET argument is not a socket.", pa -> nick);
				else if(errno == EOPNOTSUPP) to_screen(\
				"The descriptor SOCKET does not support this operation.",\
				pa -> nick);
				else if(errno == EWOULDBLOCK)
					{
					to_screen(\
				"SOCKET has nonblocking mode set, and there are no pending",\
					pa -> nick);
					to_screen("connections immediately available.",\
					pa -> nick);
					}
				else to_screen("Undocumented error", pa -> nick);
 				return(1);
				}
			if(strcmp(pa -> type, "chat") == 0)
				{
				sprintf(temp,\
				"DCC chat connection accepted by %s ip %s, port %d.",
				pa -> nick, inet_ntoa (clientname.sin_addr),\
				ntohs (clientname.sin_port));
				}
			if(strcmp(pa -> type, "send") == 0)
				{
				if(pa -> direction == RECEIVE)
					{
					sprintf(temp,\
			"DCC file transfer for file %s from %s ip %s port %d accepted",\
					pa -> filename, pa -> nick,\
					inet_ntoa (clientname.sin_addr),\
					ntohs (clientname.sin_port));
					}
				if(pa -> direction == SEND)
					{
					sprintf(temp,\
				"DCC file transfer for file %s to %s ip %s port %d accepted",\
					pa -> filename, pa -> nick,\
					inet_ntoa (clientname.sin_addr),\
					ntohs (clientname.sin_port));
					}
				}	
			to_screen(temp, pa -> nick);
			
			FD_CLR(pa -> dcc_socketfd, &readfs);
			FD_SET(new, &readfs);

			/* close the old socket */
			close(pa -> dcc_socketfd);

			pa -> dcc_socketfd = new;
			/* pa -> port = clientname.sin_port;/* not used anymore */
			pa -> status = ACCEPTED;

			/* in case of a dcc send, send the first block, then wait for
			/* acknowledge for this block */
			if(strcmp(pa -> type, "send") == 0)/* dcc send */
				{
				if(pa -> direction == SEND)/* sending */
					{
					pa -> dccfile = fopen(pa -> pathfilename, "rb");
					if(! pa -> dccfile)
						{
						sprintf(temp,\
		"dcc_read_write_all_channels: failed: cannot open file %s for read",\
						pa -> pathfilename);
						to_screen(temp, pa -> nick);
						return(0);
						}
					for(i = 0; i < 1024; i++)
						{
						while(1)
							{
							c = getc(pa -> dccfile);
							if(ferror(pa -> dccfile) )
								{
								perror("dcc_read_write_all_channels():");
								continue;
								}
							break;
							}
						if(feof(pa -> dccfile) )
/*						if(c == EOF)*/
							{
							fclose(pa -> dccfile);
							break;
							}
						txbuf[i] = c;
						}

					/* send to the other client */
					a = write(pa -> dcc_socketfd, txbuf, i);     
					if(a < 0)
						{
						to_screen("\nwrite: failed, error:", pa -> nick);
						if(errno == EBADF) to_screen(\
						"Not a valid file descriptor", pa -> nick);
						if(errno == EFBIG) to_screen("File to big",\
						pa -> nick);
						if(errno == EAGAIN) to_screen("O_NONBLOCK flag set",\
						pa -> nick);
						if(errno == EINTR) to_screen(\
						"Was waiting, but interrupted by signal", pa -> nick);
						if(errno == EIO) to_screen("Hardware error",\
						pa -> nick);
						if(errno == ENOSPC) to_screen("no space", pa -> nick);
						if(errno == EPIPE) to_screen(\
						"Writing to pipe or fifo not open for read",\
						pa -> nick);
						return(0);
						}
					pa -> bytes_send = i;
					}/* end if direction is SEND */					
				}/* end if type is send */
			}/* end fd issset */

		}/* end  status waitacceptotherclient */
	}/* end for allstructures */
return(1);
}/* end function dcc_read_write_all_channels */
