/*	NB:	You must link in socket library on Windows: 
	wsock32.lib (in Project -> Settings -> Link).

	For UNIX (not always necessary):
	-lsocket

How to use these calls - here is an example.


int tcp_read(Chan *c, char *s, int len)
Returns the number of bytes read, or 0 for a socket close, or -1
for an error.  


main()
{
	char bf[BFSZ];
	char tmp[BFSZ];
	Chan *c = NULL;

	tcp_init(printf);
	tcp_new(  &c);
	if (!tcp_open(c, "netwin.co.nz",119,NULL)) {
		printf("Could not open link %s\n",tcp_error(c,tmp));
	}
	if (!tcp_readline(c,bf,BFSZ-1)) return 0;
	printf("Response %s\n",bf);
	tcp_printf(c, "group %s\r\n","netwin.dnews");
	if (!tcp_readline(c,bf,BFSZ-1)) return 0;
	printf("Response %s\n",bf);
	tcp_release(c);
	tcp_free(&c);
	return TRUE;
}

Listening on a socket - one thread only
int tcp_listen(Chan **cc, int port, char *bindin, int reuse);
for server set reuse = TRUE, for ftp set it to false, it means
the port can be reused, if you don't set it then a second server
cannot be started while the original ones socket is still
in a twilight state.

	tcp_listen(&xmgr->sk,3000,"",FALSE);
 
	for (;!shutdown_now;) {
		newchan = NULL;
		r = tcp_waitfor(xmgr->sk,100);
		if (r) {
			if (!tcp_accept(xmgr,&newchan)) {
				tcp_free(&newchan);
				imsg("Accept failed");
				continue;
			}
			imsg("Created new MGR chan %p\n",newchan);
			link_new(&l);
			if (l==NULL) {
				imsg("Link_new failed\n");
				break;
			}
			l->c = newchan;
			process_request(l);
		}
	}


*/
#ifndef TCPLIB_HEADER
#       define  TCPLIB_HEADER

