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 
42 static int8_t
43 papi_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 
63 static papi_status_t
64 papi_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 
116 static papi_status_t
117 ipp_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 
318 static papi_status_t
319 ipp_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 
336 static papi_status_t
337 ipp_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 
366 static papi_status_t
367 ipp_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 
402 papi_status_t
403 ipp_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