/*************************************************************************/
/*                  VChat interactive IP-level chat system               */
/*-----------------------------------------------------------------------*/
/*  (c) '93/'94 by Andreas S. Wetzel (mickey@deadline.bln.sub.org)       */
/*                 All rights reserverd.                                 */ 
/*-----------------------------------------------------------------------*/
/* See the file COPYRIGHT in the top level directory of VChat for        */
/* copyright notices and further disclaimers.                            */ 
/*************************************************************************/

#include "../config.h"
#include "../global.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <pwd.h>
#include <stdarg.h>
#include <signal.h>
#include "proto.h"
#include "../version.h"

/******* Globals ********/

int	reverse=0;		/* Screenmode flag                     */
int	port;			/* Portnumber of vserver to connect to */
int	sock;			/* Socket desriptor to server          */
int	color;			/* 00 = S/W Terminal FF = Farbterminal */
int	t_cols,t_rows;		/* Bildschirmgroesse spalten x zeilen  */
int	txcol = 0xff;		/* Color emulation for text.           */

char	csi[8];			/* Command Sequence Intro              */
char	svhost[32];		/* Hostname the server is runninv on   */
char	myhost[32];		/* Hostname we are running on          */
char	user[16];		/* UID of user who invoked vchat       */
char	nick[32];		/* Our nickname :-)                    */

char	*prog_name;		/* The program name we run under       */
char	*emulation;		/* Terminal-Emulation                  */

struct	sockaddr_in sock_in;	/* Socket Addressing structure         */
struct	client_data cl_dat;	/* Client Data structure               */
struct	in_addr sv_ip;		/* IP Adress of server                 */
struct	in_addr my_ip;		/* Our own IP Adress                   */

extern	int errno;		/* Global variable errno               */

/********* Code *********/

void main(int argc, char *argv[])
{
	int c, len;
	extern int sock;
	char buf[128];

	prog_name = argv[0];

	init();
	get_param(argc, &argv[0]);
	connect_server();
	screen_init();

	signal(SIGWINCH, (void *)winch);
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);

	tty_param(SAVE);
	tty_mode(1);

	for(;;)				/* Endlos eingabe schleife */
	{
		len = edline(buf, t_cols - strlen(nick) - 5);

		if(strchr(".:/", buf[0]) && buf[1] != 'x')
		{
			handle_cmd(buf);
		}
		else if(strchr(".:/", buf[0]) && buf[1] == 'x')
		{
			break;
		}
		else
		{
			snd_serv(PUBMSG, (char *)&buf, len);
		}
	}

	snd_serv(SIGNOFF, 0, 0);
	sleep(1);
	close(sock);

	quit(0,"Have a nice day ...");
}

