/*
 * $Id: prot-a.c,v 0.8 1991/09/21 13:06:53 ceder Exp $
 * Copyright (C) 1991  Lysator Academic Computer Association.
 *
 * This file is part of the LysKOM server.
 * 
 * LysKOM 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 1, or (at your option) 
 * any later version.
 * 
 * LysKOM 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 LysKOM; see the file COPYING.  If not, write to
 * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
 * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, 
 * MA 02139, USA.
 *
 * Please mail bug reports to bug-lyskom@lysator.liu.se. 
 */
/*
 * Protocol A.
 */

static char *rcsid = "$Id: prot-a.c,v 0.8 1991/09/21 13:06:53 ceder Exp $";


#include <stdio.h>
#include <setjmp.h>

#include <kom-errno.h>
#include <kom-types.h>
#include <server/smalloc.h>
#include "lyskomd.h"
#include "com.h"
#include "connections.h"
#include "isc-parse.h"
#include "prot-a.h"
#include "prot-a-output.h"
#include "prot-a-parse.h"
#include <debug.h>
#include "isc-interface.h"
#include "mux.h"

BUGDECL;

void
prot_a_reply(Connection *client,
	     Bool status,
	     Result_holder *res)
{
    /*
     * The function is called. Now return the answer.
     */

    if ( status == OK )
    {
	mux_printf(client, "=%d", client->ref_no);

	switch ( fnc_defs[ client->function ].result )
	{
	case rt_number:
	    mux_printf(client, " %ld", (u_long)res->number);
	    BUG(("=%d\n", res->number));
	    break;

	case rt_success:
	    BUG(("=Success\n"));
	    break;
	    
	case rt_person:
	    prot_a_output_person(client, &res->person);
	    BUG(("={Person struct not listed}\n"));
	    break;

	case rt_membership:
	    prot_a_output_membership(client, &res->membership);
	    BUG(("={Membership struct not listed}\n"));
	    break;

	case rt_conf_list:
	    prot_a_output_conf_list(client, res->conf_list);
	    BUG(("={Conf_list not listed}\n"));
	    break;

	case rt_conf_no_list:
	    prot_a_output_conf_no_list(client, res->conf_no_list);
	    BUG(("={Conf_no_list not listed}\n"));
	    break;
	    	    
	case rt_conference:
	    prot_a_output_conference(client, &res->conference);
	    BUG(("={Conference struct not listed}\n"));
	    break;

	case rt_string:
	    prot_a_output_string(client, res->string);
	    BUG(("={%dH", res->string.len));
	    BUGSTR(res->string);
	    BUG(("}\n"));
	    break;
	    
	case rt_mark_list:
	    prot_a_output_mark_list(client, res->mark_list);
	    BUG(("={Mark_list not listed}\n"));
	    break;
	    
	case rt_text_stat:
	    prot_a_output_text_stat(client, &res->text_stat);
	    BUG(("={Text_stat struct not listed}\n"));
	    break;
	    
	case rt_text_list:
	    prot_a_output_text_list(client, res->text_list);
	    BUG(("={Text_list not listed}\n"));
	    break;

	case rt_who_info_list:
	    prot_a_output_who_info_list(client, res->who_info_list);
	    BUG(("={Who_info_list not listed}\n"));
	    break;

	case rt_who_info_list_old:
	    prot_a_output_who_info_list_old(client,
					    res->who_info_list_old);
	    BUG(("={Old who_info_list not listed}\n"));
	    break;

	case rt_session_info:
	    prot_a_output_session_info(client,
				       &res->session_info);
	    BUG(("={Session_info not listed}\n"));
	    break;
    
	case rt_info:
	    prot_a_output_info(client, &res->info);
	    BUG(("={Who_info struct not listed}\n"));
	    break;

	case rt_membership_list:
	    prot_a_output_membership_list (client, res->membership_list);
	    BUG (("={Membership_list not listed}\n"));
	    break;
	    
	case rt_member_list:
	    prot_a_output_member_list (client, res->member_list);
	    BUG(("={Member_list not listed}\n"));
	    break;

	case rt_time_date:
	    prot_a_output_time (client, res->time_date);
	    BUG(("=(time_t)%d\n", res->time_date));
	    break;

	case rt_session_no:
	    prot_a_output_session_no (client, res->session_no);
	    BUG(("=(Session_no)%d\n", res->session_no));
	    break;
	}
	mux_putc('\n', client);
    }
    else
    {
	/* Failure. Give a reply with the error message. */
	mux_printf(client, "%%%d %d %d\n",
		client->ref_no, kom_errno, err_stat);
	BUG(("%%Err %d\n", kom_errno));
    }

    mux_flush(client);
}

/*
 * Check if it is a legal function.
 *
 * BUG: This should be generated from fncdef.txt.
 */


/*
 * Set up all data structures that are private to protocol A. This
 * function is called from connections.c whenever a connection says
 * that it is of type A.
 */
