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