OpenSolaris_b135/lib/print/libipp-core/common/read.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 */

/* $Id: read.c 146 2006-03-24 00:26:54Z njacobs $ */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <inttypes.h>

#include <papi.h>
#include <ipp.h>


#define	_ipp_tag_string(id) ipp_tag_string((id), buf, sizeof (buf))

static papi_status_t
read_name_with_language(ipp_reader_t iread, void *fd,
			papi_attribute_t ***message)
{
	char *string;
	uint16_t size;

	/* read the language */
	if (iread(fd, &size, 2) != 2) {
		ipp_set_status(message, PAPI_BAD_REQUEST,
				"read failed: lang len\n");
		return (PAPI_BAD_REQUEST);
	}
	size = (uint16_t)ntohs(size);

	if ((string = alloca(size + 1)) == NULL) {
		ipp_set_status(message, PAPI_TEMPORARY_ERROR,
				"Memory allocation failed");
		return (PAPI_TEMPORARY_ERROR);
	}
	if (iread(fd, string, size) != size) {
		ipp_set_status(message, PAPI_BAD_REQUEST,
				"read failed: lang\n");
		return (PAPI_BAD_REQUEST);
	}

	/* read the text */
	if (iread(fd, &size, 2) != 2) {
		ipp_set_status(message, PAPI_BAD_REQUEST,
				"read failed: text len\n");
		return (PAPI_BAD_REQUEST);
	}
	size = (uint16_t)ntohs(size);

	if ((string = alloca(size + 1)) == NULL) {
		ipp_set_status(message, PAPI_TEMPORARY_ERROR,
				"Memory allocation failed");
		return (PAPI_TEMPORARY_ERROR);
	}
	if (iread(fd, string, size) != size) {
		ipp_set_status(message, PAPI_BAD_REQUEST,
				"read failed: text\n");
		return (PAPI_BAD_REQUEST);
	}

	return (PAPI_OK);
}


static struct {
	int8_t	ipp_type;
	int8_t	size;
} type_info[] = {
	{ VTAG_INTEGER,			4 },
	{ VTAG_ENUM,			4 },
	{ VTAG_BOOLEAN,			1 },
	{ VTAG_RANGE_OF_INTEGER,	8 },
	{ VTAG_RESOLUTION,		9 },
	{ VTAG_DATE_TIME,		11 },
	{ DTAG_MIN,			0 }
};

/* verify that the IPP type and size are compatible */
static int
validate_length(int8_t type, int8_t size)
{
	int i;

	for (i = 0; type_info[i].ipp_type != DTAG_MIN; i++)
		if (type_info[i].ipp_type == type)
			return ((type_info[i].size == size) ? 0 : -1);
	return (0);
}

/* convert tyep IPP type to a type that is marginally compatible */
static int8_t
base_type(int8_t i)
{
	switch (i) {
	case VTAG_ENUM:
	case VTAG_INTEGER:
		return (VTAG_INTEGER);
	case VTAG_URI:
	case VTAG_OCTET_STRING:
	case VTAG_TEXT_WITHOUT_LANGUAGE:
	case VTAG_URI_SCHEME:
	case VTAG_CHARSET:
	case VTAG_NATURAL_LANGUAGE:
	case VTAG_MIME_MEDIA_TYPE:
	case VTAG_NAME_WITHOUT_LANGUAGE:
	case VTAG_KEYWORD:
		return (VTAG_TEXT_WITHOUT_LANGUAGE);
	case VTAG_BOOLEAN:
	case VTAG_RANGE_OF_INTEGER:
	case VTAG_DATE_TIME:
	case VTAG_RESOLUTION:
	default:
		return (i);
	}
}