void
prot_a_init(Connection *conn)
{
    conn->parse_pos = 0;
    conn->fnc_parse_pos = 0;
    conn->array_parse_pos = 0;
    conn->struct_parse_pos = 0;
    conn->string_parse_pos = 0;
    conn->ref_no = 0;
    conn->function = 0;
    conn->num0 = 0;
    conn->num1 = 0;
    conn->num2 = 0;
    conn->num3 = 0;
    conn->c_string0 = EMPTY_STRING;
    conn->c_string1 = EMPTY_STRING;
    conn->string0 = EMPTY_STRING;
    conn->c_misc_info_p = NULL;
    conn->c_local_text_no_p = NULL;
    conn->priv_bits = DEFAULT_PRIV_BITS;
    conn->conf_type = NULL_CONF_TYPE;
}

void
prot_a_destruct(Connection *conn)
{

    /*
     * All strings et c should already by free:d - that is done at the
     * end of each atomic call. But free them anyhow, just in case
     * this client is forced of.
     */

    s_clear(&conn->string0);
    s_clear(&conn->c_string0);
    s_clear(&conn->c_string1);
    sfree(conn->c_misc_info_p);
    sfree(conn->c_local_text_no_p);
}


static Bool
prot_a_is_legal_fnc(Call_header fnc)
{
    switch(fnc)
    {
    case call_fnc_login: 
    case call_fnc_logout: 
    case call_fnc_pepsi: 
    case call_fnc_change_name: 
    case call_fnc_change_what_i_am_doing: 
    case call_fnc_create_person: 
    case call_fnc_get_person_stat_old:
    case call_fnc_set_priv_bits:
    case call_fnc_set_passwd: 
    case call_fnc_query_read_texts: 
    case call_fnc_create_conf: 
    case call_fnc_delete_conf: 
    case call_fnc_lookup_name: 
    case call_fnc_get_conf_stat_old: 
    case call_fnc_add_member: 
    case call_fnc_sub_member: 
    case call_fnc_set_presentation: 
    case call_fnc_set_etc_motd: 
    case call_fnc_set_supervisor: 
    case call_fnc_set_permitted_submitters: 
    case call_fnc_set_super_conf: 
    case call_fnc_set_conf_type: 
    case call_fnc_set_garb_nice: 
    case call_fnc_get_marks: 
    case call_fnc_mark_text: 
    case call_fnc_get_text: 
    case call_fnc_get_text_stat: 
    case call_fnc_mark_as_read: 
    case call_fnc_create_text: 
    case call_fnc_delete_text: 
    case call_fnc_add_recipient: 
    case call_fnc_sub_recipient: 
    case call_fnc_add_comment: 
    case call_fnc_sub_comment: 
    case call_fnc_get_map: 
    case call_fnc_get_time: 
    case call_fnc_get_info:
    case call_fnc_add_footnote:
    case call_fnc_sub_footnote:
    case call_fnc_who_is_on_old:
    case call_fnc_set_unread:
    case call_fnc_set_motd_of_lyskom:
    case call_fnc_enable:
    case call_fnc_sync:
    case call_fnc_shutdown:
    case call_fnc_broadcast:
    case call_fnc_get_membership:
    case call_fnc_get_created_texts:
    case call_fnc_get_members:
    case call_fnc_get_person_stat:
    case call_fnc_get_conf_stat:
    case call_fnc_who_is_on:
    case call_fnc_get_unread_confs:
    case call_fnc_send_message:
    case call_fnc_get_session_info:
    case call_fnc_disconnect:
    case call_fnc_who_am_i:
    case call_fnc_set_user_area:
	return TRUE;

    default:
	return FALSE;
    }
}    


void
prot_a_parse_packet(Connection *client)
{
    if ( s_empty(client->username) )
    {
	/* Connection not established yet */

	prot_a_parse_string(client, &client->c_string0, USERNAME_LEN);

	client->username = client->c_string0; /* Kludge to deal with */
	client->c_string0 = EMPTY_STRING;     /* "A5B" as first input. */
				/* Protokoll B will not suffer from this... */
	
	if ( s_strcat(&client->username,
		      s_fcrea_str((const unsigned char *)"@")) != OK )
	    restart_kom("prot_a_parse_packet: s_strcat\n");

	if ( s_strcat(&client->username, client->hostname) != OK )
	    restart_kom("prot_a_parse_packet: s_strcat II\n");

	mux_printf(client, "LysKOM\n");
	mux_flush(client);
	BUG(("[Client %d is logged on]\n", client->session_no));
    }	

    switch(client->parse_pos)
    {
    case 0:			/* Get ref_no */
	client->ref_no = prot_a_parse_long(client);
	client->parse_pos = 1;
	/* Fall through */
    case 1:			/* Get fnc_no */
	client->function = prot_a_parse_long(client);
	if ( !prot_a_is_legal_fnc(client->function) )
	    longjmp(parse_env, ISC_PROTOCOL_ERR);
	client->parse_pos = 2;
	/* Fall through */
    case 2:
	/* Call the function that parses the arguments for this call. */
	fnc_defs[client->function].parser(client);
	/* Fall through */
    default:
	client->parse_pos = 0;
    }

}