void init(void)
{
	/* So... als erste Routine im Programm wollen wir hier */
	/* erstmal sehen, ob die Umgebung ueberhaupt geeignet  */
	/* ist um Vchat auszufuehren sprich Screengroesse,     */
	/* Terminalemulation usw in Erfahrung bringen...       */

	extern int t_cols, t_rows;
	extern char *emulation;
	extern char nick[];
	extern char *getenv();
	extern int color;
#ifdef Linux
	struct winsize tsiz;
#else
	struct ttysize tsiz;
#endif
	struct passwd *pwf;
	struct hostent *ht;
	char *nk;

	if(!isatty(0) || !isatty(1))
	{
		fprintf(stderr, "%s: enter vchat interactive only.\n", prog_name);
		exit(1);
	}

	emulation = getenv("TERM");

	if((char *)strncmp(emulation, "vt1", 3) == NULL)
	{
		strcpy(csi, "\033[");
		color = 0;
	}
#ifdef Linux
	else if(strncmp(emulation, "console", 7) == 0)
	{
		strcpy(csi, "\033[");
#ifdef HAS_COLOR_CONSOLE
		color = 0xff;
#else
		color = 0;
#endif /* HAS_COLOR_CONSOLE */
	}
#endif

	else if((char *)strncmp(emulation, "vt2", 3) == NULL)
	{
		strcpy(csi, "\233");
		color = 0;
	}
	else if((char *)strncmp(emulation, "vt3", 2) == NULL)
	{
		strcpy(csi, "\233");
		color = 0;
	}
	else if((char *)strcmp(emulation, "xterm") == NULL)
	{
		strcpy(csi, "\033[");
		color = 0;
	}
	else if((char *)strcmp(emulation, "xcterm") == NULL)
	{
		strcpy(csi, "\033[");
		color = 0xff;
	}
	else if((char *)strcmp(emulation, "pc3") == NULL)
	{
		strcpy(csi, "\033[");
		color = 0xff;
	}
	else if((char *)strncmp(emulation, "ansi", 4) == NULL)
	{
		strcpy(csi, "\033[");
		color = 0xff;
	}
	else
	{
		fprintf(stderr, "%s: emulation '%s' not supported - sorry.\n",
			prog_name, emulation);
		exit(1);
	}

	/* So.... jetzt brauchen wir erstmal noch die   */
	/* Bildschirmgroesse des Terminals, um zu sehen */
	/* ob wir denn ueberhaupt genug Platz auf dem   */
	/* Bildschirm haben ...  :-) :-)                */

#ifdef Linux
	if(ioctl(0, TIOCGWINSZ, &tsiz) == 0)
	{
		t_cols = (tsiz.ws_col == 0) ? 80 : tsiz.ws_col;
		t_rows = (tsiz.ws_row == 0) ? 24 : tsiz.ws_row;
	}
#else
	if(ioctl(0, TIOCGSIZE, &tsiz) == 0)
	{
		t_cols = (tsiz.ts_cols == 0) ? 80 : tsiz.ts_cols;
		t_rows = (tsiz.ts_lines == 0) ? 24 : tsiz.ts_lines;
	}
#endif
	else
	{
		t_cols = 80;
		t_rows = 24;		
	}	

	/* Feststellen des Usernamens mittels UID und */
	/* passwd eintrag... 	                      */

	if((pwf = getpwuid(getuid())) == NULL)
	{
		perror(prog_name);
		exit(1);
	}	
	else
	{
		strcpy(user, pwf->pw_name);
		endpwent();
	}

	/* Jetzt schaun wir noch nach, ob im Environment */
	/* ein paar Variablen fuer uns gesetzt wurden :-)*/

	if(!(nk = getenv("NICK")))
	{
		strcpy(nick, user);
	}
	else
	{
		strcpy(nick, nk);
	}

	/* Den eigenen Hostnamen brauchen wir natuerlich */
	/* auch noch ...                                 */

	if(gethostname(myhost, 32) == -1)
	{
		perror(prog_name);
		exit(1);
	}

	if((ht = gethostbyname(myhost)) == NULL)
	{
		perror(prog_name);
		exit(1);
	}
	else
	{
		bcopy(ht->h_addr, &my_ip, ht->h_length);
	}

	/* Finden wir einen Server eintrag in den */
	/* Environments ?!                        */

	if(!(nk = getenv("VSERVER")))	
	{
		strcpy(svhost, myhost);
		bcopy(&my_ip, &sv_ip, sizeof(my_ip));
	}
	else
	{
		strcpy(svhost, nk);

		if((ht = gethostbyname(svhost)) == NULL)
		{
			perror(prog_name);
			exit(1);
		}
		else
		{
			bcopy(ht->h_addr, &sv_ip, ht->h_length);
		}
	}
}

void get_param(int argc, char *argv[])
{
	int opt;
	struct hostent *ht;
	struct in_addr tmpaddr;
	extern char *optarg;
	extern int optind;
	extern int opterr;
	extern int optopt;
	extern int reverse;
	extern int port;

	port = htons(SERVER_PORT);

	/* Hier erfolgt nun die Bearbeitung der */
	/* Commandline optionen                 */

	while((opt = getopt(argc, argv, ":i:n:p:rs:")) != -1)
	{
		switch(opt)
		{
#ifndef Linux
			case 'i':	if(inet_aton(optarg, &tmpaddr) == 1)
					{
						if((ht = gethostbyaddr((char *)&tmpaddr, sizeof(struct in_addr), AF_INET)) == NULL)
						{
							fprintf(stderr, "%s: unknown IP-address '%s'\n\n",
							prog_name, optarg);
							exit(1);
						}
						else
						{
							bcopy(ht->h_addr, &sv_ip, ht->h_length);
							strcpy((char *)&svhost, ht->h_name);
						}
					}
					else
					{
						usage("%s: invalid IP-address.", optarg);
						exit(1);
					}
					break;
#endif
			case 'n':	strcpy(nick, optarg);
					break;
			case 'p':	port = htons(atoi(optarg));
					break;
			case 'r':	reverse = 0xff;
					break;
			case 's':	if((ht = gethostbyname(optarg)) == NULL)
					{
						fprintf(stderr, "%s: unknown hostname '%s'.\n\n",
						prog_name, optarg);
						exit(1);
					}
					else
					{
						bcopy(ht->h_addr, &sv_ip, ht->h_length);
						strcpy((char *)&svhost, ht->h_name);
					}
					break;
			case ':':	usage("Option -%c requires an argument.", optopt);
					exit(1);
					break; 
			case '?':	usage("Unrecognized option: -%c", optopt);
					exit(1);
					break;

		}
	}
}

