/*
 * Copyright (c) 1996-2007, OpenFWTK Development Group
 * All rights reserved. See LICENSE.
 */

/*
    cvs-gw  proxy for cvs pserver protocol
    see:	
    http://rai16.informatik.uni-stuttgart.de/doc/cvs/html-cvsclient/cvsclient_toc.html	
    for detailed protocol description
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <signal.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include "firewall.h"
#include "firewall2.h"
#include "fwfunc.h"

#ifndef MIN
# define MIN(a, b) (((a) > (b)) ? (b) : (a))
#endif

static int extendperm  = 0;
static int transparent = 0;

static	unsigned	int	user_limit = 31;
static	unsigned	int	pass_limit = 31;
static	unsigned	int	retr_file_limit = 0; /* default: unlimited */
static	unsigned	int	send_file_limit = 0; /* default: unlimited */
static	char		mode[MAX_STR] = "ro";


static  char	separator[1] = "$";
static	char	**validests = (char **)0;
static	char	**validusers = (char **)0;

extern  struct  fwstats proxy_stats;
extern  Cfg*    proxy_confp;
static  Cfg     *confp;
static	int	cdscp = 0;
static	int	sdscp = 0;




extern void proxy_parse_options(Cfg *cf, fwparm options[]);
extern Cfg* proxy_init(int ac, char *av[]);
extern void proxy_exit(void);
extern void proxy_update_operation(char *op);
extern int get_resp (int server_fd, int client_fd, int flag, unsigned int op_type);
extern void resolve_flags (int server_fd, int client_fd, char *command, int flags, int argc, char **argv);
extern int auth_perm(Cfg*, char*, char*, char*, char**);
extern int conn_server(char *srv, int portnum, int priv, char *rbuf);
extern int enargv(char    *buf, char    **av, int     avsiz, char    *tobuf, int     bsiz);
extern  time_t dgets_timeout(int sec);

/*Control flags*/
#define	MLINE		00020 /* Waiting for multiline response				*/
#define	MNEXTLINE	00040 /* Waiting for undeclareted command from client at the next line*/
#define	FTRANSMISSION	00100 /* deam! another shit with lame code =) 			*/
#define	ENDRESPONSE	00200 /* server response is done					*/
#define	PARTPATHNAME	00400 /* pathname to files in responses contains two parts (path in
					              repository and obsolute server path)	*/
#define	NEWENTRIESLINE	01000 /* Some as MNEXLINE						*/
#define	CHECKFLAGS	02000 /* Allowing or Disallowing some server responses		*/
#define	OP_INTERNAL	04000 /* This one for internal use only*/

int get_response(int server_fd, int flags);
int ok_resp( char *buff);
int make_file_transmission(int client_fd, int server_fd, int flags, char **argv);
void pre_check(int op_type, char *command, int flags, int argc, char **argv);

static void handle_term(int);
static int server_fd;

/*Usefull flags*/
#define	SERVER_COMMANDS		0100000
#define	CLIENT_COMMANDS		0010000

#define	PSERVERPORT	2401
#define	KSERVERPORT	1999 /* Not emplimented yet by the promlems with kerberos					*/

#define	AUTHFLDS	5    /* Number of fields in auth section							*/
#define	MAXCOMFILESZ	1024 /* Maximum size of the single file what user can put in cvs-repository (IN kBytes)	*/
#define	MAXBUFSZ	2048
#define	MAXLINELEN 	1024
#define	MAXRESP  	1024  /* Maximum size of user command							*/
#define	MAXCMDNUM	64

#define	RESPONSE	1
#define	REQUEST		0

/*some definitions for so library*/
#define	SO_LNONE		0
#define	SO_LLF			3


					     	/* Auth fields, is it required in further in logs?		*/
char    authflds[AUTHFLDS][MAXLINELEN];		
/* 1 - is cvs path					*/
/* 2 - is username					*/
/* 3 - is password [Not needed at this time,		*/
/* (trivially encoded by Password scrambling algorithm),*/
/* but who knows? =)]					*/
/*0 & 4 is auth tags					*/

char syslog_msg[MAXBUFSZ];
char client_msg[MAXBUFSZ];


