/*
 * kadmin/v4server/kadm_stream.c
 *
 * Copyright 1988 by the Massachusetts Institute of Technology.
 *
 * For copying and distribution information, please see the file
 * <mit-copyright.h>.
 *
 * Stream conversion functions for Kerberos administration server
 */


#include <mit-copyright.h>
#include <string.h>
#include "k5-int.h"

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#else
extern char *malloc(), *calloc(), *realloc();
#endif

/*
  kadm_stream.c
  this holds the stream support routines for the kerberos administration server

    vals_to_stream: converts a vals struct to a stream for transmission
       internals build_field_header, vts_[string, char, long, short]
    stream_to_vals: converts a stream to a vals struct
       internals check_field_header, stv_[string, char, long, short]
    error: prints out a kadm error message, returns
    fatal: prints out a kadm fatal error message, exits
*/

#include "kadm.h"

#define min(a,b) (((a) < (b)) ? (a) : (b))

/* 
vals_to_stream
  recieves    : kadm_vals *, u_char *
  returns     : a realloced and filled in u_char *
     
this function creates a byte-stream representation of the kadm_vals structure
*/
vals_to_stream(dt_in, dt_out)
Kadm_vals *dt_in;
u_char **dt_out;
{
  int vsloop, stsize;		/* loop counter, stream size */

  stsize = build_field_header(dt_in->fields, dt_out);
  for (vsloop=31; vsloop>=0; vsloop--)
    if (IS_FIELD(vsloop,dt_in->fields)) {
      switch (vsloop) {
      case KADM_NAME:
	  stsize+=vts_string(dt_in->name, dt_out, stsize);
	  break;
      case KADM_INST:
	  stsize+=vts_string(dt_in->instance, dt_out, stsize);
	  break;
      case KADM_EXPDATE:
	  stsize+=vts_long(dt_in->exp_date, dt_out, stsize);
	  break;
      case KADM_ATTR:
	  stsize+=vts_short(dt_in->attributes, dt_out, stsize);
	  break;
      case KADM_MAXLIFE:
	  stsize+=vts_char(dt_in->max_life, dt_out, stsize);
	  break;
      case KADM_DESKEY: 
	  stsize+=vts_long(dt_in->key_high, dt_out, stsize); 
	  stsize+=vts_long(dt_in->key_low, dt_out, stsize); 
	  break;
      default:
	  break;
      }
}
  return(stsize);
}  

build_field_header(cont, st)
u_char *cont;			/* container for fields data */
u_char **st;			/* stream */
{
  *st = (u_char *) malloc (4);
  memcpy((char *) *st, (char *) cont, 4);
  return 4;			/* return pointer to current stream location */
}

vts_string(dat, st, loc)
char *dat;			/* a string to put on the stream */
u_char **st;			/* base pointer to the stream */
int loc;			/* offset into the stream for current data */
{
  *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1));
  memcpy((char *)(*st + loc), dat, strlen(dat)+1);
  return strlen(dat)+1;
}

vts_short(dat, st, loc)
u_short dat;			/* the attributes field */
u_char **st;			/* a base pointer to the stream */
int loc;			/* offset into the stream for current data */
{
  u_short temp;			/* to hold the net order short */

  temp = htons(dat);		/* convert to network order */
  *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short)));
  memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short));
  return sizeof(u_short);
}

vts_long(dat, st, loc)
krb5_ui_4 dat;			/* the attributes field */
u_char **st;			/* a base pointer to the stream */
int loc;			/* offset into the stream for current data */
{
  krb5_ui_4 temp;			/* to hold the net order short */

  temp = htonl(dat);		/* convert to network order */
  *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb5_ui_4)));
  memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb5_ui_4));
  return sizeof(krb5_ui_4);
}

    
vts_char(dat, st, loc)
u_char dat;			/* the attributes field */
u_char **st;			/* a base pointer to the stream */
int loc;			/* offset into the stream for current data */
{
  *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char)));
  (*st)[loc] = (u_char) dat;
  return 1;
}
    