void usage(char *fmt, ...)
{
	va_list ap;

#ifdef SunOS
	va_start(ap);
#else
	va_start(ap, fmt);
#endif
	fprintf(stderr, "\n======================================================================\n");
	fprintf(stderr, "   VChat -- Interactive IP level chat system -- %s\n", VERSION);
	fprintf(stderr, "        (c) 1993/94 by mickey@deadline.uucp (Andreas S. Wetzel)\n");
	fprintf(stderr, "======================================================================\n\n");
	vfprintf(stderr, fmt, ap);
	fprintf(stderr, "\n\n");
	fprintf(stderr, "USAGE:    %s [-i <ip-addr>] [-n <nick>] [-p <port>]\n", prog_name);
	fprintf(stderr, "          %*s [-r] [-s <host>]\n\n", strlen(prog_name), " ");
#ifdef Linux
	fprintf(stderr, "OPTIONS:  -n <nick>      Set nickname to <nick>.\n");
#else
	fprintf(stderr, "OPTIONS:  -i <ip-addr>   Connect to VChat-server at <ip-addr>.\n");
	fprintf(stderr, "          -n <nick>      Set nickname to <nick>.\n");
#endif
	fprintf(stderr, "          -p <port>      Connect to VChat-server on TCP-port <port>.\n");
	fprintf(stderr, "          -r             Set reverse screen mode.\n");
	fprintf(stderr, "          -s <host>      Connect to VChat-server on <host>.\n");
	fprintf(stderr, "\n");
	
	va_end(ap);
}

void handle_cmd(char *cmd)
{
	char	*arg = &(cmd[3]);

	switch(cmd[1])
	{
		case 'b':	ring(arg);
				break;
		case 'c':	chchan(arg);
				break;
		case 'h':	help();
				break;
		case 'i':	invite(arg);
				break;
		case 'm':	prv_msg(arg);
				break;
		case 'n':	chnick(arg);
				break;
		case 'p':	snd_serv(PAGE, arg, sizeof(struct user));
				break;
		case 'r':	screen_init();
				break;
		case 's':	ulist(arg);
				break;
		case 't':	if(color)
				{
					if(strcmp(arg, "on") == 0)
					{
						cprintf(0, "* Text color emulation on.");
						txcol = 0xff;
					}
					else if(strcmp(arg, "off") == 0)
					{
						cprintf(0, "* Text color emulation off.");
						txcol = 0x0;
					}
					else
					{
						cprintf(0, "* No valid argument to command '.t'");
					}
				}
				else
				{
					cprintf(0, "* Color support not enabled.");
				}
				break;
		case 'v':	version_info();
				break;
		case 'w':	who(arg);
				break;
		default:	chat_beep();
				cprintf(0, "* No such command - type \".h\" to get help.");
				break;
	}
}

void quit(int stat, char *fmt, ...)
{
	va_list ap;

	extern int t_rows;
	extern char csi[];

	va_start(ap, fmt);

	signal(SIGWINCH, SIG_DFL);
	signal(SIGQUIT, SIG_DFL);
	signal(SIGINT, SIG_DFL);

	printf("%s0m",csi);
	printf("%s0;0r", csi);

	/* printf("\033c"); */
	mv(1,t_rows);
	vprintf(fmt, ap);
	printf("\n\n");

	tty_param(RESTORE);

	va_end(ap);

	exit(0);
}

void version_info(void)
{
	cprintf(0, "* VChat %c%s%c", YEL, VERSION, XXX);

	snd_serv(SND_ID, 0, 0);
}

