/* jawsd - connects to AWS datalogger over serial connection and provides
 *         weather information to the network
 * Copyright (C) 2003-2004 Jeffrey Grafton <jgrafton@tjhsst.edu>
 *
 * This program 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
 * of the License, or (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <fcntl.h>

#include "config.h"
#include "data_types.h"
#include "aws_server.h"

void *
aws_server (void *voidptr)
{
	int size, yes = 1, keepgoing, listen_sock, high_sock, i;
	int comm_socks[AWS_MAX_CONNECTIONS] = {0};
	fd_set socks;
	struct sockaddr_in server_addr, client_addr;
	time_t current_time;
	int *ptrs = (int *)voidptr, *comm_sock;
 	curobs_raw *curobs = (curobs_raw *)ptrs[1];
	hilo_raw *hilo = (hilo_raw *)ptrs[2];
	char *serial = (char *)ptrs[3];
	unsigned short *history_id = (unsigned short *)ptrs[4];
	char *history = (char *)ptrs[5];
	int *history_length = (int *)ptrs[6];
	
	char buf[256];
	pthread_t child_thread;
    	void *newptrs[8];
	
	syslog(LOG_INFO, "aws_server: running.");
	
	if ((listen_sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
	{
		perror("socket");
		exit(1);
	}

	if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		perror("setsockopt");
		exit(1);
	}
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(AWS_SERVER_TCP_PORT);
	server_addr.sin_addr.s_addr = INADDR_ANY; 
	memset(&(server_addr.sin_zero), '\0', 8);
	
	if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) == -1)
	{
		perror("bind");
		exit(1);
	}
	
	if (setuid(UID) == -1)
	{
		perror("setuid");
	}
	else
	{
		printf("aws_server dropped to uid %d\n", getuid());
	}

	if (listen(listen_sock, 5) == -1)
	{
		perror("listen");
		exit(1);
	}
	
	while (1)
	{
		high_sock = setup_fd_set(&socks, listen_sock, comm_socks);


		if (select(high_sock + 1, &socks, NULL, NULL, NULL) < 0)
		{
			perror("select");
		}
		if (FD_ISSET(listen_sock, &socks))
			build_new_connection (listen_sock, comm_socks);
		else
		{
			for (i = 0; i < AWS_MAX_CONNECTIONS; i++)
			{
				if (FD_ISSET(comm_socks[i], &socks))
				{
					comm_socks[i] = handle_request (comm_socks[i], curobs, hilo, serial, *history_id, history, *history_length);
				}
			}
		}
	}
	pthread_exit(0);
}

int
handle_request (int comm_sock, curobs_raw *curobs, hilo_raw *hilo, char *serial, unsigned short history_id, char *history, int history_length)
{
	char buf[256];
	int size, keepgoing, i;
	unsigned short short_int;

	keepgoing = 1;

		memset(buf, 0, 256);
		size = recv(comm_sock, buf, 256, 0);
		if (size < 1)
			keepgoing = 0;
		else
		{
			if (buf[0] == 0x4C)
			{
				printf("received: |%s| (station %s)\n", buf, buf + 21);
				if (!strncmp(buf + 21, AWS_STATION_NAME, strlen(AWS_STATION_NAME)))
				{
					buf[0] = 0x31;
					
					if (!strncmp(buf + 1, AWS_SCHOOLNET, strlen(AWS_SCHOOLNET)) || !strncmp(buf + 1, AWS_AWSBETA, strlen(AWS_AWSBETA)))
						printf("Normal user.\n");
					else if (!strncmp(buf + 1, AWS_ADMIN, strlen(AWS_ADMIN)))
						printf("Oh crap, we have an administrator.\n");
					else
						buf[0] = 0x35;
				}
				else
					buf[0] = 0x35;
				buf[1] = 0x00;
				
				send(comm_sock, buf, 2, 0);

				printf("sending %.2hhx\n", buf[0]);
			}
			else if (buf[0] == 0x36)
			{
				size = send(comm_sock, serial, SERIAL_LENGTH, 0);
			}
			else if (buf[0] == 0x30)
			{
				size = send(comm_sock, curobs, sizeof(curobs_raw), 0);
			}
			else if (buf[0] == 0x31)
			{
				size = send(comm_sock, hilo, sizeof(hilo_raw), 0);
			}
			else if (buf[0] == 0x45)
			{
				keepgoing = 0;
			}
			else if (buf[0] == 0x34)
			{
				printf("Think this is a history id: ");
				for (i = 0; i < size; i++)
					printf("%.2hhx ", buf[i]);
				printf("\n");
				short_int = htons(history_id);
				size = send(comm_sock, &short_int, sizeof(unsigned short), 0);
			}
			else if (buf[0] == 0x3d)
			{
				printf("Think this is a history: ");
				for (i = 0; i < size; i++)
					printf("%.2hhx ", buf[i]);
				printf("\n");
				size = send(comm_sock, history, sizeof(curobs_raw), 0);
			}
			else
			{
				printf("Unknown command: %.2hhx: ", buf[0]);
				for (i = 1; i < size; i++)
					printf("%.2hhx ", buf[i]);
				printf("\n");
				strncpy(buf, "ERROR", 6);
				size = send(comm_sock, buf, 5, 0);
			}
		}


	if (!keepgoing)
	{
		syslog(LOG_INFO, "handle_request: Connection closed.");
		printf("handle_request: Connection closed.\n");
	
		close(comm_sock);
		return 0;
	}
	else
	{
		return comm_sock;
	}
}

int
setup_fd_set (fd_set *socks, int listen_sock, int *comm_socks)
{
	int i, high_sock;
	
	FD_ZERO(socks);
	
	FD_SET(listen_sock, socks);
	high_sock = listen_sock;

	for (i = 0; i < AWS_MAX_CONNECTIONS; i++)
	{
		if (comm_socks[i] != 0)
		{
				FD_SET(comm_socks[i], socks);
				if (comm_socks[i] > high_sock)
						high_sock = comm_socks[i];
		}
	}
	
	return high_sock;
}

void
build_new_connection (int listen_sock, int *comm_socks)
{
	int new_sock, i, keepgoing, size = sizeof(struct sockaddr_in);
	struct sockaddr_in client_addr;
	time_t current_time;

	if ((new_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &size)) == -1)
	{
		perror("accept");
		exit(1);
	}
	
	current_time = time(NULL);
	printf("%s: Connection from %s to port %d\n", ctime(&current_time), inet_ntoa(client_addr.sin_addr), AWS_SERVER_TCP_PORT);
	syslog(LOG_INFO, "build_new_connection: Connection from %s to port %d", inet_ntoa(client_addr.sin_addr), AWS_SERVER_TCP_PORT);
	
	keepgoing = 1;
	for (i = 0; keepgoing && i < AWS_MAX_CONNECTIONS; i++)
	{
		if (comm_socks[i] == 0)
		{
			comm_socks[i] = new_sock;
			keepgoing = 0;
		}
	}
	
	if (keepgoing)
	{
		close(new_sock);
		syslog(LOG_INFO, "build_new_connection: Too many connections, closing new connection");
	}
}