#ifdef __cplusplus
extern "C" {
#endif

#include <stdarg.h>
#include <time.h>
#ifndef WIN32
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>

#define socket_close close
#define socket_open open
#define socket_read read
#define socket_ioctl ioctl
#define socket_write write
#define NEED_BCOPY TRUE
#ifndef SOCKET
#define SOCKET short
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif

#else /* Windows */

#ifdef DO_IPV6

//#include Ws2tcpip.h and also Wspiapi.h

#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h> // apparently this fixese problems on w2k with ipv6 getinfo call missing..
#include <mstcpip.h>
#else
#include <winsock.h>
#endif

#define socket_close closesocket
#define socket_ioctl ioctlsocket
#define socket_read(s,b,l) recv(s,b,l,0)
#define socket_write(s,b,l) send(s,b,l,0)

#endif

#define socket_shutdown shutdown

#include "defines.h"
#include "lib_spawn.h"


#ifndef GOT_WORDLIST
typedef struct WORDLIST {char *word; struct WORDLIST *next;} Wordlist ;
#define GOT_WORDLIST 1
#endif

typedef struct MYSSL {char msg[1000]; int wouldblock; void *bio;void *ssl; int state; int closeit;} Myssl;

typedef struct CHAN {
	SOCKET sk;/* the socket */
	int never_close; // If true then socket is owned by someone else, so never close it.
	int id; /* socket's id */
	int block; /* Last state of channel (blocking true or false) */
	int noblock;/*Set(ie call tcp_noblock)before opening socket to make nonblocking socket*/
	char savedc; /* Used by read_dot to save char that is rplaced by null */
	int issaved;
	char *inbf;
	int maxinbf;
	time_t start_time;
	int inupto;/*Number of characters that have been read from the start of the channel 
	             buffer(inbf). This must be incremented or the buffer space cannot be
				 reused*/
	int inlen;/*The total number of characters in the channel buffer (inbf)*/
	int canclose;/* A flag that the socket should not be closed */
	int close_now; /* if true abort read and return an error */
	int xerrno;/* Error number associated with last socket call */
	int lclport;/* The prefered port for the socket to bind to(set using tcp_lclport)*/
	int write_timeout; /* timeout in milliseconds for tcp_printf */
	int read_timeout; /* timeout in milliseconds for readline functions */
	int lcl_saved,rmt_saved; /* If non zero, then contain the saved port settings before socket was 'duped' */
	Myssl *ssl;
	int start_ssl_soon; /* If true, then start ssl once the new thread is running */
	char *buffer;	/* output buffering tcp_set_buffer... */
	int buffer_len;
	int buffer_len_malloced; /* Used for over-reallocing to save on lots of reallocs. */
	int isbuffer; 
	char myname[BFSZ];
	char theirname[BFSZ];
	char erradd[200];
	int done_open; //set to true if the open has completed good or bad in tcp-wait_write
	char blackreason[200];
	int blackhole; /* if true, throw away all messages from this user */
	int fixnulls; /* if true,then kill nulls so readline works with bad input */
	int total_sent; /* Total bytes sent on this channel*/
	int total_read; /* Total bytes read on this channel*/
	int log_read; /* If true log detailed reads for this channel */
	int log_write; /* if true log detailed writes for this channel */
	int (*out_fn)(void *x, char *bf, int len);
	void *out_p1;
	struct CHAN *next;/* Link your channels and make a list if you're that way inclined*/
	int mean;
	int in_close; // If true we are closing socket so should not close it again. 
	struct sockaddr_in udp_sin;
	int nsleep; // How long to sleep between packets to maintain throughput
	int sleepi; // How long to sleep between packets to maintain throughput
	char is_atrn;	// True if this is an 'atrn' channel. 
	char notfree; // Should be zero, if it isn't crash as channel is not initialized. (or was freed)
	char fill[2];
	int sent; // counter for debugging sent data, not really used 
	int spinwrite; // set it we want to avoid poll on a write (solaris debugging)
	char *ssl_clientcert;	// Client certificate (for Apple Push notification connections)
	int ssl_verified; // true if the ssl certificate verified (the other end);
	int ssl_code; // ssl verification code on failures.  
	char rhosts[BFSZ];
} Chan;


/*tcp_hostname: gets the name of the local host
  Arguements: host: buffer to read name into(upto 190 chars can be read)
  Return: pointer to the buffer. 
  Remarks:returns 127.0.0.1 if error in determining host name occurs       */
char *tcp_hostname(char *host);
int tcp_pop_response(Chan *c, char *mode, char *reason);

/*tcp_init: initiates use of socket DLL, must be called before using other tcp functions
  Arguements: vemsg: function by which to output initialization error messages
  Return: none.
  Remarks: only required on WIN32                                          */
void tcp_init(int (*vemsg)( const char *format ,...));

/*tcp_new: allocates new CHAN structure for a channel
  Arguements: c: pointer to channel to allocate structure for (*c must == NULL)
  Return: none.
  Remarks: error messages encountered are output through emsg function      */

#define tcp_new(c) tcp_new_do(c,__FILE__,__LINE__)
void tcp_new_do(Chan **c, char *file, int line);
int tcp_set_allbf(void);
int tcp_set_inherit(int t);
int tcp_ssl_init_rhosts(Chan *c, int isserver, Chan *inherit, char *reason, char *rhosts, char *mstate);
/*tcp_accept: accepts an incoming connection attempt on a channel
  Arguements: listen:a channel that has been placed in a listening state
			  newchan: pointer to a new channel(*newchan must == NULL) that the new
					   connection will be made to
  Return: 1 if new connection successfully setup
          0 otherwise ( error number is returned in c->xerrno)
  Remarks: incoming connection attempts to connect at channel 'listen'. If a connection
		   attempt is detected the new channel is created and returned as newchan.											
           This is a blocking call.  tcp_accept_optimal sets buffer to 128Kbytes   */
int tcp_grow_inbf(Chan *c, int len);
int tcp_in_bf_set(Chan *newchan, int x);
int tcp_write_smooth(Chan *c, char *s, int len);
int tcp_set_tcpbf(int sz);
int tcp_set_readclose(int x);
int tcp_out_bf_set(Chan *newchan, int x);
int tcp_accept(Chan *listen, Chan **newchan, char *whence);
int tcp_128kaccept(Chan *vlisten, Chan **newchan);
/*tcp_listen: creates a new channel and places it in a state where it is listening 
			  for an incoming connection.
  Arguements: cc: pointer to new channel(*cc must == NULL) to be set in listening state
		      port: port to listen on
			  bindin: unused
			  reuse: if non zero, allow socket to be bound to a local address that
			         is already in use
  Return: 1 if socket successfully created and placed in listening state
          0 otherwise                                        					*/
int tcp_listen(Chan **cc, int port, char *bindin, int reuse);
int tcp_socket_write(Chan *c, char *s, int len);
int tcp_accept_test(Chan *vlisten, Chan **newchan);
int tcp_msleep(int msec);
int tcp_waitfor2(Chan *c, int msec, int israw);
/*tcp_lasterror: gets the last socket error that occured
  Arguements: c: c->xerrno is set to the value of the last error
  Return: value of the last socket error that occured	
  Remarks: the other functions call this when an error occurs, it is not required to
           call this function directly                    					*/
int tcp_lasterror(Chan *c);

/*tcp_release: puts channel into state where it may be closed
  Arguement: c: channel to be put into closable state
  Return: 1 if channel successfully put into state where it may be closed
          0 otherwise														*/
int tcp_release(Chan *c);
int tcp_log_closes(int x);
int tcp_read_timed_nice(Chan *c, char *s, int len, int msecs);

/*tcp_free: closes a channel and frees memory associated with it
  Arguements: cc: pointer to channel to be freed
  Return: 0 if cc is an invalid channel or contains and invalid socket
          1 otherwise                                                       */
int tcp_free(Chan **cc);
void tcp_set_exit(void);
int tcp_dot_lf(int fix);

/*tcp_write: sends data on a connected channel
  Arguements: c:channel to send data over
              s:a buffer containing the data to be sent
			  len:length of buffer containing data
  Return: the number of bytes sent.
          This may block if len exceeds out going buffer size              */
int tcp_write(Chan *c, char *s, int len);
int tcp_write_timed(Chan *c, char *s, int len, int msecs);
int tcp_write_tout(Chan *c, char *s, int len);
int tcp_listen_list(Chan **cc, int port, char *bindin, int reuse); // Identical but it is allowed to return a linked list of channels....

/*tcp_read: reads incoming data on a channel
  Arguements: c: channel to read data from
	          s: buffer to store data
			  len: maximum number of bytes to read
  Return: the number of bytes read  
  Remarks: By default this is a blocking call.								*/
int tcp_read(Chan *c, char *s, int len);



/*tcp_read_timed: puts a time limit on an attempt to read incoming data on a channel
  Arguements: c: channel to read data from
			  s: buffer to store data
			  len: maximum number of bytes to read
			  msecs: maximum number of milliseconds to wait before receiving data
  Return:  the number of bytes read
		   -2 if the function timed out								    	*/
//#define tcp_read_timed(a,b,c,d) tcp_read_timed_real(a,b,c,d,__FILE__,__LINE__)
int tcp_read_timed(Chan *c, char *s, int len, int msecs);

/*tcp_peek_line_timed: puts a time limit on an attempt to read incoming data on a channel
                       data can be reread from subsequent calls.
  Arguements: c: channel to read data from
			  s: buffer to store data
			  len: maximum number of bytes to read
			  msecs: maximum number of milliseconds to wait before receiving data
  Return:  the number of bytes read
		   -2 if the function timed out								    	*/
int tcp_peek_line_timed(Chan *c, char *s, int len, int msecs);
int tcp_wait_open(Chan *c, int msec);

/*tcp_printf: sends data on a connected channel using same format as printf
  Arguements: c: channel to send data over
              arglist: printf arguements 
  Return: 1 if data successfully sent
          0 otherwise
  Remarks: errors are output through emsg.
           All tcp_printf's are logged using emsg if tcp_setdebug(TRUE) has been called */
int tcp_printf(Chan *c, char * arg_list, ...);

#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif

int tcp_printf(Chan *c, char * arg_list, ...) __attribute__((format(printf, 2, 3)));

/*tcp_open: opens a connection to a remote machine
  Arguements: cc: pointer to channel structure to connect with
              hostname: name of machine to connect to
			  port: port number on remote machine to connect to
			  bindout: unused
  Return: 1 if valid channel created
          0 otherwise
  Remarks: errors are variously outputed using emsg or printf               */

int tcp_open_x(Chan *cc, char *hostname,  int port, char *bindout, char *src, int line);
#define tcp_open(a,b,c,d) tcp_open_x(a,b,c,d,__FILE__,__LINE__)

/*tcp_readline: reads upto the first newline encountered in incoming data on a channel
Arguements: c: the channel to read from
              out: a buffer to read data into(data is also read into channel buffer)
			  len: maximum number of characters to read into out buffer(less may be read 
				   if there is insufficient room in the channel buffer)
Return: 0 if a time out was encountered before receiving a new line(data read so far
            will be in channel buffer, but not in 'out' arguement).
          1 otherwise
Remarks: This is nonblocking, but time outs may be significant if no data is queued ie
           in the order of seconds.
           The actual newline char and any directly proceding cariage returns are removed*/
int tcp_readline(Chan *c, char *out, int len);
int tcp_set_getsock( int t);
int tcp_udp_read(Chan *c, char *bf, int len);
int tcp_udp_write(Chan *c, char *bf, int len);
int tcp_open_udp(Chan *c, char *xname,int dst_port);

/*tcp_error: gets a text message detailing a socket error number
  Arguements: c:channel on which a socket error has occured
              bf: buffer to read error message into
  Return: a pointer to bf                                                               */
char *tcp_error(Chan *c, char *bf);
int tcp_close_mean(Chan *c);
int tcp_set_v11(int x);

/*tcp_getip: gets ip address of the peer to which a channel is connected
  Arguements c: connected channel from which to get the peers ip from
             ipbf: buffer to store ip address
  Return: 1 if peer ip successfully determined
          0 otherwise
  Remarks: Errors outputed through emsg                                                  */
int tcp_getip(Chan *c, char *ipbf);
int tcp_open_noblock_x(Chan *cc, char *hostname,  int port, char *bindout, char *src, int line);
int tcp_open_nice_x(Chan *cc, char *hostname,  int port, char *bindout, char *src, int line);
#define tcp_open_nice(a,b,c,d) tcp_open_nice_x(a,b,c,d,__FILE__,__LINE__)
#define tcp_open_noblock(a,b,c,d) tcp_open_noblock_x(a,b,c,d,__FILE__,__LINE__)

int tcp_open_reuse_nice(Chan *cc, char *hostname,  int port, char *bindout);

/*tcp_getports: get local and remote port through which a channel is connected
  Arguements: c: connected channel
              local: buffer to store local port
			  remote: buffer to store remote port
  Return: 0 if either of the ports could not be determined
          1 otherwise
  Remarks: Error messages output through emsg										     */
int tcp_getports(Chan *c, int *local, int *remote);
int tcp_notipv4(char *s);
/*tcp_getport: get local port through which a channel is connected
  Arguements: c: connected channel
	          local: buffer to store local port number in
  Return: 0 if port could not be determined
          1 otherwise
  Remarks: Error messages output through emsg                                            */
int tcp_getport(Chan *c, int *local);

/*tcp_isip: tells you if s is an ip
*/
int tcp_isip(char *s);

/*tcp_isopen: determines if a channel believes it is connected
  Arguements: c: the channel to be queried
  Return: 0 if channel has invalid socket or is NULL
          1 otherwise	                                                                 */
int tcp_isopen(Chan *c);
int tcp_fd_setsize(void);
int tcp_wait_read(Chan *c, int msec);

int tcp_wait_read_buf(Chan *c, int msec);
int tcp_read_complete(Chan *c, char *s, int len);
int tcp_insert_buffer(Chan *c, char *bf, int len);

/*tcp_nodelay: switches on/off the tcp Nagle algorithm for a given channel
  Arguements: c: the channel
              x: 1 for on, 0 for off
  Return: 1 if Nagle algorithm successfully switched on or off
          0 otherwise
  Remarks: Nagle algorithm buffers incoming data if previous data is still in transit and 
           buffers out going data until a full packet can be sent.                        */
int tcp_nodelay(Chan *c, int x);

/*tcp_getmyip: gets local ip of a channel
  Arguements: c: connected channel to get ip of
              ipbf: buffer to store ip
  Return: 1 if ip successfully determined
          0 otherwise   
  Remarks: errors outputed via emsg                                                      */
int tcp_getmyip(Chan *c, char *ipbf);

/*tcp_waitfor: wait a given length of time or until their is data queued on a socket for reading

  Arguements: sk: socket to wait on

              msec: number of millseconds to wait before timing out

  Return: 0 if times out

          1 if their is data queued to be read                                           */

int tcp_waitfor(Chan *c, int msec);



/*tcp_waitforbuf: like tcp_waitfor but checks c->inlen too incase already have something in buf

  Arguements: sk: socket to wait on

              msec: number of millseconds to wait before timing out

  Return: 0 if times out

          1 if their is data queued to be read                                           */

int tcp_waitforbuf(Chan *c, int msec);


/*tcp_printf_nolog: sends data on a connected channel using same format as printf
  Arguements: c: channel to send data over
              arglist: printf arguements 
  Return: 1 if data successfully sent
          0 otherwise
  Remarks: errors are output through emsg.                                               */
int /*@printflike@*/  tcp_printf_nolog(Chan *c, char * arg_list, ...);

/*tcp_wait_write: waits for a channel to enter a writeable state
  Arguements: c: a connected channel
              msec: number of milliseconds to wait
  Return: 1 if check was successful
          0 if check failed or time out occured                                           */
int tcp_wait_write(Chan *c, int msec);
int tcp_set_do_gzip(int x);

/*tcp_noblock: if called before openning channel, will create a non-blocking channel
  Arguements: c: the channel
  Return: 1 if channel successfully set to nonblocking
          0 otherwise                                                                     */
int tcp_noblock(Chan *c);

/*tcp_lookup: find ip address of a domain name
  Arguements: s: domain name(may contain port in name:port format)
              ip: buffer to return ip address in
  Return: 1 if ip successfully determined
          0 otherwise                                                                     */
int tcp_lookup(char *s, char *ip);

/*tcp_lookup_all: find all ip address of a domain name
  Arguements: s: domain name(may contain port in name:port format)
            res: wordlist to add results to
  Return: x # of ips found
          0 otherwise                                                                     */
int tcp_lookup_all(char *s, Wordlist **res);

/*tcp_setdebug: adds extra debug functionality to tcp functions
  Arguements: x: 1 for debug on, 0 for off                                                */
void tcp_setdebug(int x);

/*tcp_setlclport: sets the desired port a channel should use
  Arguements: c: the channel
              x: port number
  Return: 1 if lclport successfully set
          0 otherwise	                                                                  */
int tcp_buffer_len(Chan *c);
int tcp_setlclport(Chan *c, int x);
int tcp_getname(char *ipnumber, char *ipname);
int tcp_open_reuse(Chan *cc, char *hostname,  int port, char *bindout);
void tcp_setlinger(Chan *c, int xon, int hunds);
int tcp_setblock(Chan *c, int t);
int tcp_out_bf_128k(Chan *newchan);
int tcp_in_bf_128k(Chan *newchan);
int tcp_close_all(void);
char * tcp_buffer_get(Chan *c, int *len);
void tcp_buffer_clear(Chan *c);

int tcp_handle_count(void);
int tcp_ssl_server(Chan *c, char *reason);
int tcp_waitfor(Chan *c, int msec);
int tcp_ssl_ciphers_web(char *xciphers);
int tcp_ssl_clientcert(Chan *c, char *cert);
int tcp_ssl_init(Chan *c, int isserver, Chan *inherit, char *reason);
int tcp_ssl_init_domain(Chan *c, int isserver, char *domain, Chan *inherit, char *reason, int isweb, char *mstate);
char *tcp_myssl_get_protocol_name_used(Chan *c);
int tcp_ssl_init_real(Chan *c, int require_cert, int isserver, char *domain, Chan *inherit, char *reason, int isweb, char *mstate);
int tcp_ssl_cmp(Chan *a, Chan *b);
int tcp_ssl_setkey(char *pk, char *keyfile);
int tcp_ssl_setdir(char *dir);
Wordlist *tcp_ssl_setdomains(Wordlist *domains);
char *tcp_read_dot(Chan *c, int *len, int *isend, int *isnulls, int secs, int isstart);
int tcp_readbf(Chan *c, int msec, int max);
int tcp_close_nice(Chan *c);
int tcp_wouldblock(Chan *c);
int tcp_check_open(Chan *c);
int tcp_not_connected(Chan *c);
int tcp_blist_tot(void);

int tcp_handle_dump(void);
int tcp_buffer_add(Chan *c, char *bf, int len);
int tcp_buffer_flush(Chan *c);
void tcp_buffer_set(Chan *c, int x);
void tcp_out_fn(Chan *c, int (*fn)(void *x, char *bf, int len), void *p1);
int tcp_socket_write_raw(Chan *c, char *s, int len);
int tcp_set_handle_cb(int (*cb)(IHANDLE handle, int isopen, int logonly, char *filename, int line, char *info));
int tcp_check_open_real(Chan *c, int noclose);
int tcp_download_timeout(int x);
int tcp_cidr_ipv6(char *address, char *cidr);
int tcp_set_write_timeout(Chan *c, int msec);
char *tcp_hostname_full(char *host);
int tcp_mime_encode_base64(char *xin, int inlen, char *xout, int outlen);

int tcp_waitfor_list(Chan *first, int msec);
int tcp_set_read_timeout(Chan *c, int msec);
void tcp_init_dmsg(int (*vemsg)( const char *format ,...));
int tcp_download_file(char *url, char *proxy, char *localname, time_t modified, int *is_modified, char *reason, int binmode);
int tcp_download_file_do(char *url, char *proxy, char *localname, time_t modified, int *is_modified, char *reason, int binmode, int (*progress_fn)(void *param, int so_far, int total),void *progress_param, char *user, char *pass);
int tcp_download_file_real(char *xurl, char *proxy, char *localname, time_t modified, int *is_modified, char *reason, int binmode, int (*progress_fn)(void *param, int so_far, int total),void *progress_param, char *user, char *pass, char *cookies, int *vcode);
int tcp_post_do(char *type, char *xurl, char *proxy, char *localname, char *reason, int binmode, int (*progress_fn)(void *param, int so_far, int total),void *progress_param, char *content, int content_len, int *code, int (*auth_fn)(void *param, char*challenge, char*method, char*url, char*bf, int bfsz), void *auth_param);

int tcp_listen_que(int x);

/* For reading long command lines, free vout using tcp_readline_free, max can
safely be a silly number, it's a limit not a preallocated amount */
int tcp_readline_big(Chan *c, char **vout, int max, int toutsec);
int tcp_readline_free(char *out);
int tcp_clear_buffer(Chan *c);
int tcp_http_get(char *url, char *reason);
int tcp_peek_data_timed(Chan *c, int msecs);
int tcp_socket_close(SOCKET sk, char *src, int line);
int tcp_ssl_ciphers(char *xciphers);
int tcp_read_all(Chan *c, char *s, int len, int msecs); // Keep reading until we have the entire packet we want, or the timeout occurs.


char *inet_ntoa_safe(struct in_addr in, char *tmp);
int tcp_fix_ipv4(int x);
int tcp_close_on_exec(Chan *c);
void tcp_close_on_exec_raw(int fd);
void tcp_open_on_exec_raw(int fd);
char *tcp_ipv6_to_string(struct in6_addr *addr, char *tmp);
#ifdef DO_IPV6
int tcp_string_to_addr_ipv6(char *xname, struct in6_addr *addr);
#endif 
int tcp_set_ipv6(int x);
int tcp_download_cb(char *url, char *user_agent, char *prefix,int tout, char *reason, int (*cb)(int inhead, void *param, char *bf, int len),void *param);
int tcp_set_mutex(int (*cb)(int vm, int isget, int seconds), int m);
#ifdef __cplusplus
}
#endif

#endif