static void
close_all()
{
	if(strlen(client_msg))
		sosayn(0, client_msg, MIN(MAXLINELEN - 1,strlen(client_msg)));
	else {
		bzero(client_msg, sizeof(client_msg));
		snprintf(client_msg, MAXBUFSZ, 
			"E Some problems with client-proxy-server connection\n"\
			"E serv:port - %.100s:%hd\n" \
			"error 0\n", proxy_stats. dst,proxy_stats.port);
		sosayn(0, client_msg, MIN(MAXLINELEN - 1,strlen(client_msg)));
	}

	if(*syslog_msg)
		syslog(LLEV, "%.256s", syslog_msg);
	else {
		syslog(LLEV, "going down on internal error: %s", strerror(errno));
	}
	if(server_fd > 0)
		close(server_fd);
	close(0);
	proxy_exit();
}

static void
handle_term (sig)
	int sig;
{
	bzero(syslog_msg, sizeof(syslog_msg));
	snprintf(syslog_msg, MAXBUFSZ, "going down on signal SIGTERM");
	close_all();
	proxy_exit();
}

static int
conn_cvs_server(buff)
char *buff;
{
	int server_fd = -1;
	char *ptr, *s, *t;
	char server[sizeof(proxy_stats.dst)];
	int  port = PSERVERPORT;
	int  have_error = 0;    /*Flag for error configuration data	*/
	char username[MAXLINELEN];
	char authline[MAXLINELEN];

	bzero(username, sizeof(username));


	/*Take out username, server and port(? if exist the last one)*/
	if (!(*proxy_stats.dst)) {
		bzero(authline, sizeof(authline));
		bzero(server, sizeof(server));
		strncpy(authline, authflds[2], MIN(MAXLINELEN, strlen(authflds[2])));
		s = authline;

		if((t = index(authline, separator[0])) != NULL)
			if((ptr = strtok(s, separator)) != NULL)
				strncpy(username, ptr, sizeof(username) - 1);


		if(strlen(username) && (t = index(authline + strlen(username) + 1, separator[0])) != NULL) {
			if((ptr = strtok(NULL, separator)) != NULL) {
				strncpy(server, ptr, 64);
				if((ptr = strtok(NULL, "@")) != NULL)
					port = atoi(ptr);
				else
					have_error = 1;
			} else
				have_error = 2;
		} else {
			if((ptr = strtok(NULL, separator)) != NULL)
				strncpy(server, ptr, 64);
			 else
				have_error = 3;
		}
	} else {
		strncpy(username, authflds[2], sizeof(username) -1);
	}

	if(have_error) {
		snprintf(syslog_msg, MAXBUFSZ, "cannot parse CVSROOT data %.100s:%hd - %s", proxy_stats.dst,proxy_stats.port,strerror(errno));
		snprintf(client_msg, MAXBUFSZ, "E Fatal error, aborting.\n" \
				"E Check yours CVSROOT, can't connect server:port %.100s:%hd\n" \
				"E Username = %.20s Server=%.20s\n" \
				"error 0\n",
					proxy_stats.dst, proxy_stats.port,
					username, server);
		close_all();
	}

	if(strlen(username) > user_limit) {
		snprintf(syslog_msg, MAXBUFSZ, "Username \"%.10s\"length too long, max %hd", username, user_limit);
		close_all();
	}

	if(strlen(authflds[3]) > pass_limit) {
		snprintf(syslog_msg, MAXBUFSZ, "Encoded password length too long, max %hd", pass_limit);
		close_all();
	}

	if (searchlist(username,validusers)) {
		snprintf(client_msg, MAXBUFSZ, "E access denied\n" \
					       "error 0\n");
		snprintf(syslog_msg, MAXBUFSZ, "deny host=%.512s/%.20s" \
			" access for user %s(access for this user denied)",
			proxy_stats.rladdr, proxy_stats.riaddr, username);
		close_all();
	}

	if (!(*proxy_stats.dst)) {
		strlcpy(proxy_stats.dst, server, sizeof(proxy_stats.dst));
		proxy_stats.port = port;
	}

	if (proxy_check_dest(validests, extendperm)) {
		snprintf(client_msg,MAXBUFSZ,"E CVS gateway denied destination access\n" \
					"error 0\n");
		snprintf(syslog_msg, MAXBUFSZ,	"deny host=%.512s/%.20s" \
			 "access for user %s(access to destination host denied)",
			 proxy_stats.rladdr, proxy_stats.riaddr, username);
		close_all();
	}

	/*Building auth message for cvs-server*/
	snprintf(buff, 256, "%.255s\n"	\
			"%.1024s\n"		\
			"%.1024s\n"		\
			"%.1024s\n"		\
			"%.255s",
				authflds[0],
				authflds[1],
				username,
				authflds[3],
				authflds[4]
		);
	/*Setting up connection with cvs-server*/

	proxy_update_operation("CONNECTING");

	if((server_fd = conn_server(proxy_stats.dst,proxy_stats.port,0,(char *)0)) < 0) {
		snprintf(syslog_msg, MAXBUFSZ, "cannot connect to server [%s:%hd] %s",
			 proxy_stats.dst, proxy_stats.port, strerror(errno));
		snprintf(client_msg, MAXBUFSZ, "E Fatal error, aborting.\n" \
			 "E can't connect to the server %.100s:%hd\nerror 0\n",
			 proxy_stats.dst, proxy_stats.port);
		close_all();
	}
	return(server_fd);
}


