/*
 * Copyright 1990 by Baylor College of Medicine ALL RIGHTS RESERVED. 
 *
 * This program is subject to a license agreement between 
 * Baylor College of Medicine and MIT. Any use inconsistent with
 * said license and any use by persons other than the faculty, 
 * students and staff at MIT or any use on a computer not operated 
 * as part of the Athena Computing Environment (ACE) is expressly 
 * prohibited.
 */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <malloc.h>

#include "dbsrv.h"

/*
 * Process a single client request
 */
static
proc_req(c)
	Connection c ;
{
	long req ;
	int rc ;

	/* read in request */
	if (read_f(c->conn_sock,"l",&req) < 0)
	{
		return -1 ;
	}

	/* Determine what it was and answer back to it */
	switch(req)
	{
		case REQ_KILL :
			rc = req_kill(c) ;
			break ;
		case REQ_DEBUG :
			rc = req_debug(c) ;
			break ;
		case REQ_ALLOWED_NOTEBOOK_USE_LIST :
			rc = req_get_notebook_access_list(c) ;
			break ;
		case REQ_ADD_USER :
			rc = req_add_user(c) ;
			break ;
		case REQ_DELETE_NOTEBOOK :
			rc = req_delete_notebook(c) ;
			break ;
		case REQ_DELETE_OBJECT :
			rc = req_delete_object(c) ;
			break ;
		case REQ_CREATE_NOTEBOOK :
			rc = req_create_notebook(c) ;
			break ;
		case REQ_CREATE_PAGE :
			rc = req_create_page(c) ;
			break ;
		case REQ_CREATE_OBJECT :
			rc = req_create_object(c) ;
			break ;
		case REQ_MODIFY_NOTEBOOK :
			rc = req_modify_notebook(c) ;
			break ;
		case REQ_MODIFY_PAGE :
			rc = req_modify_page(c) ;
			break ;
		case REQ_MODIFY_OBJECT :
			rc = req_modify_object(c) ;
			break ;
		case REQ_INQ_NOTEBOOK :
			rc = req_inq_notebook(c) ;
			break ;
		case REQ_INQ_PAGE :
			rc = req_inq_page(c) ;
			break ;
		case REQ_INQ_OBJECT :
			rc = req_inq_object(c) ;
			break ;
		case REQ_INQ_NOTEBOOK_PAGES :
			rc = req_inq_notebook_pages(c) ;
			break ;
		case REQ_INQ_PAGE_OBJECTS :
			rc = req_inq_page_objects(c) ;
			break ;
		case REQ_ALLOW_AUTHOR :
			rc = req_allow_author(c) ;
			break ;
		case REQ_DISALLOW_AUTHOR :
			rc = req_disallow_author(c) ;
			break ;
		case REQ_GET_TEXT :
			rc = req_get_text(c) ;
			break ;
		case REQ_SET_TEXT :
			rc = req_set_text(c) ;
			break ;
		case REQ_GET_IMAGE :
			rc = req_get_image(c) ;
			break ;
		case REQ_SET_IMAGE :
			rc = req_set_image(c) ;
			break ;
		case REQ_GET_AUTHOR_ACCESS :
			rc = req_get_author_access(c) ;
			break ;
		case REQ_GET_AUTHOR_LIST :
			rc = req_get_author_list(c) ;
			break ;
		case REQ_VALIDATE_PAGE :
			rc = req_validate_page(c) ;
			break ;
		case REQ_BLURT_PAGE :
			rc = req_blurt_page(c) ;
			break ;
		case REQ_DELETE_PAGE :
			rc = req_delete_page(c) ;
			break ;
		case REQ_FIND_NOTEBOOK_BY_NAME :
			rc = req_find_notebook_by_name(c) ;
			break ;
		case REQ_FIND_PAGE_BY_NAME :
			rc = req_find_page_by_name(c) ;
			break ;
		case REQ_INQ_PAGE_LINKS :
			rc = req_inq_page_links(c) ;
			break ;
		case REQ_INQ_NOTEBOOK_ORPHANS :
			rc = req_inq_notebook_orphans(c) ;
			break ;
		case REQ_SEARCH_NOTEBOOK :
			rc = req_search_notebook(c) ;
			break ;
		case REQ_SEARCH_DATABASE :
			rc = req_search_database(c) ;
			break ;
		case REQ_BLURT_NOTE :
			rc = req_blurt_note(c) ;
			break ;
		case REQ_GET_ACTIVE_AUTHORS :
			rc = req_get_active_authors(c) ;
			break ;
		case REQ_SHUTDOWN_CLIENTS :
			rc = req_shutdown_clients(c) ;
			break ;

		default :
			rc = -3 ;
			break ;
	}

	/* If non-fatal error then return error */
	if (rc == -2)
	{
		if (write_f(c->conn_sock,"l",EVT_ERR) < 0)
		{
			rc = -1 ;
		}
		else
		{
			rc = 0 ;
		}
	}
	if (rc < 0)
	{
		return -1 ;
	}
	return 0 ;
}

