/*
 * sshchat.c
 *
 * Client component of sshchat, connects to server, identifies itself using ~/.sshchat file and forwards stdin/stdout.
 *
 * Author: Dario Rodriguez dario@softhome.net
 * This program is licensed under the terms of the GPL v2.1+
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "socklib.h"
#include "sbuf.h"

#include "sshchat.h"

#define MAXIDSIZE 128
#define CONNTIMEOUT 2000
#define SELECTTIMEOUT 1000

#define BUFSIZE 16384

#define INFD 0
#define OUTFD 1

int
main(int argc, char *argv[])
{
	char id[MAXIDSIZE];
	char *ptr;
	char *host;
	int port;
	long hostsize;
	int socket;
	sbuf *c2s,*s2c;
	sselect *ssel;
	int forcedexit;
	int readfds[2];
	int writefds[2];
	int nreadfds;
	int nwritefds;
	int i;
	int queued;
	if(argc!=2 || strcmp(argv[argc-1],"--help")==0) {
		fprintf(stderr,"Syntax: %s username",argv[0]);
		return(1); /* no id */
	}
	strncpy(id,argv[1],sizeof(MAXIDSIZE));
	id[MAXIDSIZE-1]='\0';
	if((ptr=strchr(id,' '))!=NULL)
		*id='\0'; /* spaces not allowed */
	/* open socket to server */
	if((host=ipv4_genip("127.0.0.1",&hostsize))==NULL)
		return(-3); /* couldn't resolv localhost */
	if((port=ipv4_genport("sshchat",SERVERPORT))==-1) {
		free(host),host=NULL;
		return(-4); /* couldn't resolv port */
	}
	if((socket=ipv4_preconnect(host,hostsize,port))==-1) {
		free(host),host=NULL;
		return(-5); /* couldn't connect to localhost (1) */
	}
	if(ipv4_connect(socket,CONNTIMEOUT)==-1) {
		free(host),host=NULL;
		return(-6); /* couldn't connect to localhost (2) */
	}
	ipv4_postconnect(socket);
	free(host),host=NULL,hostsize=0;
	/* alloc I/O buffers */
	if((c2s=sbuf_init(BUFSIZE))==NULL || (s2c=sbuf_init(BUFSIZE))==NULL) {
		close(socket),socket=-1,sbuf_free(c2s),c2s=NULL;
		return(-7); /* couldn't alloc buffers */
	}
	if((ssel=sselect_init())==NULL) {
		close(socket),socket=-1,sbuf_free(c2s),c2s=NULL,sbuf_free(s2c),s2c=NULL;
		return(-8); /* couldn't alloc select struct */
	}
	/* prefeed the identification */
	sbuf_addstr(c2s,id);
	sbuf_addstr(c2s,"\n");
	/* forward data */
	forcedexit=0;
	while(!forcedexit) {
		sselect_reset(ssel);
		if(sbuf_count(c2s)<BUFSIZE)
			sselect_addread(ssel,INFD,NULL);
		if(sbuf_count(c2s)>0)
			sselect_addwrite(ssel,socket,NULL);
		if(sbuf_count(s2c)>0)
			sselect_addwrite(ssel,OUTFD,NULL);
		if(sbuf_count(s2c)<BUFSIZE)
			sselect_addread(ssel,socket,NULL);
		if(sselect_wait(ssel,SELECTTIMEOUT)<1)
			continue;
		nreadfds=sselect_getread(ssel,readfds,sizeof(readfds)/sizeof(readfds[1]));		
		nwritefds=sselect_getwrite(ssel,writefds,sizeof(writefds)/sizeof(writefds[1]));		
		for(i=0;i<nreadfds && i<(sizeof(readfds)/sizeof(readfds[0]));i++) {
			if(readfds[i]==INFD && sbuf_count(c2s)<BUFSIZE) {
				if((queued=sock_queued(readfds[i]))==0) {
					forcedexit=1;
					break;
				}
				sbuf_discard(c2s);
				sbuf_fill(c2s,INFD,queued);
			}
			if(readfds[i]==socket && sbuf_count(s2c)<BUFSIZE) {
				if((queued=sock_queued(readfds[i]))==0) {
					forcedexit=1;
					break;
				}
				sbuf_discard(s2c);
				sbuf_fill(s2c,socket,queued);
			}
		}
		for(i=0;i<nwritefds && i<(sizeof(writefds)/sizeof(writefds[0]));i++) {
			if(writefds[i]==OUTFD && sbuf_count(s2c)>0)
				sbuf_send(s2c,OUTFD,sbuf_count(s2c));
			if(writefds[i]==socket && sbuf_count(c2s)>0)
				sbuf_send(c2s,socket,sbuf_count(c2s));
		}
	}	
	/* normal exit */
	close(socket),socket=-1;
	sbuf_free(c2s),c2s=NULL;
	sbuf_free(s2c),s2c=NULL;
	sselect_free(ssel),ssel=NULL;
	return(0);
}