int
do_noargs(server_fd, client_fd, command, flags, argc, argv)
int 	server_fd;
int     client_fd;
char 	*command;
int 	flags;
int 	argc;
char 	**argv;
{
	if (argc != 1) {
		return 1;
	} else {
		if(sosayn(server_fd, argv[0], strlen(argv[0])))
			return -1;
		if (server_fd)
			proxy_stats.outbytes += strlen(argv[0]) + 1;
		else
			proxy_stats.inbytes += strlen(argv[0]) + 1;
		resolve_flags (server_fd, client_fd, command, flags, argc, argv);
		return(0);
	}
}

/*
 *	Handle cvs commands that take single message number as argument
*/

int
do_message(server_fd, client_fd, command, flags, argc, argv)
int 	server_fd;
int client_fd;
char 	*command;
int 	flags;
int 	argc;
char 	**argv;
{
	char			buff[MAXBUFSZ];

	bzero(buff,sizeof(buff));

	if (argc != 2) {
		syslog(LLEV,"command syntax error: %.100s", command);
		snprintf(buff,MAXBUFSZ, "E syntax error\nE cmd:%.100s\nerror", 
			 command);
		if(sosayn(0, buff,strlen(buff)))
			return(-1);

		return(1);
	} else {
  		snprintf(buff, MAXBUFSZ - 1, "%.100s %.100s", argv[0], argv[1]);
		if(sosayn(server_fd, buff, strlen(buff)))
			return(-1);
		if (server_fd)
			proxy_stats.outbytes += strlen(buff) + 1;
		else
			proxy_stats.inbytes += strlen(buff) + 1;

		resolve_flags (server_fd, client_fd, command, flags, argc, argv);
	}
	return(0);
}


/*
 *	Handle generic cvs command - no advanced parsing here 
 */

int
do_generic(server_fd, client_fd, command, flags, argc, argv)
int 	server_fd;
int client_fd;
char 	*command;
int 	flags;
int 	argc;
char 	**argv;
{
	char *p;
	p = command;
	if(sosayn(server_fd, command, strlen(command)))
		return(-1);

	if (server_fd)
		proxy_stats.outbytes += strlen(command) + 1;
	else
		proxy_stats.inbytes += strlen(command) + 1;

	resolve_flags (server_fd, client_fd, command, flags, argc, argv);
	return(0);
}

/*
 *	Handle commands that take filename as parameter (no advanced
 *	parsing implemented yet, but here is the place to do)
 */

int
do_path(server_fd, client_fd, command, flags, argc, argv)
int 	server_fd;
int client_fd;
char 	*command;
int 	flags;
int 	argc;
char 	**argv;
{
	char *p;
	p = command;
	if(sosayn(server_fd, command, strlen(command)))
		return(-1);

	if (server_fd)
		proxy_stats.outbytes += strlen(command) + 1;
	else
		proxy_stats.inbytes += strlen(command) + 1;
	resolve_flags (server_fd, client_fd, command, flags, argc, argv);
	return(0);
}

