/* 
 * $Id: c_handle.c,v 1.1 91/05/03 03:01:27 qjb Exp Locker: qjb $
 * $Source: /afs/athena.mit.edu/astaff/project/qcs/src/RCS/c_handle.c,v $
 * $Author: qjb $
 *
 * This file contains code to handle activity on various client
 * file descriptors.
 */

#if !defined(lint) && !defined(SABER) || defined(RCS_HDRS)
static char *rcsid = "$Id: c_handle.c,v 1.1 91/05/03 03:01:27 qjb Exp Locker: qjb $";
#endif /* !lint && !SABER || RCS_HDRS */

#include <stdio.h>
#include <sys/types.h>
#include <qrpc.h>

#include "qcs.h"
#include "qcs_private.h"
#include "qcs_qrpc.h"

/* XXX */
#include "sample.h"

/* 
 * This function reads from the server what port number to connect
 * to and then creates a client qrpc on this port to connect to the
 * server.  Once connected, it sends an authenticator.  If all 
 * is well, then make this new qrpc qcs->user_qrpc.
 */
static qcs_error_t qcsi_mkcomm(qcs_t *qcs, qrpc_length_t length, void *data)
{
    char packet[sizeof(qcs_jobno) + sizeof(u_short)];
    qcs_jobno jobno;
    u_short port;
    char *p;
    qrpc_error_t status;
    qrpc_client_info params;
    qrpc_t qrpc;

    if (length != sizeof(packet)) {
	sprintf(qcs->errmsg, "qcs protocol error: %s %d %s %d.",
		"server sending", length, "bytes for mkcomm instead of",
		sizeof(packet));
	return (QCS_FAILURE);
    }

    /* 
     * Read packet -- contains job number in network byte order
     * followed by port in network byte order.  
     */
    
    if (status = qrpc_recv_data(qcs->qrpc, (qrpc_data_t) packet)) {
	sprintf(qcs->errmsg, 
		"failure receiving user_data information packet: %s",
		qrpc_error_string(qcs->qrpc, status));
	return (QCS_FAILURE);
    }

    /* Disassemble packet */
    p = packet;
    bcopy(p, &jobno, sizeof(qcs_jobno));
    p += sizeof(qcs_jobno);
    bcopy(p, &port, sizeof(u_short));

    jobno = ntohl(jobno);
    port = ntohs(port);
    
    /* XXX Don't use SAMPLE_STUFF */
    /* Now attempt to connect to the server. */
    qrpc_init_tcp_client_info(&params, qcs->qrpc->setup.client.host, NULL,
			      port);
    if (status = qrpc_create_client(&qrpc, &params, sample_opcodes, 
				    SAMPLE_MINV, SAMPLE_MAXV)) {
	strcpy(qcs->errmsg, qrpc_error_string(qrpc, status));
	if (qrpc)
	    qrpc_destroy(&qrpc);
	return (status);
    }
    
    qcs->user_qrpc[jobno] = qrpc;

    /*
     * Now that we're connected, encrypt the network-byte order version
     * of the port number encrypted in the session key 
     */

    return(QCS_SUCCESS);
}

qcs_error_t qcsi_handle_qcsserver(qcs_t *qcs, void *userdata) 
{
    qrpc_error_t status = QRPC_SUCCESS;
    qrpc_length_t length;
    qrpc_data_t data;
    qrpc_opcode_t opcode;
    qcs_jobno jobno;
    
    status = qrpc_recv_header(qcs->qrpc, &opcode, &length);
    if (status == QRPC_ERR_NOPEER) {
	qcsi_be_flushed(qcs);
	return (QCS_SUCCESS);
    }
    else if (status) {
	sprintf(qcs->errmsg, "Error getting header from server: %s",
		qrpc_error_string(qcs->qrpc, status));
	return (QCS_FAILURE);
    }

    switch (opcode) {
      case QCS_OP_CMD:
	if (length != sizeof(qcs_jobno)) {
	    sprintf(qcs->errmsg, 
		    "qcs protocol error while receiving command completion");
	    return (QCS_FAILURE);
	}
	if (status = qrpc_recv_data(qcs->qrpc, (qrpc_data_t)&jobno)) {
	    sprintf(qcs->errmsg, "error reading jobno: %s", 
		    qrpc_error_string(qcs->qrpc, status));
	    return (QCS_FAILURE);
	}
	jobno = ntohl(jobno);
	if (qcs->params->handle_completion)
	    qcs->params->handle_completion(qcs, jobno, userdata);
	break;

      case QCS_OP_NOTIFY:
      case QCS_OP_ERROR:
      case QCS_OP_MESSAGE:
	/*
	 * Allocate memory to read string from server and call
	 * appropriate handler 
	 */

	if ((data = (qrpc_data_t)calloc(length + 1, sizeof(char))) == NULL) {
	    sprintf(qcs->errmsg, 
		    "unable to allocated %d bytes for server message",
		    length);
	    return (QCS_FAILURE);
	}
	status = qrpc_recv_data(qcs->qrpc, data);
	if (status == QRPC_ERR_NOPEER) {
	    qcsi_be_flushed(qcs);
	    return (QCS_SUCCESS);
	}
	else if (status) {
	    sprintf(qcs->errmsg,
		    "failure receiving server data: %s",
		    qrpc_error_string(qcs->qrpc, status));
	    return (QCS_FAILURE);
	}

	switch (opcode) {
	  case QCS_OP_NOTIFY:
	    qcs->params->handle_notify(qcs, (char *)data, userdata);
	    break;
	  case QCS_OP_ERROR:
	    qcs->params->handle_error(qcs, (char *)data, userdata);
	    break;
	  case QCS_OP_MESSAGE:
	    qcs->params->handle_message(qcs, (char *)data, userdata);
	    break;
	}
	    
    
	free(data);

	break;

      case QCS_OP_FLUSH:
	if (length != 0) {
	    sprintf(qcs->errmsg, 
		    "qcs protocol error while receiving quit or flush");
	    return (QCS_FAILURE);
	}
	qrpc_recv_data(qcs->qrpc, NULL);
	qcsi_be_flushed(qcs);
	break;

      case QCS_OP_MKCOMM:
	if (qcsi_mkcomm(qcs, length, userdata))
	    return (QCS_FAILURE);
	break;

      default:
	sprintf(qcs->errmsg, "internal error: %s",
		"default reached in qcsi_handle_qcsserver");
	return (QCS_FAILURE);
    }

    return (QCS_SUCCESS);
}
