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