1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 */
27
28/* $Id: write.c 146 2006-03-24 00:26:54Z njacobs $ */
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/types.h>
36#include <netinet/in.h>
37#include <inttypes.h>
38
39#include <papi.h>
40#include <ipp.h>
41
42static int8_t
43papi_attribute_to_ipp_type(papi_attribute_value_type_t type)
44{
45	switch (type) {
46	case PAPI_INTEGER:
47		return (VTAG_INTEGER);
48	case PAPI_BOOLEAN:
49		return (VTAG_BOOLEAN);
50	case PAPI_RANGE:
51		return (VTAG_RANGE_OF_INTEGER);
52	case PAPI_RESOLUTION:
53		return (VTAG_RESOLUTION);
54	case PAPI_DATETIME:
55		return (VTAG_DATE_TIME);
56	case PAPI_STRING:
57		return (VTAG_TEXT_WITHOUT_LANGUAGE);
58	}
59
60	return (0);
61}
62
63static papi_status_t
64papi_ipp_type_match(papi_attribute_value_type_t papi, int8_t ipp)
65{
66	switch (papi) {
67	case PAPI_STRING:
68		switch (ipp) {
69		case VTAG_URI:
70		case VTAG_OCTET_STRING:
71		case VTAG_TEXT_WITHOUT_LANGUAGE:
72		case VTAG_URI_SCHEME:
73		case VTAG_CHARSET:
74		case VTAG_NATURAL_LANGUAGE:
75		case VTAG_MIME_MEDIA_TYPE:
76		case VTAG_NAME_WITHOUT_LANGUAGE:
77		case VTAG_KEYWORD:
78			break;
79		default:
80			return (PAPI_CONFLICT);
81		}
82		break;
83	case PAPI_INTEGER:
84		switch (ipp) {
85		case VTAG_ENUM:
86		case VTAG_INTEGER:
87			break;
88		default:
89			return (PAPI_CONFLICT);
90		}
91		break;
92	case PAPI_BOOLEAN:
93		if (ipp != VTAG_BOOLEAN)
94			return (PAPI_CONFLICT);
95		break;
96	case PAPI_RANGE:
97		if (ipp != VTAG_RANGE_OF_INTEGER)
98			return (PAPI_CONFLICT);
99		break;
100	case PAPI_RESOLUTION:
101		if (ipp != VTAG_RESOLUTION)
102			return (PAPI_CONFLICT);
103		break;
104	case PAPI_DATETIME:
105		if (ipp != VTAG_DATE_TIME)
106			return (PAPI_CONFLICT);
107		break;
108	case PAPI_COLLECTION:
109		/* don't need to match */
110		break;
111	}
112
113	return (PAPI_OK);
114}
115
116static papi_status_t
117ipp_write_attribute(ipp_writer_t iwrite, void *fd, papi_attribute_t *attribute)
118{
119	papi_status_t status;
120	papi_attribute_value_t	**values;
121	int8_t type;
122	int i;
123	char *name;
124
125	name = attribute->name;
126	values = attribute->values;
127
128	if ((type = name_to_ipp_type(name)) == 0)
129		type = papi_attribute_to_ipp_type(attribute->type);
130
131	/* The types don't match, so don't send the attribute */
132	if ((status = papi_ipp_type_match(attribute->type, type)) != PAPI_OK)
133		return (status);
134
135	if (values == NULL) {
136		uint16_t length;
137
138		type = VTAG_UNSUPPORTED;
139		if (iwrite(fd, &type, 1) != 1)
140			return (PAPI_DEVICE_ERROR);
141
142		if (name != NULL) {	/* first value gets named */
143			length = (uint16_t)htons(strlen(name));
144
145			if (iwrite(fd, &length, 2) != 2)
146				return (PAPI_DEVICE_ERROR);
147			if (iwrite(fd, name, strlen(name)) != strlen(name))
148				return (PAPI_DEVICE_ERROR);
149		}
150
151		length = (uint16_t)htons(0);
152		if (iwrite(fd, &length, 2) != 2)
153			return (PAPI_DEVICE_ERROR);
154
155		return (PAPI_OK);
156	}
157
158
159
160	for (i = 0; values[i] != NULL; i++) {
161		papi_attribute_value_t	*value = values[i];
162		uint16_t length = 0;
163
164		if (iwrite(fd, &type, 1) != 1)
165			return (PAPI_DEVICE_ERROR);
166
167		if (name != NULL) {	/* first value gets named */
168			length = (uint16_t)htons(strlen(name));
169
170			if (iwrite(fd, &length, 2) != 2)
171				return (PAPI_DEVICE_ERROR);
172			if (iwrite(fd, name, strlen(name)) != strlen(name))
173				return (PAPI_DEVICE_ERROR);
174			name = NULL;
175		} else {
176			length = (uint16_t)htons(0);
177
178			if (iwrite(fd, &length, 2) != 2)
179				return (PAPI_DEVICE_ERROR);
180		}
181
182		switch (attribute->type) {
183		case PAPI_STRING: {
184			char *v = (char *)value->string;
185
186			if (v != NULL) {
187				size_t str_length = strlen(v);
188
189				/*
190				 * if the length is more than 16 bits can
191				 * express, send what can be represented
192				 * in 16 bits. IPP "strings" can only be
193				 * that large.
194				 */
195				if (str_length > 0xFFFF)
196					str_length = 0xFFFF;
197
198				length = (uint16_t)htons(str_length);
199				if (iwrite(fd, &length, 2) != 2)
200					return (PAPI_DEVICE_ERROR);
201				if (iwrite(fd, v, str_length) != str_length)
202					return (PAPI_DEVICE_ERROR);
203			} else
204				if (iwrite(fd, &length, 2) != 2)
205					return (PAPI_DEVICE_ERROR);
206			}
207			break;
208		case PAPI_BOOLEAN: {
209			int8_t v = (int8_t)value->boolean;
210
211			length = (uint16_t)htons(1);
212			if (iwrite(fd, &length, 2) != 2)
213				return (PAPI_DEVICE_ERROR);
214			if (iwrite(fd, &v, 1) != 1)
215				return (PAPI_DEVICE_ERROR);
216			}
217			break;
218		case PAPI_INTEGER: {
219			int32_t v = (int32_t)value->integer;
220
221			length = (uint16_t)htons(4);
222			v = (int32_t)htonl(v);
223			if (iwrite(fd, &length, 2) != 2)
224				return (PAPI_DEVICE_ERROR);
225			if (iwrite(fd, &v, 4) != 4)
226				return (PAPI_DEVICE_ERROR);
227			}
228			break;
229		case PAPI_RANGE: {
230			int32_t min = (int32_t)htonl((int)(value->range).lower),
231				max = (int32_t)htonl((int)(value->range).upper);
232
233			length = (uint16_t)htons(8);
234			if (iwrite(fd, &length, 2) != 2)
235				return (PAPI_DEVICE_ERROR);
236			if (iwrite(fd, &min, 4) != 4)
237				return (PAPI_DEVICE_ERROR);
238			if (iwrite(fd, &max, 4) != 4)
239				return (PAPI_DEVICE_ERROR);
240			}
241			break;
242		case PAPI_RESOLUTION: {
243			int32_t x = (int)(value->resolution).xres,
244				y = (int)(value->resolution).yres;
245			int8_t units = (int8_t)(value->resolution).units;
246
247			length = (uint16_t)htons(9);
248			x = (int32_t)htonl(x);
249			y = (int32_t)htonl(y);
250
251			if (iwrite(fd, &length, 2) != 2)
252				return (PAPI_DEVICE_ERROR);
253			if (iwrite(fd, &x, 4) != 4)
254				return (PAPI_DEVICE_ERROR);
255			if (iwrite(fd, &y, 4) != 4)
256				return (PAPI_DEVICE_ERROR);
257			if (iwrite(fd, &units, 1) != 1)
258				return (PAPI_DEVICE_ERROR);
259			}
260			break;
261		case PAPI_DATETIME: {
262			struct tm *v = gmtime(&value->datetime);
263			int8_t c;
264			uint16_t s;
265
266			length = (uint16_t)htons(11);
267			if (iwrite(fd, &length, 2) != 2)
268				return (PAPI_DEVICE_ERROR);
269			s = (uint16_t)htons(v->tm_year + 1900);
270			if (iwrite(fd, &s, 2) != 2)
271				return (PAPI_DEVICE_ERROR);
272			c = v->tm_mon + 1;
273			if (iwrite(fd, &c, 1) != 1)
274				return (PAPI_DEVICE_ERROR);
275			c = v->tm_mday;
276			if (iwrite(fd, &c, 1) != 1)
277				return (PAPI_DEVICE_ERROR);
278			c = v->tm_hour;
279			if (iwrite(fd, &c, 1) != 1)
280				return (PAPI_DEVICE_ERROR);
281			c = v->tm_min;
282			if (iwrite(fd, &c, 1) != 1)
283				return (PAPI_DEVICE_ERROR);
284			c = v->tm_sec;
285			if (iwrite(fd, &c, 1) != 1)
286				return (PAPI_DEVICE_ERROR);
287			c = /* v->deciseconds */ 0;
288			if (iwrite(fd, &c, 1) != 1)
289				return (PAPI_DEVICE_ERROR);
290			c = /* v->utc_dir */ 0;
291			if (iwrite(fd, &c, 1) != 1)
292				return (PAPI_DEVICE_ERROR);
293			c = /* v->utc_hours */ 0;
294			if (iwrite(fd, &c, 1) != 1)
295				return (PAPI_DEVICE_ERROR);
296			c = /* v->utc_minutes */ 0;
297			if (iwrite(fd, &c, 1) != 1)
298				return (PAPI_DEVICE_ERROR);
299			}
300			break;
301		default: {
302			/*
303			 * If there is a value, it is not one of our
304			 * types, so we couldn't use it anyway.  We assume
305			 * that it was an OOB type with no value
306			 */
307			length = (uint16_t)htons(0);
308			if (iwrite(fd, &length, 2) != 2)
309				return (PAPI_DEVICE_ERROR);
310			}
311			break;
312		}
313	}
314
315	return (PAPI_OK);
316}
317
318static papi_status_t
319ipp_write_attribute_group(ipp_writer_t iwrite, void *fd, int8_t type,
320		papi_attribute_t **attributes)
321{
322	papi_status_t result = PAPI_OK;
323	int i;
324
325	/* write group tag */
326	if (iwrite(fd, &type, 1) != 1)
327		return (PAPI_DEVICE_ERROR);
328
329	/* write values */
330	for (i = 0; ((attributes[i] != NULL) && (result == PAPI_OK)); i++)
331		result = ipp_write_attribute(iwrite, fd, attributes[i]);
332
333	return (result);
334}
335
336static papi_status_t
337ipp_write_attribute_groups(ipp_writer_t iwrite, void *fd,
338		papi_attribute_t **groups)
339{
340	papi_status_t result = PAPI_OK;
341	int8_t	c;
342
343	for (c = DTAG_MIN; c <= DTAG_MAX; c++) {
344		papi_status_t status;
345		papi_attribute_t **group = NULL;
346		void *iter = NULL;
347		char name[32];
348
349		(void) ipp_tag_string(c, name, sizeof (name));
350		for (status = papiAttributeListGetCollection(groups, &iter,
351						name, &group);
352			((status == PAPI_OK) && (result == PAPI_OK));
353			status = papiAttributeListGetCollection(groups, &iter,
354						NULL, &group))
355				result = ipp_write_attribute_group(iwrite, fd,
356								c, group);
357	}
358
359	c = DTAG_END_OF_ATTRIBUTES;
360	if (iwrite(fd, &c, 1) != 1)
361		result = PAPI_DEVICE_ERROR;
362
363	return (result);
364}
365
366static papi_status_t
367ipp_write_message_header(ipp_writer_t iwrite, void *fd,
368		papi_attribute_t **message)
369{
370	int tmp;
371	int8_t c;
372	uint16_t s;
373	int32_t i;
374
375	/* write the version */
376	papiAttributeListGetInteger(message, NULL, "version-major", &tmp);
377	c = tmp;
378	if (iwrite(fd, &c, 1) != 1)
379		return (PAPI_DEVICE_ERROR);
380
381	papiAttributeListGetInteger(message, NULL, "version-minor", &tmp);
382	c = tmp;
383	if (iwrite(fd, &c, 1) != 1)
384		return (PAPI_DEVICE_ERROR);
385
386	/* write the request/status code */
387	papiAttributeListGetInteger(message, NULL, "status-code", &tmp);
388	papiAttributeListGetInteger(message, NULL, "operation-id", &tmp);
389	s = (uint16_t)htons(tmp);
390	if (iwrite(fd, &s, 2) != 2)
391		return (PAPI_DEVICE_ERROR);
392
393	/* write the request id */
394	papiAttributeListGetInteger(message, NULL, "request-id", &tmp);
395	i = (uint32_t)htonl(tmp);
396	if (iwrite(fd, &i, 4) != 4)
397		return (PAPI_DEVICE_ERROR);
398
399	return (PAPI_OK);
400}
401
402papi_status_t
403ipp_write_message(ipp_writer_t iwrite, void *fd, papi_attribute_t **message)
404{
405	papi_status_t result;
406
407	if ((iwrite == NULL) || (fd == NULL) || (message == NULL))
408		return (PAPI_BAD_ARGUMENT);
409
410	result = ipp_write_message_header(iwrite, fd, message);
411	if (result == PAPI_OK)
412		result = ipp_write_attribute_groups(iwrite, fd, message);
413
414	return (result);
415}
416