/*************************************************************************/
/*                  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.                            */ 
/*************************************************************************/

/****** includes ******/

#include "../config.h"
#include "../global.h"
#include <stdio.h>
#include <syslog.h>
#include <sys/socket.h>
#include "proto.h"

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

extern int errno;

char	our_host[32];		/* Our own hostname 		*/
struct in_addr our_ipaddr;	/* Our own IP-Address		*/
char	*prog_name;		/* The Program name		*/
int	sock;			/* descriptor of server socket 	*/
struct	sockaddr_in sock_in;	/* Socket structure		*/
int	clients = 0;		/* Number of clients active	*/
char	*cl_buf;		/* Buffer for client data	*/
int	logsw;			/* Logging enable/disable       */
char	x_usr[16];		/* Special privileged username */
char	x_host[32];		/* Special privileged user host*/

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

void main(int argc, char *argv[])
{
	/* Die Hauptschleife des ganzen :-)                          */
	/* Hier wird jeweils nachgesehen, ob weitere Clientprozesse  */
	/* um Verbindung ersucht haben, oder von bereits bestehenden */
	/* Clientprozessen inzwischen neue Daten empfangen wurden.   */

	extern int sock;
	fd_set	rfdset;
	fd_set	xfdset;
	int i, x;
	long cn;
	struct client_data *cl_ptr;
	char buffer[SVMSGBUF];
	struct serv_msg svmsg;
	prog_name = argv[0];

	init(argc, argv);

	for(;;)
	{
		FD_ZERO(&rfdset);
		FD_ZERO(&xfdset);
		FD_SET(sock, &rfdset);

		for(i=0; i<clients; i++)
		{
			cl_ptr = cl_addr(i);

			FD_SET(cl_ptr->fd, &rfdset);
			FD_SET(cl_ptr->fd, &xfdset);
		}

		if(select(FD_SETSIZE, &rfdset, (fd_set *) 0, &xfdset, 0) < 0)
		{
			log(LOG_ERR, "select: %m");
			exit(1);
		}

		if(FD_ISSET(sock, &rfdset))
		{
			assign_client();
		}

		for(i=0; i<clients; i++)	/* check if clients have data */
		{
			cl_ptr = cl_addr(i);

			if(FD_ISSET(cl_ptr->fd, &xfdset))
			{
				log(LOG_WARNING, "WARNING: Removing client ($%x -- %s@%s [%s]) due to error condition on socket.",
					cl_ptr->fd, cl_ptr->user,
					cl_ptr->host, inet_ntoa(cl_ptr->ip_addr));
				delete_client(cl_ptr->fd);
			}

			if(FD_ISSET(cl_ptr->fd, &rfdset))
			{
				for(x=0; x<SVMSGBUF; x++)
					buffer[x] = '\0';

				if((cn = read(cl_ptr->fd, &svmsg, sizeof(struct serv_msg))) == sizeof(struct serv_msg))
				{
					if(svmsg.len)
						read(cl_ptr->fd, &buffer, svmsg.len);

					handle_cmd(svmsg.cmd, cl_ptr, (char *)&buffer);
				}
				else if(cn <= 0)
				{
					log(LOG_WARNING, "WARNING: Broken pipe on client $%x (%s@%s)",
						cl_ptr->fd, cl_ptr->user,
						cl_ptr->host);
					err_signoff(cl_ptr);
				}
				else
				{
					log(LOG_WARNING, "received garbled command packet ($%x bytes)", cn);
					flush_buf(cl_ptr->fd);
				}
			}
		}

#ifdef	VSERVER_INETD
		if(clients <= 0)
			break;
#endif
	}

	close(sock);
	free(cl_buf);
}

void handle_cmd(int cmd, struct client_data *cl, char *data)
{
	/* Hier wird das command des clients ausgewertet     */
	/* und in die entsprechenden Verarbeitungsroutinen   */
	/* verzweigt, oder eine Fehlermeldung zurueckgegeben */
	/* wenn wir das command nicht behandeln koennen.     */

	switch(cmd)
	{
		case SIGNON:	signon(cl);
				break;
		case SIGNOFF:	signoff(cl);
				break;
		case CHNICK:	chnick(cl, data);
				break;
		case CHCHAN:	chchan(cl, data);
				break;
		case PRVMSG:	prvmsg(cl, data);
				break;
		case PUBMSG:	pubmsg(cl, data);
				break;
		case INVITE:	invite(cl, data);
				break;
		case SND_ID:	snd_id(cl);
				break;
		case ULIST:	ulist(cl, data);
				break;
		case PAGE:	page(cl, data);
				break;
		case RING:	ring(cl, data);
				break;
		case WHO:	who(cl, data);
				break;
		default:	snd_svmsg(cl->fd, "* Command byte %c$%x%c not recognized by server\n",
					RED, cmd, XXX);
				log(LOG_NOTICE, "Client process sent unknown command byte ($%x).", cmd);
				break;
	}
}