/* verify that the IPP type is correct for the named attribute */
static papi_status_t
validate_type(char *name, int8_t type)
{
	int8_t t = name_to_ipp_type(name);

	if (t == 0)		/* The attribute is not defined in the RFC */
		return (PAPI_NOT_FOUND);
	else if (t == type)	/* The supplied type matched the RFC type */
		return (PAPI_OK);
	else {			/* The supplied type doesn't match the RFC */
		if (base_type(t) == base_type(type))
			return (PAPI_OK);

		return (PAPI_CONFLICT);
	}
}

/* verify that the IPP value is within specification for the named attribute */
static int
validate_value(papi_attribute_t ***message, char *name, int8_t type, ...)
{
#define	within(a, b, c)	((b >= a) && (b <= c))
	va_list ap;
	int rc = -1;
	int min = min_val_len(type, name),
	    max = max_val_len(type, name);
	char buf[64];	/* For _ipp_<...>_string() */

	va_start(ap, type);
	switch (type) {
	case VTAG_ENUM:
	case VTAG_INTEGER: {
		int32_t i = (int32_t)va_arg(ap, int32_t);

		if (within(min, i, max))
			rc = 0;
		else
			ipp_set_status(message, PAPI_BAD_ARGUMENT,
				"%s(%s): %d: out of range (%d - %d)", name,
				_ipp_tag_string(type), i, min, max);
		}
		break;
	case VTAG_BOOLEAN: {
		int8_t v = (int8_t)va_arg(ap, int);

		if (within(0, v, 1))
			rc = 0;
		else
			ipp_set_status(message, PAPI_BAD_ARGUMENT,
				"%s(%s): %d: out of range (0 - 1)", name,
				_ipp_tag_string(type), v);
		}
		break;
	case VTAG_RANGE_OF_INTEGER: {
		int32_t lower = (int32_t)va_arg(ap, int32_t);
		int32_t upper = (int32_t)va_arg(ap, int32_t);

		if (within(min, lower, max) &&
		    within(min, upper, max))
			rc = 0;
		else
			ipp_set_status(message, PAPI_BAD_ARGUMENT,
				"%s(%s): %d - %d: out of range (%d - %d)", name,
				_ipp_tag_string(type), lower, upper, min, max);
		}
		break;
	case VTAG_URI:
	case VTAG_OCTET_STRING:
	case VTAG_TEXT_WITHOUT_LANGUAGE:
	case VTAG_URI_SCHEME:
	case VTAG_CHARSET:
	case VTAG_NATURAL_LANGUAGE:
	case VTAG_MIME_MEDIA_TYPE:
	case VTAG_NAME_WITHOUT_LANGUAGE: {
		char *v = (char *)va_arg(ap, char *);

		if (strlen(v) < max)
			rc = 0;
		else
			ipp_set_status(message, PAPI_BAD_ARGUMENT,
				"%s(%s): %s: too long (max length: %d)", name,
				_ipp_tag_string(type), v, max);
		}
		break;
	case VTAG_KEYWORD: {
		char *v = (char *)va_arg(ap, char *);

		if (strlen(v) >= max)
			ipp_set_status(message, PAPI_BAD_ARGUMENT,
				"%s(%s): %s: too long (max length: %d)", name,
				_ipp_tag_string(type), v, max);
		else if (is_keyword(v) == 0)
			ipp_set_status(message, PAPI_BAD_ARGUMENT,
				"%s(%s): %s: invalid keyword", name,
				_ipp_tag_string(type), v);
		else
			rc = 0;
		}
		break;
	case VTAG_DATE_TIME:
	case VTAG_RESOLUTION:
	default:
		rc = 0;
	}
	va_end(ap);

	return (rc);
#undef within
}

/*
 * read_attr_group() reads in enough of the message data to parse an entire
 * attribute group.  Since to determine that the group is finished you have to
 * read the character that determines the type of the next group, this function
 * must return that character, in order that our caller knows how to call us for
 * the next group.  Thus type is used both as an input parameter (the type of
 * attribute group to read in) and an output parameter (the type of the next
 * attribute group).
 */