/* Services connection activity */
static
service_connection(c)
	Connection c ;
{
	switch(c->state)
	{
		case STATE_REQ_OK :
			return (proc_req(c) < 0) ? -1 : 0 ;
		case STATE_WAIT_INFO :
			return (proc_init_info(c) < 0) ? -1 : 0 ;
	}
	return -1 ;
}

static
void
notify_author_logout(c)
	Connection c;
{
	Connection cp = pc.cl.first;

	/* make sure that the connection is valid and has an author id */
	if (c == NULL) return;

	if (c->author == NULL) return;

	while(cp != NULL)
	{
		if (cp != c)
		{
			write_f(cp->conn_sock,"lll",EVT_AUTHOR_LOGOUT,sizeof(long),c->author->aid);
		}
		cp = cp->next;
	}
}

/*
 *	Checks for requests and processes them utilizing select.
 */
dbsrv_process_requests()
{
	fd_set ready ;
	int max_sock = pc.listen_sock ;

	/* Zero out the fd field */
	FD_ZERO(&ready) ;

	/* Set listening socket bit */
	FD_SET(pc.listen_sock,&ready) ;

	/* Set the connected sockets' bits */
	{
		Connection c ;
		for(c = pc.cl.first ; c != NULL; c = c->next)
		{
			FD_SET(c->conn_sock,&ready) ;
			if (c->conn_sock > max_sock)
			{
				max_sock = c->conn_sock ;
			}
		}
	}

	/* Find out who is there */
	{
		int rc = select(max_sock + 1,&ready,(fd_set *)0,(fd_set *)0,(struct timeval *)0) ;
		if (rc < 0)
		{
			return -1 ;
		}
	}

	/* Look for conversation among the connected sockets first */
	{
		Connection c ;
		Connection *cp = &(pc.cl.first) ;

		/* For each connection */
		while((c = *cp) != NULL)
		{

			/* Somebody talking to the socket for this connection */
			if (FD_ISSET(c->conn_sock,&ready) && service_connection(c) < 0)
			{
				notify_author_logout(c);
				close(c->conn_sock) ;
				*cp = c->next ;
				free((char *)c) ;
			}
			else
			{
				cp = &(c->next) ;
			}
		}
	}

	/* If somebody wants to connect */
	if (FD_ISSET(pc.listen_sock,&ready))
	{
		int msg_sock = accept(pc.listen_sock,(struct sockaddr *)0,(int *)0) ;
		if (msg_sock >= 0)
		{
			Connection new = (Connection)malloc(sizeof *new) ;
			new->conn_sock = msg_sock ;
			new->next = pc.cl.first ;
			new->state = STATE_WAIT_INFO ;
			new->lbuf = 0 ;
			pc.cl.first = new ;
		}
	}

	/* AOK */
	return 0 ;
}