/* 
stream_to_vals
  recieves    : u_char *, kadm_vals *
  returns     : a kadm_vals filled in according to u_char *
     
this decodes a byte stream represntation of a vals struct into kadm_vals
*/
stream_to_vals(dt_in, dt_out, maxlen)
u_char *dt_in;
Kadm_vals *dt_out;
int maxlen;				/* max length to use */
{
  register int vsloop, stsize;		/* loop counter, stream size */
  register int status;

  memset((char *) dt_out, 0, sizeof(*dt_out));

  stsize = check_field_header(dt_in, dt_out->fields, maxlen);
  if (stsize < 0)
      return(-1);
  for (vsloop=31; vsloop>=0; vsloop--)
    if (IS_FIELD(vsloop,dt_out->fields))
      switch (vsloop) {
      case KADM_NAME:
	  if ((status = stv_string(dt_in, dt_out->name, stsize,
				   sizeof(dt_out->name), maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  break;
      case KADM_INST:
	  if ((status = stv_string(dt_in, dt_out->instance, stsize,
				   sizeof(dt_out->instance), maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  break;
      case KADM_EXPDATE:
	  if ((status = stv_long(dt_in, &dt_out->exp_date, stsize,
				 maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  break;
      case KADM_ATTR:
	  if ((status = stv_short(dt_in, &dt_out->attributes, stsize,
				  maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  break;
      case KADM_MAXLIFE:
	  if ((status = stv_char(dt_in, &dt_out->max_life, stsize,
				 maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  break;
      case KADM_DESKEY:
	  if ((status = stv_long(dt_in, &dt_out->key_high, stsize,
				    maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  if ((status = stv_long(dt_in, &dt_out->key_low, stsize,
				    maxlen)) < 0)
	      return(-1);
	  stsize += status;
	  break;
      default:
	  break;
      }
  return stsize;
}  

check_field_header(st, cont, maxlen)
u_char *st;			/* stream */
u_char *cont;			/* container for fields data */
int maxlen;
{
  if (4 > maxlen)
      return(-1);
  memcpy((char *) cont, (char *) st, 4);
  return 4;			/* return pointer to current stream location */
}

stv_string(st, dat, loc, stlen, maxlen)
register u_char *st;		/* base pointer to the stream */
char *dat;			/* a string to read from the stream */
register int loc;		/* offset into the stream for current data */
int stlen;			/* max length of string to copy in */
int maxlen;			/* max length of input stream */
{
  int maxcount;				/* max count of chars to copy */

  maxcount = min(maxlen - loc, stlen);

  (void) strncpy(dat, (char *)st + loc, maxcount);

  if (dat[maxcount-1]) /* not null-term --> not enuf room */
      return(-1);
  return strlen(dat)+1;
}

stv_short(st, dat, loc, maxlen)
u_char *st;			/* a base pointer to the stream */
u_short *dat;			/* the attributes field */
int loc;			/* offset into the stream for current data */
int maxlen;
{
  u_short temp;			/* to hold the net order short */

  if (loc + sizeof(u_short) > maxlen)
      return(-1);
  memcpy((char *) &temp, (char *) st+ loc, sizeof(u_short));
  *dat = ntohs(temp);		/* convert to network order */
  return sizeof(u_short);
}

stv_long(st, dat, loc, maxlen)
u_char *st;			/* a base pointer to the stream */
krb5_ui_4 *dat;			/* the attributes field */
int loc;			/* offset into the stream for current data */
int maxlen;			/* maximum length of st */
{
  krb5_ui_4 temp;			/* to hold the net order short */

  if (loc + sizeof(krb5_ui_4) > maxlen)
      return(-1);
  memcpy((char *) &temp, (char *) st + loc, sizeof(krb5_ui_4));
  *dat = ntohl(temp);		/* convert to network order */
  return sizeof(krb5_ui_4);
}
    
stv_char(st, dat, loc, maxlen)
u_char *st;			/* a base pointer to the stream */
u_char *dat;			/* the attributes field */
int loc;			/* offset into the stream for current data */
int maxlen;
{
  if (loc + 1 > maxlen)
      return(-1);
  *dat = *(st + loc);
  return 1;
}