static papi_status_t
ipp_read_attribute_group(ipp_reader_t iread, void *fd, int8_t *type,
			papi_attribute_t ***message)
{
	int8_t value_tag;
	uint16_t name_length, value_length;
	papi_attribute_t **attributes = NULL;
	char *name = NULL;
	int i;
	char buf[64];	/* For _ipp_<...>_string() */

	/*
	 * RFC2910 3.3 says we need to handle `An expected but missing
	 * "begin-attribute-group-tag" field.  How?
	 */
	if (*type > DTAG_MAX)  {
		/* Scream bloody murder, or assign a new type? */
		ipp_set_status(message, PAPI_BAD_REQUEST,
			"Bad attribute group tag 0x%.2hx (%s)",
			*type, _ipp_tag_string(*type));
		return (PAPI_BAD_REQUEST);
	}

	/* This loops through *values* not *attributes*! */
	for (i = 0; ; i++) {
		papi_status_t valid = PAPI_OK;
		if (iread(fd, &value_tag, 1) != 1) {
			ipp_set_status(message, PAPI_BAD_REQUEST,
				"bad read: value tag\n");
			return (PAPI_BAD_REQUEST);
		}
		/* are we done with this group ? */
		if (value_tag <= DTAG_MAX)
			break;

		if (iread(fd, &name_length, 2) != 2) {
			ipp_set_status(message, PAPI_BAD_REQUEST,
				"bad read: name length\n");
			return (PAPI_BAD_REQUEST);
		}
		name_length = (uint16_t)ntohs(name_length);

		/* Not just another value for the previous attribute */
		if (name_length != 0) {
			if ((name = alloca(name_length + 1)) == NULL) {
				ipp_set_status(message, PAPI_TEMPORARY_ERROR,
					"alloca(): failed\n");
				return (PAPI_TEMPORARY_ERROR);
			}
			(void) memset(name, 0, name_length + 1);

			if (iread(fd, name, name_length) != name_length) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: name\n");
				return (PAPI_BAD_REQUEST);
			}
		}

		valid = validate_type(name, value_tag);
		if ((valid != PAPI_OK) && (valid != PAPI_NOT_FOUND))
			ipp_set_status(message, valid, "%s(%s): %s", name,
				_ipp_tag_string(value_tag),
				papiStatusString(valid));

		if (iread(fd, &value_length, 2) != 2) {
			ipp_set_status(message, PAPI_BAD_REQUEST,
				"bad read: value length\n");
			return (PAPI_BAD_REQUEST);
		}
		value_length = (uint16_t)ntohs(value_length);

		if (validate_length(value_tag, value_length) < 0) {
			ipp_set_status(message, PAPI_BAD_REQUEST,
				"Bad value length (%d) for type %s",
				value_length, _ipp_tag_string(value_tag));
			return (PAPI_BAD_REQUEST);
		}

		switch (value_tag) {
		case VTAG_INTEGER:
		case VTAG_ENUM: {
			int32_t v;

			if (iread(fd, &v, value_length) != value_length) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: int/enum\n");
				return (PAPI_BAD_REQUEST);
			}
			v = (int32_t)ntohl(v);
			(void) validate_value(message, name, value_tag, v);
			papiAttributeListAddInteger(&attributes,
						PAPI_ATTR_APPEND, name, v);

			}
			break;
		case VTAG_BOOLEAN: {
			int8_t v;

			if (iread(fd, &v, value_length) != value_length) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: boolean\n");
				return (PAPI_BAD_REQUEST);
			}
			(void) validate_value(message, name, value_tag, v);
			papiAttributeListAddBoolean(&attributes,
						PAPI_ATTR_APPEND, name, v);
			}
			break;
		case VTAG_RANGE_OF_INTEGER: {
			int32_t min, max;

			if (iread(fd, &min, 4) != 4) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: min\n");
				return (PAPI_BAD_REQUEST);
			}
			if (iread(fd, &max, 4) != 4) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: max\n");
				return (PAPI_BAD_REQUEST);
			}
			min = (int32_t)ntohl(min);
			max = (int32_t)ntohl(max);
			(void) validate_value(message, name, value_tag,
					min, max);
			papiAttributeListAddRange(&attributes, PAPI_ATTR_APPEND,
						name, min, max);
			}
			break;
		case VTAG_RESOLUTION: {
			int32_t x, y;
			int8_t units;

			if (iread(fd, &x, 4) != 4) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: x\n");
				return (PAPI_BAD_REQUEST);
			}
			if (iread(fd, &y, 4) != 4) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: y\n");
				return (PAPI_BAD_REQUEST);
			}
			if (iread(fd, &units, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: units\n");
				return (PAPI_BAD_REQUEST);
			}
			x = (int32_t)ntohl(x);
			y = (int32_t)ntohl(y);
			papiAttributeListAddResolution(&attributes,
						PAPI_ATTR_APPEND, name, x, y,
						(papi_resolution_unit_t)units);
			}
			break;
		case VTAG_DATE_TIME: {
			struct tm tm;
			time_t v;
			int8_t c;
			uint16_t s;

			(void) memset(&tm, 0, sizeof (tm));
			if (iread(fd, &s, 2) != 2) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: year\n");
				return (PAPI_BAD_REQUEST);
			}
			tm.tm_year = (uint16_t)ntohs(s) - 1900;
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: month\n");
				return (PAPI_BAD_REQUEST);
			}
			tm.tm_mon = c - 1;
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: day\n");
				return (PAPI_BAD_REQUEST);
			}
			tm.tm_mday = c;
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: hour\n");
				return (PAPI_BAD_REQUEST);
			}
			tm.tm_hour = c;
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: minutes\n");
				return (PAPI_BAD_REQUEST);
			}
			tm.tm_min = c;
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: seconds\n");
				return (PAPI_BAD_REQUEST);
			}
			tm.tm_sec = c;
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: decisec\n");
				return (PAPI_BAD_REQUEST);
			}
			/* tm.deciseconds = c; */
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: utc_dir\n");
				return (PAPI_BAD_REQUEST);
			}
			/* tm.utc_dir = c; */
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: utc_hour\n");
				return (PAPI_BAD_REQUEST);
			}
			/* tm.utc_hours = c; */
			if (iread(fd, &c, 1) != 1) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: utc_min\n");
				return (PAPI_BAD_REQUEST);
			}
			/* tm.utc_minutes = c; */

			v = mktime(&tm);

			(void) validate_value(message, name, value_tag, v);
			papiAttributeListAddDatetime(&attributes,
						PAPI_ATTR_APPEND, name, v);
			}
			break;
		case VTAG_NAME_WITH_LANGUAGE:
		case VTAG_TEXT_WITH_LANGUAGE:
			/*
			 * we are dropping this because we don't support
			 * name with language at this time.
			 */
			(void) read_name_with_language(iread, fd, message);
			break;
		case VTAG_NAME_WITHOUT_LANGUAGE:
		case VTAG_TEXT_WITHOUT_LANGUAGE:
		case VTAG_URI:
		case VTAG_KEYWORD:
		case VTAG_CHARSET: {
			char *v;

			if ((v = calloc(1, value_length + 1)) == NULL) {
				ipp_set_status(message, PAPI_TEMPORARY_ERROR,
					"calloc(): failed\n");
				return (PAPI_TEMPORARY_ERROR);
			}
#ifdef NOTDEF
			if (iread(fd, v, value_length) != value_length) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: stringy\n");
				return (PAPI_BAD_REQUEST);
			}