void chnick(char *nick)
{
	struct nickname nk;

	bzero(&nk, sizeof(struct nickname));
	strncpy(nk.nick, nick, 31);

	snd_serv(CHNICK, (char *)&nk, sizeof(struct nickname));
}

void chchan(char *chan)
{
	struct channel ch;

	ch.chan = strtol(chan, NULL, 0);

	snd_serv(CHCHAN, (char *)&ch, sizeof(struct channel));
}

void help(void)
{
	extern char csi[];
	extern int t_rows;

	cprintf(3, "\n* %cVChat%c -- Interactive IP level Chat system -- %c%s%c",
		RED, XXX, YEL, VERSION, XXX);
	cprintf(4, "*");
	cprintf(4, "*  Commands are:");
	cprintf(4, "*  %cb%c <user[@host]>   - send chat request to <user>", BLU, XXX);
	cprintf(4, "*  %cc%c <chan>          - change channel to <chan>", BLU, XXX);
	cprintf(4, "*  %ch%c                 - you just found out what this does", BLU, XXX);
	cprintf(4, "*  %ci%c <nick>          - invite <nick> to join the channel you're on", BLU, XXX);
	cprintf(4, "*  %cm%c <nick> <msg>    - send private message <msg> to <nick>", BLU, XXX);
	cprintf(4, "*  %cn%c <nick>          - change nickname to <nick>", BLU, XXX);
	cprintf(4, "*  %cp%c <user[@host]>   - send chat request to <user> via mail", BLU, XXX);
	cprintf(4, "*  %cr%c                 - clear and redraw screen (in case of xmission errors)", BLU, XXX);
	cprintf(4, "*  %cs%c [* | <chan>]    - show who is chatting on <chan>", BLU, XXX);
	cprintf(4, "*                      * means your channel, default is all channels");
	cprintf(4, "*  %ct%c <on|off>        - toggle text color emulation on/off", BLU, XXX);
	cprintf(4, "*  %cv%c                 - show chat version information", BLU, XXX);
	cprintf(4, "*  %cw%c [host]          - show who is logged in on [host]", BLU, XXX);
	cprintf(4, "*                      default ist your host", BLU, XXX);
	cprintf(4, "*  %cx%c                 - exit from the chat system", BLU, XXX);
	cprintf(4, "*");
	cprintf(5, "* You may start commands with either '%c.%c', '%c/%c' or '%c:%c'\n",
		RED, XXX, RED, XXX, RED, XXX);
}

void invite(char *user)
{
	struct nickname nk;

	strcpy(nk.nick, user);

	snd_serv(INVITE, (char *)&nk, sizeof(struct nickname));
}

void prv_msg(char *dat)
{
	char nk[32];
	char msg[255];
	struct prv_message prv;

	if(sscanf(dat, "%s "
	"%[a-z A-Z0-9!\"$%&/()=?`'\\@+*~#-_.:,;<>|]", nk, msg) < 1)
	{
		cprintf(0, "* Missing argument(s)");
	}
	else
	{
		strcpy(prv.receiver, nk);
		strcpy(prv.msg, msg);

		snd_serv(PRVMSG, (char *)&prv, sizeof(struct prv_message));
	}


}

void ulist(char *dat)
{
	struct list lt;

	if(dat[0] == '*')	/* my channel */
	{
		lt.cmd = MY_CHAN;
		snd_serv(ULIST, (char *)&lt, sizeof(struct list));		
	}
	else if(strlen(dat) == 0)	/* default: all channels */
	{
		lt.cmd = ALL_CHAN;
		snd_serv(ULIST, (char *)&lt, sizeof(struct list));
	}
	else				/* Channel <chan> */
	{
		lt.cmd = CHAN;
		lt.chan = strtol(dat, 0, 0);
		snd_serv(ULIST, (char *)&lt, sizeof(struct list)); 
	}
}

void ring(char *dat)
{
	if(strlen(dat) == 0)
	{
		cprintf(0, "* Missing argument");
	}
	else
	{
		snd_serv(RING, dat, strlen(dat));
	}
}

void who(char *dat)
{
	if(strlen(dat) == 0)
	{
		snd_serv(WHO, NULL, 0);
	}
	else
	{
		snd_serv(WHO, dat, strlen(dat));
	}
}