/*
 *	Handle commands that take relative filename as parameter 
 */

int
do_rpath(server_fd, client_fd, command, flags, argc, argv)
int 	server_fd;
int client_fd;
char 	*command;
int 	flags;
int 	argc;
char 	**argv;
{
	char *p;
	p = command;
	if(sosayn(server_fd, command, strlen(command)))
		return(-1);

	if (server_fd)
		proxy_stats.outbytes += strlen(command) + 1;
	else
		proxy_stats.inbytes += strlen(command) + 1;
	resolve_flags (server_fd, client_fd, command, flags, argc, argv);
	return(0);
}

/*
 *	Handle commands that take Entry line as parameter (no advanced
 *	parsing implemented yet, but here is the place to do)
 */

int
do_entry(server_fd, client_fd, command, flags, argc, argv)
int 	server_fd;
int client_fd;
char 	*command;
int 	flags;
int 	argc;
char 	**argv;
{
	char *p;
	p = command;
	if(sosayn(server_fd, command, strlen(command)))
		return(-1);

	if (server_fd)
		proxy_stats.outbytes += strlen(command) + 1;
	else
		proxy_stats.inbytes += strlen(command) + 1;
	resolve_flags (server_fd, client_fd, command, flags, argc, argv);
	return(0);
}

/*
 *	Handle commands that require no response
 */

int
do_noresponse(server_fd, client_fd, command, flags, argc, argv)
int server_fd;
int client_fd;
int flags;
char *command;
int argc;
char **argv;
{
	if(sosayn(server_fd, command, strlen(command)))
		return(-1);

	if (server_fd)
		proxy_stats.outbytes += strlen(command) + 1;
	else
		proxy_stats.inbytes += strlen(command) + 1;
	resolve_flags (server_fd, client_fd, command, flags, argc, argv);
	return(0);
}

/*
 *	Handle restricted and unemplemented commands
 */

int
do_restricted(server_fd, client_fd, command, flags, argc, argv)
int server_fd;
int client_fd;
int flags;
char *command;
int argc;
char **argv;
{
	syslog(LLEV,"restricted command: %.128s",command);
	sosay(0,"E operation restricted\nerror 0\n");
	return(0);
}

#include "responses.h"
#include "requests.h"


static fwparm	options[] = {
	{FWPARM_LIST,	"-dest",		(char*) &validests},
	{FWPARM_LIST,	"-user",		(char*) &validusers},
	{FWPARM_CHAR,	"-separator",		(char*) &separator},
	{FWPARM_BOOL,	"-transparent",		(char*) &transparent},
	{FWPARM_BOOL,	"-extnd",		(char*) &extendperm},
	{FWPARM_STRING,	"-plug-to",		(char*) proxy_stats.dst},
	{FWPARM_STRING,	"-mode",		(char*) mode},
	{FWPARM_PORT,	"-port",		(char*) &proxy_stats.port},
        {FWPARM_INT,	"-retr-file-limit",	(char*) &retr_file_limit},
        {FWPARM_INT,	"-send-file-limit",	(char*) &send_file_limit},
	{FWPARM_OPLOG,	"-log-response",	(char*) &cvs_gw_res_descr},
	{FWPARM_OPDENY,	"-deny-response",	(char*) &cvs_gw_res_descr},
	{FWPARM_OPLOG,	"-log-request",		(char*) &cvs_gw_req_descr},
	{FWPARM_OPDENY,	"-deny-request",	(char*) &cvs_gw_req_descr},
	{FWPARM_DSCP,   "-client-dscp",         (char*) &cdscp},
	{FWPARM_DSCP,   "-server-dscp",         (char*) &sdscp},
	{0,	0,		0}
};

