/*
/* Utility functions for working with client_cert_info structures, which is
 * used to encode a certificate's information in a buffer for transmission
 * to another thread/process.
 *
 * Author:	David Jones
 */
#include <stdio.h>
#include <stdlib.h>
#include "tutil.h"
#include "client_cert.h"	/* verify prototypes */

client_cert_info *client_cert_info_init ( void *buffer, int buf_size )
{
    int i;
    /*
     * Initialize a buffer as an cert_info structure, setting dn_limit
     * to ensure no overflow.
     */
    client_cert_info *info;
    info = (client_cert_info *) buffer;

    if ( buf_size < sizeof(client_cert_info) ) return (client_cert_info *) 0;

    info->d_limit = buf_size - (sizeof(client_cert_info) - sizeof(info->data));
    info->d_used = 0;
    info->error = 0;
    for ( i = 0; i < 4; i++ ) info->length[i] = 0;

    return info;
}

/*
 * The validate function verifies the internally stored length is
 * consistent with the final message length.
 */
client_cert_info *client_cert_info_validate ( void *buffer, int buf_len )
{
    int i, offset;
    client_cert_info *info;
    info = (client_cert_info *) buffer;

#ifdef DEBUG
    printf ( "Validating %d long buffer, limit: %d, used: %d, length: %d %d %d %d\n",
    buf_len, info->d_limit, info->d_used, info->length[0], info->length[1],
    info->length[2], info->length[3] );
#endif
    if ( info->d_limit < info->d_used ) return (client_cert_info *) 0;
    if ( info->d_used != 
	(buf_len-(sizeof(client_cert_info)-sizeof(info->data))) ) {
	/*
	 * Inconsitent internal and external size.
	 */
#ifdef DEBUG
	printf ( "d_used != (buf_len-header) (%d != %d-%d)\n", info->d_used,
		buf_len, (sizeof(client_cert_info)-sizeof(info->data)) );
#endif
	return (client_cert_info *) 0;
    }
    /*
     * Check the lengths are valid and that the total equals info->d_used.
     */
    if ( (info->length[0] < 0) || info->length[0] > 3 ) {
#ifdef DEBUG
	printf ( "string count invalid in buffer\n" ) ;
#endif
	return (client_cert_info *) 0;
    }
    for ( offset = 0, i = 1; i <= info->length[0]; i++ ) {
	if ( info->length[i] < 0 ) printf ( "string[%d] length invalid\n", i);
	if ( info->length[i] < 0 ) return (client_cert_info *) 0;
	offset += info->length[i];
#ifdef DEBUG
	if ( offset > info->d_used ) printf ( "string[%d] overflows\n", i );
#endif
	if ( offset > info->d_used ) return (client_cert_info *) 0;
    }
#ifdef DEBUG
    if ( offset != info->d_used ) printf ( "final offset wrong: %d\n", offset);
#endif
    if ( offset != info->d_used ) return (client_cert_info *) 0;
    return info;
}
/*
 * Add_name appends a formatted X509 certificate data to the list
 * stored internally.
 */
int client_cert_info_add_item ( unsigned char *buffer, int buflen, 
	client_cert_info *info )
{
    int i, mask, offset;
    /*
     * Get length of name.
     */
    offset = info->d_used;
    if ( buflen > (info->d_limit - offset) ) {
	/* Not enough room left. */
	return 0;
    }
    if ( buflen < 0 ) buflen = 0;
    if ( info->length[0] >= 3 ) {
	/* No more length slots left. */
	return 0;
    }
    /*
     * Copy buffer into data region and adjust size.
     */
    info->length[0]++;
    info->length[info->length[0]] = buflen;
    for ( i = 0; i < buflen; i++ ) {
	info->data[offset++] = buffer[i];
    }
    info->d_used = offset;
    return 1;
}
