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: ipp-listener.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 <netinet/in.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <syslog.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 
43 #include <papi.h>
44 #include <ipp-listener.h>
45 
46 typedef papi_status_t (ipp_handler_t)(papi_service_t svc,
47 					papi_attribute_t **request,
48 					papi_attribute_t ***response,
49 					ipp_reader_t iread, void *fd);
50 
51 /*
52  * protocol request handlers are inserted below.  The handler must be
53  * declared extern immediately below this comment and then an entry
54  * must be inserted in the "handlers" table a little further down.
55  */
56 extern ipp_handler_t ipp_print_job;
57 extern ipp_handler_t ipp_validate_job;
58 extern ipp_handler_t ipp_create_job;
59 extern ipp_handler_t ipp_get_printer_attributes;
60 extern ipp_handler_t ipp_get_jobs;
61 extern ipp_handler_t ipp_pause_printer;
62 extern ipp_handler_t ipp_resume_printer;
63 extern ipp_handler_t ipp_disable_printer;
64 extern ipp_handler_t ipp_enable_printer;
65 extern ipp_handler_t ipp_purge_jobs;
66 extern ipp_handler_t ipp_send_document;
67 extern ipp_handler_t ipp_cancel_job;
68 extern ipp_handler_t ipp_get_job_attributes;
69 extern ipp_handler_t ipp_release_job;
70 extern ipp_handler_t ipp_hold_job;
71 extern ipp_handler_t ipp_restart_job;
72 extern ipp_handler_t ipp_set_job_attributes;
73 extern ipp_handler_t ipp_set_printer_attributes;
74 extern ipp_handler_t cups_get_default;
75 extern ipp_handler_t cups_get_printers;
76 extern ipp_handler_t cups_get_classes;
77 extern ipp_handler_t cups_accept_jobs;
78 extern ipp_handler_t cups_reject_jobs;
79 extern ipp_handler_t cups_move_job;
80 
81 /* ARGSUSED0 */
82 static papi_status_t
83 default_handler(papi_service_t svc, papi_attribute_t **request,
84 		papi_attribute_t ***response, ipp_reader_t iread, void *fd)
85 {
86 	int result = (int)PAPI_INTERNAL_ERROR;
87 
88 	if (response != NULL)
89 		(void) papiAttributeListGetInteger(*response, NULL,
90 					"status-code", &result);
91 
92 	return ((papi_status_t)result);
93 }
94 
95 static struct {
96 	int16_t	id;
97 	char *name;
98 	ipp_handler_t *function;
99 	enum { OP_REQUIRED, OP_OPTIONAL, OP_VENDOR } type;
100 } handlers[] = {
101 	/* Printer Operations */
102 	{ 0x0002, "print-job",			ipp_print_job,	OP_REQUIRED },
103 	{ 0x0003, "print-uri",			NULL,		OP_OPTIONAL },
104 	{ 0x0004, "validate-job",		ipp_validate_job,
105 								OP_REQUIRED },
106 	{ 0x0005, "create-job",			ipp_create_job,	OP_OPTIONAL },
107 	{ 0x000a, "get-jobs",			ipp_get_jobs,	OP_REQUIRED },
108 	{ 0x000b, "get-printer-attributes",	ipp_get_printer_attributes,
109 								OP_REQUIRED },
110 	{ 0x0010, "pause-printer",		ipp_pause_printer,
111 								OP_OPTIONAL },
112 	{ 0x0011, "resume-printer",		ipp_resume_printer,
113 								OP_OPTIONAL },
114 	{ 0x0012, "purge-jobs",			ipp_purge_jobs,	OP_OPTIONAL },
115 	{ 0x0013, "set-printer-attributes",	ipp_set_printer_attributes,
116 								OP_OPTIONAL },
117 	{ 0x0014, "set-job-attributes",		ipp_set_job_attributes,
118 								OP_OPTIONAL },
119 	{ 0x0022, "enable-printer",		ipp_enable_printer,
120 								OP_OPTIONAL },
121 	{ 0x0023, "disable-printer",		ipp_disable_printer,
122 								OP_OPTIONAL },
123 	/* Job Operations */
124 	{ 0x0006, "send-document",		ipp_send_document,
125 								OP_OPTIONAL },
126 	{ 0x0007, "send-uri",			NULL,		OP_OPTIONAL },
127 	{ 0x0008, "cancel-job",			ipp_cancel_job,	OP_REQUIRED },
128 	{ 0x0009, "get-job-attributes",		ipp_get_job_attributes,
129 								OP_REQUIRED },
130 	{ 0x000c, "hold-job",			ipp_hold_job,	OP_OPTIONAL },
131 	{ 0x000d, "release-job",		ipp_release_job,
132 								OP_OPTIONAL },
133 	{ 0x000e, "restart-job",		ipp_restart_job,
134 								OP_OPTIONAL },
135 	/* Other Operations */
136 	{ 0x4001, "cups-get-default",		cups_get_default,
137 								OP_VENDOR },
138 	{ 0x4002, "cups-get-printers",		cups_get_printers,
139 								OP_VENDOR },
140 	{ 0x4005, "cups-get-classes",		cups_get_classes,
141 								OP_VENDOR },
142 	{ 0x4008, "cups-accept-jobs",		cups_accept_jobs,
143 								OP_VENDOR },
144 	{ 0x4009, "cups-reject-jobs",		cups_reject_jobs,
145 								OP_VENDOR },
146 	{ 0x400D, "cups-move-job",		cups_move_job,	OP_VENDOR },
147 	{ 0, NULL, NULL, OP_VENDOR }
148 };
149 
150 static int
151 ipp_operation_name_to_index(char *name)
152 {
153 	int i;
154 
155 	for (i = 0; handlers[i].name != NULL; i++)
156 		if (strcasecmp(name, handlers[i].name) == 0)
157 			return (i);
158 
159 	return (-1);
160 }
161 
162 static int
163 ipp_operation_id_to_index(int16_t id)
164 {
165 	int i;
166 
167 	for (i = 0; handlers[i].name != NULL; i++)
168 		if (id == handlers[i].id)
169 			return (i);
170 
171 	return (-1);
172 }
173 
174 static ipp_handler_t *
175 ipp_operation_handler(papi_attribute_t **request, papi_attribute_t ***response)
176 {
177 	int id = 0;
178 	int index;
179 	papi_attribute_t **ops = NULL;
180 	papi_status_t status;
181 	char configured = PAPI_FALSE;
182 
183 	/* get the operation from the request */
184 	status = papiAttributeListGetInteger(request, NULL,
185 				"operation-id", &id);
186 	if (status != PAPI_OK) {
187 		ipp_set_status(response, PAPI_BAD_ARGUMENT,
188 			"no operation specified in request");
189 		return (default_handler);
190 	}
191 
192 	/* find the operation in the handler table */
193 	index = ipp_operation_id_to_index(id);
194 #ifdef DEBUG
195 	if (index == -1)
196 		fprintf(stderr, "Operation: 0x%4.4x\n", id);
197 	else
198 		fprintf(stderr, "Operation: 0x%4.4x(%s)\n", id,
199 			handlers[index].name);
200 	fflush(stderr);
201 #endif
202 
203 	if ((index == -1) || (handlers[index].function == NULL)) {
204 		ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
205 			"operation (0x%4.4x) not implemented by server",
206 			id);
207 		return (default_handler);
208 	}
209 
210 	/* find the configured operations */
211 	status = papiAttributeListGetCollection(request, NULL,
212 				"operations", &ops);
213 	if (status != PAPI_OK) {	/* this should not be possible */
214 		ipp_set_status(response, PAPI_INTERNAL_ERROR,
215 			"sofware error, no operations configured");
216 		return (default_handler);
217 	}
218 
219 	/* check if the requested operation is configured */
220 	status = papiAttributeListGetBoolean(ops, NULL,
221 				handlers[index].name, &configured);
222 	if ((status != PAPI_OK) || (configured != PAPI_TRUE)) {
223 		ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
224 			"operation (%s 0x%4.4x) not enabled on server",
225 			handlers[index].name, id);
226 		return (default_handler);
227 	}
228 
229 	return (handlers[index].function);
230 }
231 
232 static char
233 type_to_boolean(char *type)
234 {
235 	char result = PAPI_FALSE;
236 
237 	if ((strcasecmp(type, "true") == 0) ||
238 	    (strcasecmp(type, "yes") == 0) ||
239 	    (strcasecmp(type, "on") == 0) ||
240 	    (strcasecmp(type, "enable") == 0))
241 		result = PAPI_TRUE;
242 
243 	return (result);
244 }
245 
246 static papi_status_t
247 ipp_configure_required_operations(papi_attribute_t ***list, char boolean)
248 {
249 	papi_status_t result = PAPI_OK;
250 	int i;
251 
252 	for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
253 		if (handlers[i].type == OP_REQUIRED)
254 			result = papiAttributeListAddBoolean(list,
255 					PAPI_ATTR_REPLACE, handlers[i].name,
256 					boolean);
257 
258 	return (result);
259 
260 }
261 
262 static papi_status_t
263 ipp_configure_all_operations(papi_attribute_t ***list, char boolean)
264 {
265 	papi_status_t result = PAPI_OK;
266 	int i;
267 
268 	for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
269 		result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
270 				handlers[i].name, boolean);
271 
272 	return (result);
273 }
274 
275 papi_status_t
276 ipp_configure_operation(papi_attribute_t ***list, char *operation, char *type)
277 {
278 	papi_status_t result = PAPI_OPERATION_NOT_SUPPORTED;
279 	char boolean = PAPI_FALSE;
280 
281 	if ((list == NULL) || (operation == NULL) || (type == NULL))
282 		return (PAPI_BAD_ARGUMENT);
283 
284 	boolean = type_to_boolean(type);
285 
286 	if (strcasecmp(operation, "all") == 0) {
287 		result = ipp_configure_all_operations(list, boolean);
288 	} else if (strcasecmp(operation, "required") == 0) {
289 		result = ipp_configure_required_operations(list, boolean);
290 	} else if (ipp_operation_name_to_index(operation) != -1) {
291 		result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
292 							operation, boolean);
293 	}
294 
295 	return (result);
296 }
297 
298 void
299 ipp_operations_supported(papi_attribute_t ***list, papi_attribute_t **request)
300 {
301 	papi_attribute_t **group = NULL;
302 
303 	(void) papiAttributeListGetCollection(request, NULL,
304 				"operations", &group);
305 	if (group != NULL) {
306 		int i;
307 
308 		for (i = 0; handlers[i].name != NULL; i++) {
309 			char boolean = PAPI_FALSE;
310 			(void) papiAttributeListGetBoolean(group, NULL,
311 					handlers[i].name, &boolean);
312 
313 			if (boolean == PAPI_TRUE)
314 				(void) papiAttributeListAddInteger(list,
315 					PAPI_ATTR_APPEND,
316 					"operations-supported",
317 					handlers[i].id);
318 		}
319 	}
320 }
321 
322 static papi_status_t
323 ipp_initialize_response(papi_attribute_t **request,
324 			papi_attribute_t ***response)
325 {
326 	papi_attribute_t **operational = NULL;
327 	int i;
328 
329 	if ((request == NULL) || (response == NULL))
330 		return (PAPI_BAD_ARGUMENT);
331 
332 	/* If the response was initialized, start over */
333 	if (*response != NULL) {
334 		papiAttributeListFree(*response);
335 		*response = NULL;
336 	}
337 
338 	/* Add the basic ipp header information to the response */
339 	(void) papiAttributeListGetInteger(request, NULL, "version-major", &i);
340 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
341 					"version-major", i);
342 	(void) papiAttributeListGetInteger(request, NULL, "version-minor", &i);
343 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
344 					"version-minor", i);
345 
346 	(void) papiAttributeListGetInteger(request, NULL, "request-id", &i);
347 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
348 					"request-id", i);
349 
350 	/* Add a default operational attributes group to the response */
351 	(void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
352 			"attributes-charset", "utf-8");
353 	(void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
354 			"attributes-natural-language", "en-us");
355 
356 	(void) papiAttributeListAddCollection(response, PAPI_ATTR_REPLACE,
357 				"operational-attributes-group", operational);
358 	papiAttributeListFree(operational);
359 
360 	return (PAPI_OK);
361 }
362 
363 static papi_status_t
364 print_service_connect(papi_service_t *svc, papi_attribute_t **request,
365 		papi_attribute_t ***response)
366 {
367 	papi_status_t status;
368 	papi_attribute_t **operational = NULL;
369 	char *printer_uri = NULL;
370 	char *svc_name = NULL;
371 	char *user = NULL;
372 
373 	/* Get the operational attributes group from the request */
374 	(void) papiAttributeListGetCollection(request, NULL,
375 				"operational-attributes-group", &operational);
376 
377 	/* get the user name */
378 	(void) papiAttributeListGetString(request, NULL, "default-user", &user);
379 	(void) papiAttributeListGetString(operational, NULL,
380 				"requesting-user-name", &user);
381 
382 	/* get the printer or service name */
383 	(void) papiAttributeListGetString(request, NULL,
384 				"default-service", &svc_name);
385 	get_printer_id(operational, &svc_name, NULL);
386 
387 	status = papiServiceCreate(svc, svc_name, user, NULL, NULL,
388 					PAPI_ENCRYPT_NEVER, NULL);
389 	if (status != PAPI_OK) {
390 		ipp_set_status(response, status, "print service: %s",
391 				papiStatusString(status));
392 		return (status);
393 	}
394 
395 	/*
396 	 * Trusted Solaris can't be trusting of intermediaries.  Pass
397 	 * the socket connection to the print service to retrieve the
398 	 * sensativity label off of a multi-level port.
399 	 */
400 	{
401 		int fd = -1;
402 
403 		(void) papiAttributeListGetInteger(request, NULL,
404 					"peer-socket", &fd);
405 		if (fd != -1)
406 			papiServiceSetPeer(*svc, fd);
407 	}
408 
409 	return (status);
410 }
411 
412 papi_status_t
413 ipp_process_request(papi_attribute_t **request, papi_attribute_t ***response,
414 	ipp_reader_t iread, void *fd)
415 {
416 	papi_status_t result = PAPI_OK;
417 
418 	ipp_initialize_response(request, response);
419 
420 #ifdef DEBUG
421 	fprintf(stderr, "REQUEST:");
422 	papiAttributeListPrint(stderr, request, " %d  ", getpid());
423 	fprintf(stderr, "\n");
424 #endif
425 
426 	/* verify that the request is "well-formed" */
427 	if ((result = ipp_validate_request(request, response)) == PAPI_OK) {
428 		papi_service_t svc = NULL;
429 		ipp_handler_t *handler;
430 
431 		result = print_service_connect(&svc, request, response);
432 		handler = ipp_operation_handler(request, response);
433 
434 		/* process the request */
435 		if ((result == PAPI_OK) && (handler != NULL))
436 			result = (handler)(svc, request, response, iread, fd);
437 #ifdef DEBUG
438 		fprintf(stderr, "RESULT: %s\n", papiStatusString(result));
439 #endif
440 		papiServiceDestroy(svc);
441 	}
442 
443 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_EXCL,
444 				"status-code", result);
445 	massage_response(request, *response);
446 
447 #ifdef DEBUG
448 	fprintf(stderr, "RESPONSE:");
449 	papiAttributeListPrint(stderr, *response, " %d  ", getpid());
450 	fprintf(stderr, "\n");
451 #endif
452 
453 	return (result);
454 }
455