int get_resp(server_fd, client_fd, flags, op_type)
	int server_fd;
	int client_fd;
	int flags;
	unsigned int op_type;
{
	char 		buff[MAXBUFSZ];
	int 		size = 0;
	int 		f_res_nextline = 0;

	char    tokbuf[MAXBUFSZ];
	char    *tokav[56];
	int     tokac;


	if(!((op_type == REQUEST) || (op_type == RESPONSE)))
		return(-1);

	while(1) {
		bzero(buff, sizeof(buff));
		sotimeout(600L);
		if((size = sogets(server_fd, buff, sizeof(buff)))== -1) {
			break;
		} else {

			int 				msg_int;
			const 	struct	hash_entry	*he_resp;
			int 				(*handler)(int, int, char*,int,int,char**);
			buff[size - 1] = '\0';
			tokac = 0;
			bzero(tokbuf, sizeof(tokbuf));
			bzero(tokav,  sizeof(tokav));
			tokac = enargv(buff, tokav, 56, tokbuf, sizeof(tokbuf));

			if(!tokav[0])
				continue;

			if(op_type == REQUEST)
				he_resp = find_vhe2(&cvs_gw_req_descr, tokav[0], strlen(tokav[0]));
			else
				he_resp = find_vhe2(&cvs_gw_res_descr, tokav[0], strlen(tokav[0]));

			if (!he_resp) {
				if(f_res_nextline) {
#ifdef	DEBUG
					syslog(LLEV,"%.20s:%.4s@%.20s: cmd[not-parsed] %.100s", proxy_stats.rladdr, authflds[1], proxy_stats.dst, buff);
#endif
					do_generic(client_fd, server_fd, buff, OP_LOG, tokac,tokav);
				} else {
					syslog(LLEV,"unknown cvs command %.100s", tokav[0]);
					sosay(0,"E command unknown or out of state\nerror 0\n");
					continue;
				}
			} else {
				f_res_nextline = 0;
				proxy_update_operation(buff);

				if (he_resp->flags & MNEXTLINE)
					f_res_nextline = 1;

				if (he_resp->flags & OP_LOG)
					syslog(LLEV,"%.20s:%.4s@%.20s: cmd %.100s", proxy_stats.rladdr, authflds[2], proxy_stats.dst, buff);

				if (he_resp->flags & OP_DENY) {
					syslog(LLEV,"deny host=%.512s/%.20s operation %.100s", proxy_stats.rladdr, proxy_stats.riaddr, buff);
					sosay(0,"E operation denied\nerror 0\n");
					continue;
				}

				if(extendperm && (he_resp->flags & OP_XTND)) {
					msg_int = auth_perm(proxy_confp, proxy_stats.authuser, "cvs-gw", proxy_stats.riaddr, tokav);
					if(msg_int == 1 || msg_int == -1) {
						syslog(LLEV,"deny host=%.512s/%.20s operation %.100s - extended permissions",proxy_stats.rladdr,proxy_stats.riaddr,buff);
						sosay(0,"E operation denied\nerror 0\n");
						continue;
					}
				}

				handler = he_resp->handler;
				if(op_type == REQUEST) {
					assert(CVS_GW_REQ_HANDLER (handler));
				}
				else {
					assert(CVS_GW_RES_HANDLER (handler));
				}
/*				(void)pre_check(op_type, buff, he_resp->flags, tokac, tokav);
 */
				(void)handler(client_fd, server_fd, buff, he_resp->flags, tokac,tokav);
				if (he_resp->flags & ENDRESPONSE)
					return(0);

			}
		}
	}
return(0);
}