#else
			{
			int rc, i = value_length;
			char *p = v;

			while ((rc = iread(fd, p, i)) != i) {
				if (rc <= 0) {
					ipp_set_status(message,
						PAPI_BAD_REQUEST,
						"bad read: stringy\n");
					return (PAPI_BAD_REQUEST);
				}
				i -= rc;
				p += rc;
			}
			}
#endif
			(void) validate_value(message, name, value_tag, v);
			papiAttributeListAddString(&attributes,
						PAPI_ATTR_APPEND, name, v);
			}
			break;
		case VTAG_UNKNOWN:
		case VTAG_NOVALUE:
		case VTAG_UNSUPPORTED:
			papiAttributeListAddValue(&attributes, PAPI_ATTR_EXCL,
					name, PAPI_COLLECTION, NULL);
			break;
		default: {
			char *v;

			if ((v = calloc(1, value_length + 1)) == NULL) {
				ipp_set_status(message, PAPI_TEMPORARY_ERROR,
					"calloc(): failed\n");
				return (PAPI_TEMPORARY_ERROR);
			}
			if (iread(fd, v, value_length) != value_length) {
				ipp_set_status(message, PAPI_BAD_REQUEST,
					"bad read: other\n");
				return (PAPI_BAD_REQUEST);
			}
			papiAttributeListAddString(&attributes,
						PAPI_ATTR_APPEND, name, v);
			}
			break;
		}
	}

	if (attributes != NULL) {
		char name[32];

		(void) ipp_tag_string(*type, name, sizeof (name));
		papiAttributeListAddCollection(message, PAPI_ATTR_APPEND, name,
					attributes);
	}

	*type = value_tag;

	return (PAPI_OK);
}


