/*****************************************************************************
 *
 * File ..................: client
 * Purpose ...............: MBSE Deamon Client
 * Last modification date : 29-Mar-1999
 *
 *****************************************************************************
 * Copyright (C) 1993-1999
 *   
 * Michiel Broek		FIDO:		2:2801/16
 * Beekmansbos 10		Internet:	mbroek@ux123.pttnwb.nl
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * MBSE BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MB BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../include/libs.h"
#include "../include/structs.h"
#include "common.h"

static int	sock = -1;	/* TCP/IP socket			*/

struct hostent	*hp;		/* pointer to host info for remote host	*/
struct servent	*sp;		/* pointer to service information	*/

struct sockaddr_in myaddr_in;	/* for local socket address		*/
struct sockaddr_in peeraddr_in;	/* for peer socket address		*/

static char	*myname='\0';	/* my program name			*/


/************************************************************************
 *
 * Connect to internet domain socket, return -1 if error or socket no. 
 * The parameters are the host to connect to, the calling program name,
 * and the city where you are. The last parameters is only filled when
 * the connecting program is a bbs line. Note that the parameters may
 * never be empty or a segmentation fault will occur.
 */

int socket_connect(char *host, char *user, char *prg, char *city, char *authcode)
{
	int 		addrlen, s;
	static char	buf[SS_BUFSIZE], cbuf[24];
	char		tty[18];

	myname = prg;
	memset((char *)&myaddr_in, 0, sizeof(struct sockaddr_in));
	memset((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in));
	peeraddr_in.sin_family = AF_INET;
	hp = gethostbyname(host);
	if (hp == NULL) {
		printf("%s not found\n", host);
		return -1;
	}

	peeraddr_in.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr;

	sp = getservbyname("mbse", "tcp");
	if (sp == NULL) {
		printf("Service \"mbse\" tcp not found in /etc/services\n");
		return -1;
	}
	peeraddr_in.sin_port = sp->s_port;

	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s == -1) {
		perror(myname);
		printf("Unable to create tcp socket\n");
		return -1;
	}

	if (connect(s, (struct sockaddr *)&peeraddr_in, sizeof(struct sockaddr_in)) == -1) {
		perror(myname);
		printf("Cannot connect tcp socket\n");
		return -1;
	}

	addrlen = sizeof(struct sockaddr_in);

	if (getsockname(s, (struct sockaddr *)&myaddr_in, &addrlen) == -1) {
		perror(myname);
		printf("Unable to read socket address\n");
		return -1;
	}

	/*
	 * Now that we have an TCP connection, we gather 
	 * information to tell the server who we are.
	 */
	if (isatty(1) && (ttyname(1) != NULL)) {
		strcpy(tty, ttyname(1));
		if (strchr(tty, 't'))
			strcpy(tty, index(tty, 't'));
		if (strchr(tty, 'c'))
			strcpy(tty, index(tty, 'c'));
	} else {
		strcpy(tty, "-");
	}
	sock = s;

	/*
	 * Send the information to the server. 
	 */
	sprintf(buf, "AINI:5,%d,%s,%s,%s,%s;", getpid(), tty, user, prg, city);
	if (socket_send(buf) != 0) {
		sock = -1;
		return -1;
	}

	strcpy(buf, socket_receive());
	if (strncmp(buf, "100:0;", 6) != 0) {
		printf("AINI not acknowledged by the server\n");
		sock = -1;
		return -1;
	}

	/*
	 * Now send the authorisation code, this is the 32bit crc of
	 * a string made of the pid + authcode. This will produce a
	 * crc value which is different for each client, and as long
	 * as nobody knows the authcode in your config files this should
	 * be safe against sniffers. Note that all your programs MUST use
	 * the same authcode, nomather what it is.
	 */
	sprintf(cbuf, "%d%s", getpid(), authcode);
	sprintf(buf, "AAUT:2,%d,%08lx;", getpid(), str_crc32(cbuf));
	if (socket_send(buf) != 0) {
		sock = -1;
		return -1;
	}

	strcpy(buf, socket_receive()); 
	if (strncmp(buf, "100:0;", 6) != 0) {
		printf("Access denied by the server!\n");
		exit(1);
	}

	return s;
}



/*
 * Send data via internet domain socket
 */
int socket_send(char *buf)
{
	if (sock == -1)
		return -1;

	if (send(sock, buf, strlen(buf), 0) != strlen(buf)) {
		printf("Socket send failed error %d\n", errno);
		return -1;
	}

	return 0;
}



/*
 * Return an empty buffer if somthing went wrong, else the complete
 * dataline is returned.
 */
char *socket_receive(void)
{
	static char	buf[SS_BUFSIZE];
	int		i, j;

	memset((char *)&buf, 0, SS_BUFSIZE);
	i = 0;
	while ((strchr(buf, ';')) == NULL) {
		j = recv(sock, &buf[i], SS_BUFSIZE-i, 0);
		if (j == -1) {
			perror("recv");
			printf("Error reading socket\n");
			memset((char *)&buf, 0, SS_BUFSIZE);
			return buf;
		}
		i += j;
	}
	return buf;
}



/***************************************************************************
 *
 *  Shutdown the socket, first send the server the close command so this
 *  application will be removed from the servers active clients list.
 *  There must be a parameter with the pid so that client applications
 *  where the shutdown will be done by a child process is able to give
 *  the parent pid as an identifier.
 */

int socket_shutdown(pid_t pid)
{
	static char	buf[SS_BUFSIZE];

	if (sock == -1)
		return 0;

	sprintf(buf, "ACLO:1,%d;", pid);
 	if (socket_send(buf) == 0) {
		strcpy(buf, socket_receive());
		if (strncmp(buf, "107:0;", 6) != 0) {
			printf("Shutdown not acknowledged by the server\n");
			printf("Got \"%s\"\n", buf);
		}
	}
	
	if (shutdown(sock, 1) == -1) {
		perror(myname);
		printf("Cannot shutdown socket\n");
		return -1;
	}

	sock = -1;
	return 0;
}