int
make_file_transmission(client_fd, server_fd, flags, argv)
int client_fd;
int server_fd;
int flags;
char **argv;
{
	int i = 0;
	int rd = 0;
	unsigned int fsz = 0,
		    count = 0;

	char rline[MAXLINELEN],
		buff[MAXBUFSZ];

	bzero(buff, sizeof(buff));
	bzero(rline, sizeof(rline));
	/*Read file mode and send it to the cvs-server	*/
	if((rd=sogets(client_fd, rline, sizeof(rline))) != -1) {
		rline[rd -1] = '\0';
		if(sosayn(server_fd, rline, strlen(rline)))
			return(-1);

	/*Read file size					*/
		bzero(rline, sizeof(rline));
		if((rd = sogets(client_fd, rline, sizeof(rline)))) {
			rline[rd -1] = '\0';
			if((fsz = atoi(rline))) {
				if((fsz > retr_file_limit) && client_fd && retr_file_limit) {
					snprintf(syslog_msg, MAXBUFSZ, "retr file size limit exceeded");
					close_all();
                                }
				if((fsz > send_file_limit) && !client_fd && send_file_limit) {
					snprintf(syslog_msg, MAXBUFSZ, "send file size limit exceeded");
					close_all();
				}

				if (flags & OP_LOG)
					syslog(LLEV, "file in '%.100s' size is '%d' bytes", argv[0], fsz);
				if(sosayn(server_fd, rline, strlen(rline))) {
					return -1;
				}
	/*Lets do it!					*/
				count = fsz;
				rd = 0;
				i  = 0;
				while((rd < fsz) && (count)) {
					if(((i = soread(client_fd, buff, MIN(sizeof(buff), count))) <= 0)) {
						snprintf(client_msg, MAXBUFSZ,
							"E Client connection error\n" \
							"E serv:port - %.100s:%hd\n" \
							"error 0\n", proxy_stats. dst,proxy_stats.port);
						snprintf(syslog_msg, MAXBUFSZ, "Client connection failed, error: %s", strerror(errno));
						close_all();
						exit(2);
					}

					if((sowrite(server_fd, buff, i) != i)) {
						snprintf(client_msg, MAXBUFSZ,
							"E Server connection error\n" \
							"E serv:port - %.100s:%hd\n" \
							"error 0\n", proxy_stats. dst,proxy_stats.port);
						snprintf(syslog_msg, MAXBUFSZ, "Server connection failed, error: %s", strerror(errno));
						close_all();
					}
					if (client_fd)
						proxy_stats.inbytes += i;
					else
						proxy_stats.outbytes += i;
					rd += i;
					count -=i;
					bzero(buff, sizeof(buff));
					proxy_update_status();
				}
				(void) sosetline(SO_LLF);
				if(count != 0)
					close_all();
			} else {
				return 0;
			}
		} else{
			return -1;
		}
	} else {
		return -1;
	}
	return 1;
}


static int
read_client_auth(descr)
	int  descr;
{
	int	rd  = -1,
		sz  = 0,
		i   = 0;
	char     tmp[MAXLINELEN];
	while(i < AUTHFLDS) {
		bzero(tmp, sizeof(tmp));
		bzero(authflds[i], sizeof(authflds[i]));
		if((rd = sogets(descr, tmp, sizeof(tmp))) <= 0)
			return 0;
		tmp[rd - 1] = '\0';
		strncpy(authflds[i++], tmp, MIN(MAXLINELEN, rd));
		sz += rd;
	}
	return sz;
}

static int
auth_response(response)
	char *response;
{

#define ASW_SZ 10

	char secc[ASW_SZ];
	char deny[ASW_SZ];
	strncpy(secc, "I LOVE YOU", ASW_SZ);
	strncpy(deny, "I HATE YOU", ASW_SZ);

	if(!(strncmp(response, secc, ASW_SZ)))
		return 1; /*Auth is successful				*/
	if(!(strncmp(response, deny, ASW_SZ)))
		return 0; /*Access denied				*/
	return -1;	  /*Unknown answer - closing connection anyway	*/
}

int
check_commands (flag, argc, argv)
int flag;
int 	argc;
char 	**argv;
{
	int i = 0;
	unsigned int count = 0;
	struct 	hash_descr	*descr = NULL;
	struct 	hash_entry	*he;

	if(argc < 5) {
		syslog(LLEV,"CHECK IS FAILED $argc is too short");
		return 0;
	}

	if(flag & SERVER_COMMANDS) {
		descr = (struct hash_descr *)&cvs_gw_req_descr;
		count = sizeof(cvs_gw_req_data)/(sizeof(struct hash_entry));
	} else {
		if(flag & CLIENT_COMMANDS) {
			descr = (struct hash_descr *)&cvs_gw_res_descr;
			count = sizeof(cvs_gw_res_data)/(sizeof(struct hash_entry));
		} else {
			snprintf(syslog_msg,MAXBUFSZ,"unknown cvs-gw internal error");
			snprintf(client_msg,MAXBUFSZ,"E internal proxy error\nerror 0\n");
			close_all();

		}
	}

	while(count--) {

		if((he = (struct hash_entry *)&descr->data[count])) {
			if(he->flags & OP_DENY)
				he->flags |= OP_INTERNAL;
			else
				he->flags |= OP_DENY;
		}
	}

	/* Allow a bit*/
	while(argc - 1> i++) {
		if ((he = find_vhe2(descr, argv[i], strlen(argv[i])))) {
			if(!(he->flags & OP_INTERNAL))
				he->flags ^= OP_DENY;
			else
				he->flags ^= OP_INTERNAL;
		} else
			syslog(LLEV,"warning: unknown command %.100s",argv[i]);

	}
	return 0;
}

