/**********************************************************************
 *
 *           Copyright 1997 by Carnegie Mellon University
 * 
 *                       All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of CMU not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * 
 * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * 
 * $Id: snmp_client.c,v 1.7 1997/03/26 01:36:15 ryan Exp $
 * 
 **********************************************************************/

/* Our autoconf variables */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>

#ifdef HAVE_STRINGS_H
# include <strings.h>
#else /* HAVE_STRINGS_H */
# include <string.h>
#endif /* HAVE_STRINGS_H */

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */

#ifdef HAVE_BSTRING_H
#include <bstring.h>
#endif /* HAVE_BSTRING_H */

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

static char rcsid[] =
"$Id";

#include "asn1.h"
#include "snmp_error.h"
#include "snmp_pdu.h"
#include "snmp_vars.h"

#include "snmp_session.h"
#include "snmp_api.h"
#include "snmp_client.h"
#include "snmp_api_error.h"

/* #define DEBUG_CLIENT 1 */

struct synch_state snmp_synch_state;

/*
 * Add a null variable with the requested name to the end of the list of
 * variables for this pdu.
 */
void snmp_add_null_var(struct snmp_pdu *pdu, oid *name, int name_length)
{
  struct variable_list *vars;
  struct variable_list *ptr;

#ifdef DEBUG_CLIENT
  printf("CLIENT %x: Adding NULL var.\n", (unsigned int)pdu);
#endif

  vars = snmp_var_new(name, name_length);
  if (vars == NULL) {
    perror("snmp_add_null_var:malloc");
    return;
  }

  if (pdu->variables == NULL) {
    pdu->variables = vars;
  } else {

    /* Insert at the end */
    for (ptr = pdu->variables; 
	 ptr->next_variable; 
	 ptr = ptr->next_variable)
      /*EXIT*/;
    ptr->next_variable = vars;
  }

  return;
}

int snmp_synch_input(int op, 
		     struct snmp_session *session, 
		     int reqid, 
		     struct snmp_pdu *pdu,
		     void *magic)
{
  struct variable_list *var;
  struct synch_state *state = (struct synch_state *)magic;
  struct snmp_pdu *newpdu;

  struct variable_list *varPtr;

#ifdef DEBUG_CLIENT
  printf("CLIENT %x: Synchronizing input.\n", (unsigned int)pdu);
#endif

  /* Make sure this is the proper request
   */
  if (reqid != state->reqid)
    return 0;
  state->waiting = 0;

  /* Did we receive a Get Response
   */
  if (op == RECEIVED_MESSAGE && pdu->command == SNMP_PDU_RESPONSE) {

    /* clone the pdu */
    state->pdu = newpdu = snmp_pdu_clone(pdu);
    newpdu->variables = 0;

    /* Clone all variables */
    var = pdu->variables;
    if (var != NULL) {
      newpdu->variables = snmp_var_clone(var);

      varPtr = newpdu->variables;

      /* While there are more variables */
      while(var->next_variable) {

	/* Clone the next one */
	varPtr->next_variable = snmp_var_clone(var->next_variable);

	/* And move on */
	var    = var->next_variable;
	varPtr = varPtr->next_variable;

      }
      varPtr->next_variable = NULL;
    }
    state->status = STAT_SUCCESS;
  } else if (op == TIMED_OUT){
    state->status = STAT_TIMEOUT;
  }
  return 1;
}


int snmp_synch_response(struct snmp_session *ss, 
			struct snmp_pdu *pdu,
			struct snmp_pdu **response)
{
  struct synch_state *state = &snmp_synch_state;
  int numfds, count;
  fd_set fdset;
  struct timeval timeout, *tvp;
  int block;

  state->reqid = snmp_send(ss, pdu);
  if (state->reqid == 0) {
    *response = NULL;
    snmp_free_pdu(pdu);
    return STAT_ERROR;
  }
  state->waiting = 1;

  while(state->waiting) {
    numfds = 0;
    FD_ZERO(&fdset);
    block = 1;
    tvp = &timeout;
    timerclear(tvp);
    snmp_select_info(&numfds, &fdset, tvp, &block);
    if (block == 1)
      tvp = NULL;	/* block without timeout */
    count = select(numfds, &fdset, 0, 0, tvp);
    if (count > 0){
      snmp_read(&fdset);
    } else switch(count){
    case 0:
      snmp_timeout();
      break;
    case -1:
      if (errno == EINTR){
	continue;
      } else {
	perror("select");
      }
      /* FALLTHRU */
    default:
      return STAT_ERROR;
    }
  }
  *response = state->pdu;
  return state->status;
}

void snmp_synch_setup(session)
  struct snmp_session *session;
{
  session->callback = snmp_synch_input;
  session->callback_magic = (void *)&snmp_synch_state;
}