static papi_status_t
ipp_read_header(ipp_reader_t iread, void *fd, papi_attribute_t ***message,
		char type)
{
	char *attr_name = "status-code";	/* default to a response */
	char buf[8];
	int8_t c;
	uint16_t s;
	int32_t i;

	if ((iread == NULL) || (fd == NULL) || (message == NULL))
		return (PAPI_BAD_ARGUMENT);

	/*
	 * Apache 1.X uses the buffer supplied to it's read call to read in
	 * the chunk size when chunking is used.  This causes problems
	 * reading the header a piece at a time, because we don't have
	 * enough room to read in the chunk size prior to reading the
	 * chunk.
	 */

	if (iread(fd, buf, 8) != 8)
		return (PAPI_BAD_REQUEST);

	c = buf[0];
	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
				"version-major", c);

	c = buf[1];
	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
				"version-minor", c);

	memcpy(&s, &buf[2], 2);
	s = (uint16_t)ntohs(s);
	if (type == IPP_TYPE_REQUEST)
		attr_name = "operation-id";
	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
				attr_name, s);

	memcpy(&i, &buf[4], 4);
	i = (uint32_t)ntohl(i);
	(void) papiAttributeListAddInteger(message, PAPI_ATTR_REPLACE,
				"request-id", i);

	return (PAPI_OK);
}

static papi_status_t
ipp_read_attribute_groups(ipp_reader_t iread, void *fd,
			papi_attribute_t ***message)
{
	papi_status_t result = PAPI_OK;
	int8_t tag;

	/* start reading the attribute groups */
	if (iread(fd, &tag, 1) != 1)	/* prime the pump */
		return (PAPI_BAD_REQUEST);

	while ((tag != DTAG_END_OF_ATTRIBUTES) && (result == PAPI_OK)) {
		result = ipp_read_attribute_group(iread, fd, &tag, message);
	}

	return (result);
}

papi_status_t
ipp_read_message(ipp_reader_t iread, void *fd, papi_attribute_t ***message,
		char type)
{
	papi_status_t result = PAPI_OK;

	if ((iread == NULL) || (fd == NULL) || (message == NULL))
		return (PAPI_BAD_ARGUMENT);

	result = ipp_read_header(iread, fd, message, type);
	if (result == PAPI_OK)
		result = ipp_read_attribute_groups(iread, fd, message);

	return (result);
}