void
init_readonly(void)
{
	/*List of commands, must grad read-only mode       */
        /*Not sure that this list is full, what i missed???*/
        int i = 0;
	unsigned int count;
	struct 	hash_descr	*descr;
	struct 	hash_entry	*he;
        char deny_commands[][20] = {
				{"ci"},
                                {"tag"},
                                {"admin"},
                                {"watchers"}, 
                                {"editors"},  
                                {"annotate"}, 
                                {"rtag"},
                                {"rannotate"},
                                {"add"},
                                {"remove"},
                                {"watch-on"},
                                {"watch-off"},
                                {"watch-add"},
                                {"watch-remove"},
                                {"release"},
                                {"other-request"},
                                {"import"},
                                {0},
                                };
	descr = (struct hash_descr *)&cvs_gw_req_descr;
	count = sizeof(cvs_gw_req_data)/(sizeof(struct hash_entry));

	for(i=0;*deny_commands[i];i++) {
		if ((he = find_vhe2(descr, deny_commands[i], strlen(deny_commands[i])))) {
			he->flags |= OP_DENY;
                        syslog(LLEV, "REQUEST command[%d] \"%.20s\" is denied",i, deny_commands[i]);
		} else {
			syslog(LLEV, "Can not find such REQUEST command[%d] \"%.20s\", denied" \
				"action have no affect", i,deny_commands[i]);
                }
	}
}


void
resolve_flags (server_fd, client_fd, command, flags, argc, argv)
int	server_fd;
int	client_fd;
char	*command;
int	flags;
int	argc;
char	**argv;
{
	char 		buff[MAXBUFSZ];
	bzero(buff,sizeof(buff));

	if (!flags)
		return;
	if(flags & CHECKFLAGS) {
		if(client_fd)
			check_commands (SERVER_COMMANDS, argc, argv);
		else
			check_commands (CLIENT_COMMANDS, argc, argv);
	}
	if(flags & PARTPATHNAME) {
		bzero(buff, sizeof(buff));
		sogets(client_fd, buff, sizeof(buff));
		buff[strlen(buff) - 1] = '\0';
		sosayn(server_fd, buff, strlen(buff));
	}

	if(flags & NEWENTRIESLINE) {
		bzero(buff, sizeof(buff));
		sogets(client_fd, buff, sizeof(buff));
		buff[strlen(buff) - 1] = '\0';
		if (flags & OP_LOG)
			syslog(LLEV,"New Entry tag %s", buff);
		sosayn(server_fd, buff, strlen(buff));
	}

	if(flags & MLINE) {
		if(client_fd)
			get_resp(server_fd, client_fd, flags, REQUEST);
		else
			get_resp(server_fd, client_fd, flags, RESPONSE);
	}

	if(flags & FTRANSMISSION) {
		make_file_transmission(client_fd, server_fd, flags, argv);
	}

	return;
}

