/****************************************************************
 * This software is Copyright (C) 1986-1998 by                  *
 *                                                              *
 * F6FBB - Jean-Paul ROUBELAT,  jpr@f6fbb.org                   *
 * 6, rue George Sand                                           *
 * 31120 - Roquettes - France                                   *
 *                                                              *
 * License to copy and use this software is granted for         *
 * non-commercial use provided that it is identified as         *
 *                                                              *
 * "FBB packet-radio BBS software by Jean-Paul ROUBELAT, F6FBB" *
 *                                                              *
 * in all material mentioning or referencing this software      *
 * or this function.                                            *
 *                                                              *
 * These notices must be retained in any copies of any part of  *
 * this documentation and/or software.                          *
 *                                                              *
 * Parts of code have been taken from many other softwares.     *
 * Thanks for the help.                                         *
 ****************************************************************/


 /**********************************************
 *                                             *
 * xfbbC : Client for xfbbd BBS daemon version *
 *                                             *
 **********************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>

#include <sys/socket.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <netdb.h>

#define CONSOLE 0
#define MONITOR 1
#define ALLCHAN 2

#define FBB_MSGS        1
#define FBB_STATUS      2
#define FBB_NBCNX       4
#define FBB_LISTCNX     8
#define FBB_MONITOR     16
#define FBB_CONSOLE     32
#define FBB_CHANNEL     64

static char *usage =
"Usage: xfbbC [-c | -m channel] [-d] [-f] [-h hostname] [-p port] [-i mycall] [-w password]\n"
"-c : console connection\n"
"-d : debug mode\n"
"-f : data filtering. Control characters are discarded\n"
"-m channel monitoring of a connected channel (default to all channels)\n\n"
"Environment variables :\n"
"\tXFBBC_HOST : hostname    (default localhost)\n"
"\tXFBBC_PORT : socket port (default 3286)\n"
"\tXFBBC_CALL : my callsign\n"
"\tXFBBC_PASS : password string of passwd.sys in xfbbd\n\n";


void makekey (char *cle, char *pass, char *buffer);

static int open_connection (char *tcp_addr, int tcp_port, int mask)
{
	int sock;
	struct sockaddr_in sock_addr;
	struct hostent *phe;

	if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror ("socket_r");
		return (0);
	}

	if ((phe = gethostbyname (tcp_addr)) == NULL)
	{
		perror ("gethostbyname");
		return (-1);
	}

	sock_addr.sin_family = AF_INET;
	sock_addr.sin_port = htons (tcp_port);
	memcpy ((char *) &sock_addr.sin_addr, phe->h_addr, phe->h_length);

	if (connect (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) == -1)
	{
		perror ("connect");
		close (sock);
		return (-1);
	}

	return (sock);
}

static int do_filter (char *ptr, int len)
{
	char *scan = ptr;
	int lg = 0;

	while (len)
	{
		if ((*ptr == '\n') || isprint (*ptr))
			scan[lg++] = *ptr;
		++ptr;
		--len;
	}
	return (lg);
}

int main (int ac, char *av[])
{
	char host[80];
	char pass[256];
	char key[256];
	char mycall[80];
	char buffer[1024];
	char *ptr;
	int s;
	int nb;
	int console;
	int channel;
	int sock;
	int mask = 0;
	int debug = 0;
	int filter = 0;
	int port;

	fprintf (stderr, "\nClient application for xfbbd v2.1 ( help : xfbbC -? )\n\n");

	if ((ptr = getenv ("XFBBC_HOST")))
		strcpy (host, ptr);
	else
		strcpy (host, "localhost");

	if ((ptr = getenv ("XFBBC_PORT")))
		port = atoi (ptr);
	else
		port = 3286;

	if ((ptr = getenv ("XFBBC_PASS")))
		strcpy (pass, ptr);
	else
		strcpy (pass, "password");

	if ((ptr = getenv ("XFBBC_CALL")))
		strcpy (mycall, ptr);
	else
		strcpy (mycall, "nocall");

	console = 0;
	channel = 0;

	mask = FBB_MONITOR;

	while ((s = getopt (ac, av, "cdfh:i:m:p:w:?")) != -1)
	{
		switch (s)
		{
		case 'h':
			strcpy (host, optarg);
			break;
		case 'd':
			debug = 1;;
			break;
		case 'f':
			filter = 1;;
			break;
		case 'p':
			port = atoi (optarg);
			break;
		case 'w':
			strcpy (pass, optarg);
			break;
		case 'i':
			strcpy (mycall, optarg);
			break;
		case 'c':
			console = 1;
			mask = FBB_CONSOLE;
			break;
		case 'm':
			channel = atoi (optarg);
			mask = FBB_CHANNEL;
			break;
		case ':':
			fprintf (stderr, "xfbbC: option needs an argument\n");
			return 1;
		case '?':
			fprintf (stderr, usage);
			return 1;
		}
	}

	if (debug)
	{
		/* if -d option, ask all services */
		mask |= (FBB_MSGS | FBB_STATUS | FBB_NBCNX | FBB_LISTCNX);
	}

	sock = open_connection (host, port, mask);

	if (sock == -1)
	{
		fprintf (stderr, "Cannot connect xfbbd\n");
		return 0;
	}

	sprintf (buffer, "%d %d %s\n", mask, channel, mycall);
	write (sock, buffer, strlen (buffer));

	printf ("Authentication in process...");
	fflush (stdout);

	nb = read (sock, buffer, 20);
	if (nb <= 0)
	{
		printf ("Connection closed. Terminating\n");
		return (0);
	}

	buffer[nb] = '\0';
	sscanf (buffer, "%s", key);

	printf (" Ok\n\n");

	makekey (key, pass, buffer);
	strcat (buffer, "\n");
	write (sock, buffer, strlen (buffer));

	if (mask & FBB_CONSOLE)
		printf ("Console connection ...\n\n");
	if (mask & FBB_CHANNEL)
		printf ("Monitoring channel %d ...\n\n", channel);
	if (mask & FBB_MONITOR)
		printf ("Monitoring all ports ...\n\n");


	for (;;)
	{
		fd_set sock_read;

		FD_ZERO (&sock_read);
		FD_SET (STDIN_FILENO, &sock_read);
		FD_SET (sock, &sock_read);

		/* Wait for I/O event */
		if (select (sock + 1, &sock_read, NULL, NULL, NULL) == -1)
		{
			perror ("select");
			break;
		}

		if (FD_ISSET (STDIN_FILENO, &sock_read))
		{
			nb = read (STDIN_FILENO, buffer, sizeof (buffer));
			if (nb == -1)
			{
				perror ("read");
				break;
			}
			nb = write (sock, buffer, nb);
			if (nb == -1)
			{
				perror ("read");
				break;
			}
		}

		if (FD_ISSET (sock, &sock_read))
		{
			unsigned char header[4];
			unsigned int service;
			unsigned int len;
			unsigned int total;
			char *ptr;

			/* Read header first. Be sure the 4 bytes are read */
			for (total = 0; total < 4;)
			{
				nb = read (sock, header + total, 4 - total);
				if (nb == -1)
				{
					perror ("read");
					break;
				}

				if (nb == 0)
				{
					printf ("Connection closed. Terminating\n");
					return (0);
				}

				total += nb;
			}

			service = (unsigned int) header[0];
			len = ((unsigned int) header[3] << 8) + (unsigned int) header[2];

			/* Read the data following the header. Be sure all bytes are read */
			for (total = 0; total < len;)
			{
				nb = read (sock, buffer + total, len - total);
				if (nb == -1)
				{
					perror ("read");
					break;
				}
				if (nb == 0)
				{
					printf ("Connection closed. Terminating\n");
					return (0);
				}
				total += nb;
			}

			if (total != 0)
			{
				/* decodes and displays the services */
				switch (service)
				{
				case FBB_CONSOLE:
				case FBB_MONITOR:
				case FBB_CHANNEL:
					if (total > 3)
					{
						/* skip color and header information */
						total -= 3;
						ptr = buffer + 3;
						if (filter)
							total = do_filter (ptr, total);
						write (1, ptr, total);
					}
					break;
				case FBB_MSGS:
					{
						int nbPriv, nbHeld, nbTotal;

						buffer[total] = '\0';
						sscanf (buffer, "%d %d %d",
								&nbPriv, &nbHeld, &nbTotal);
						if (debug)
							printf ("Messages : Priv %d  Held %d  Total %d\n",
								nbPriv, nbHeld, nbTotal);
					}
					break;
				case FBB_STATUS:
					{
						int MemUsed, MemAvail, Disk1, Disk2;

						buffer[total] = '\0';
						sscanf (buffer, "%d %d %d %d",
								&MemUsed, &MemAvail, &Disk1, &Disk2);
						if (debug)
							printf ("Status   : LMemUsed %d  GMemUsed %dk  Disk1 %dk  Disk2 %dk\n",
								MemUsed, MemAvail, Disk1, Disk2);
					}
					break;
				case FBB_NBCNX:
					buffer[total] = '\0';
					if (debug)
						printf ("Nb Conn  : %s\n", buffer);
					break;
				case FBB_LISTCNX:
					buffer[total] = '\0';
					if (debug)
						printf ("ConnLine :%s\n", buffer);
					break;
					break;
				}
			}
		}
	}

	close (sock);
	printf ("Abnormal termination\n");
	return (1);
}

#define PROTOTYPES 1
#include "global.h"
#include "md5.h"

static void MD5String (unsigned char *dest, unsigned char *source)
{
	int i;
	MD5_CTX context;
	unsigned char digest[16];
	unsigned int len = strlen (source);

	MD5Init (&context);
	MD5Update (&context, source, len);
	MD5Final (digest, &context);

	*dest = '\0';

	for (i = 0; i < 16; i++)
	{
		char tmp[5];

		sprintf (tmp, "%02X", digest[i]);
		strcat (dest, tmp);
	}
}

void makekey (char *cle, char *pass, char *buffer)
{
	char source[1024];

	strcpy (source, cle);
	strcat (source, pass);
	MD5String (buffer, source);
}