int
main(argc, argv)
	int argc;
	char *argv[];
{
	char    buf[MAXBUFSZ+1];

	int	 size          = 0,
		 i             = 0,
		 auth          = 0;
	struct sigaction act;
        char *s_mode;
	bzero(syslog_msg, sizeof(syslog_msg));
	bzero(client_msg, sizeof(client_msg));
	bzero((char *)&act, sizeof(act));
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaddset(&act.sa_mask, SIGTERM);
	act.sa_handler = handle_term;
	sigaction(SIGTERM, &act, NULL);
	(void) sosetline(SO_LLF);

	proxy_stats.port = 0;
	proxy_init(argc, argv);
	proxy_chroot_setugid();
	if (cdscp)
		 proxy_set_dscp(0,cdscp);

	proxy_stats.inbytes  = 0;
	proxy_stats.outbytes = 0;
	if((confp = proxy_conf_hosts(proxy_confp,proxy_stats.rladdr,proxy_stats.riaddr)))
		proxy_parse_options(confp, options);
	else {
		snprintf(buf,MAXBUFSZ,"E Access denied\nerror 0\n");
		sosayn(0, buf, strlen(buf));
		proxy_exit();
	}

	if(!proxy_stats.port)
		proxy_stats.port = PSERVERPORT;

	s_mode = proxy_conf_string(proxy_confp, "mode");
	if (s_mode)
		(void)strncpy(mode, s_mode, sizeof(mode));

	if(!strncmp(mode,"ro",sizeof(mode))) {
		(void)init_readonly();
	} else {
		if(strncmp(mode,"rw",sizeof(mode)))
			syslog(LLEV, "fwtkcfgerr: bad \"mode\" option in config");

        }

	user_limit = proxy_conf_int(proxy_confp,"user-limit",1,MAXLINELEN-1,user_limit);
	pass_limit = proxy_conf_int(proxy_confp,"pass-limit",1,MAXLINELEN-1,pass_limit);

	bzero(buf, sizeof(buf));

/*AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH */
/*AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH */
/*AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH AUTH */

	sotimeout(PROXY_TIMEOUT);
	if((size = read_client_auth(0)) == -1) {
		snprintf(syslog_msg, MAXBUFSZ, "Client connection error: %s", strerror(errno));
		close_all();
	}
	proxy_stats.inbytes  += size;
	bzero(buf, sizeof(buf));
	server_fd = conn_cvs_server(buf);

	if (sdscp)
		proxy_set_dscp(server_fd,sdscp);

	proxy_update_operation("AUTHENTICATION");

	if(sosayn(server_fd, buf, strlen(buf))) {
		snprintf(syslog_msg, MAXBUFSZ, "Server write error: %s", strerror(errno));
		snprintf(client_msg, MAXBUFSZ, "E Server write error\n" \
				"E serv:port - %.100s:%hd\n" \
			 	"error 0\n", proxy_stats. dst,proxy_stats.port);
		close_all();
	}

	bzero(buf, sizeof(buf));

	if((size = soread(server_fd, buf, 1024)) == -1) {
		snprintf(syslog_msg, MAXBUFSZ, "Server read error");
		snprintf(client_msg, MAXBUFSZ, "E Server read error\n" \
			"E serv:port - %.100s:%hd\n" \
			"error 0\n", proxy_stats. dst,proxy_stats.port);
		close_all();
	}

	if(size && (auth = auth_response(buf)) > 0) {
		buf[size - 1] = '\0';
		if(sosayn(0, buf, size)) {
			snprintf(syslog_msg, MAXBUFSZ, "Client write error: %s",strerror(errno));
			close_all();
		}
		proxy_stats.outbytes  += size;
		bzero(buf, sizeof(buf));
	} else {
		if(!size) {
			snprintf(syslog_msg, MAXBUFSZ, "Server is silent");
			snprintf(client_msg, MAXBUFSZ, "E No answer from server\n" \
				"E serv:port - %.100s:%hd\n" \
				"error 0\n", proxy_stats. dst,proxy_stats.port);
			close_all();
		} else {
			if(auth == 0) {
				snprintf(syslog_msg, MAXBUFSZ, "user failed to authenticate to server");
				strlcpy(client_msg, buf, sizeof(client_msg));
				proxy_stats.outbytes  += size;
				close_all();
			} else {
				snprintf(syslog_msg, MAXBUFSZ, "Can't undestand srv response: %.100s", buf);
				strlcpy(client_msg,"I HATE YOU\n", 11);
				proxy_stats.outbytes  += 11;
				close_all();
			}
		}
	}
/*	ENDAUTH											*/


	i = 0;

	if(fcntl(server_fd, F_GETFL, O_NONBLOCK) != O_NONBLOCK)
		if(fcntl(server_fd, F_GETFL, O_NONBLOCK) == -1)
			syslog(LLEV,"setting up the fd_flag for server_fd fd failed");
	proxy_update_operation("IDLE");
	get_resp(0, server_fd, 0, REQUEST);
	proxy_exit();
	return 0;
}
