• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * IPP routines for the CUPS scheduler.
3  *
4  * Copyright © 2020-2025 by OpenPrinting
5  * Copyright © 2007-2021 by Apple Inc.
6  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7  *
8  * This file contains Kerberos support code, copyright 2006 by
9  * Jelmer Vernooij.
10  *
11  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
12  * information.
13  */
14 
15 /*
16  * Include necessary headers...
17  */
18 
19 #include "cupsd.h"
20 #include <cups/ppd-private.h>
21 
22 #ifdef __APPLE__
23 #  ifdef HAVE_MEMBERSHIP_H
24 #    include <membership.h>
25 #  endif /* HAVE_MEMBERSHIP_H */
26 extern int mbr_user_name_to_uuid(const char* name, uuid_t uu);
27 extern int mbr_group_name_to_uuid(const char* name, uuid_t uu);
28 extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember);
29 #endif /* __APPLE__ */
30 
31 
32 /*
33  * Local functions...
34  */
35 
36 static void	accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
37 static void	add_class(cupsd_client_t *con, ipp_attribute_t *uri);
38 static int	add_file(cupsd_client_t *con, cupsd_job_t *job,
39 		         mime_type_t *filetype, int compression);
40 static cupsd_job_t *add_job(cupsd_client_t *con, cupsd_printer_t *printer,
41 			    mime_type_t *filetype);
42 static void	add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job);
43 static void	add_job_uuid(cupsd_job_t *job);
44 static void	add_printer(cupsd_client_t *con, ipp_attribute_t *uri);
45 static void	add_printer_state_reasons(cupsd_client_t *con,
46 		                          cupsd_printer_t *p);
47 static void	add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
48 static void	apply_printer_defaults(cupsd_printer_t *printer,
49 				       cupsd_job_t *job);
50 static void	authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
51 static void	cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
52 static void	cancel_job(cupsd_client_t *con, ipp_attribute_t *uri);
53 static void	cancel_subscription(cupsd_client_t *con, int id);
54 static int	check_rss_recipient(const char *recipient);
55 static int	check_quotas(cupsd_client_t *con, cupsd_printer_t *p);
56 static void	close_job(cupsd_client_t *con, ipp_attribute_t *uri);
57 static void	copy_attrs(ipp_t *to, ipp_t *from, cups_array_t *ra,
58 		           ipp_tag_t group, int quickcopy,
59 			   cups_array_t *exclude);
60 static int	copy_banner(cupsd_client_t *con, cupsd_job_t *job,
61 		            const char *name);
62 static int	copy_file(const char *from, const char *to, mode_t mode);
63 static int	copy_model(cupsd_client_t *con, const char *from,
64 		           const char *to);
65 static void	copy_job_attrs(cupsd_client_t *con,
66 		               cupsd_job_t *job,
67 			       cups_array_t *ra, cups_array_t *exclude);
68 static void	copy_printer_attrs(cupsd_client_t *con,
69 		                   cupsd_printer_t *printer,
70 				   cups_array_t *ra);
71 static void	copy_subscription_attrs(cupsd_client_t *con,
72 		                        cupsd_subscription_t *sub,
73 					cups_array_t *ra,
74 					cups_array_t *exclude);
75 static void	create_job(cupsd_client_t *con, ipp_attribute_t *uri);
76 static void	*create_local_bg_thread(cupsd_client_t *con);
77 static void	create_local_printer(cupsd_client_t *con);
78 static cups_array_t *create_requested_array(ipp_t *request);
79 static void	create_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
80 static void	delete_printer(cupsd_client_t *con, ipp_attribute_t *uri);
81 static void	get_default(cupsd_client_t *con);
82 static void	get_devices(cupsd_client_t *con);
83 static void	get_document(cupsd_client_t *con, ipp_attribute_t *uri);
84 static void	get_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
85 static void	get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
86 static void	get_notifications(cupsd_client_t *con);
87 static void	get_ppd(cupsd_client_t *con, ipp_attribute_t *uri);
88 static void	get_ppds(cupsd_client_t *con);
89 static void	get_printers(cupsd_client_t *con, int type);
90 static void	get_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
91 static void	get_printer_supported(cupsd_client_t *con, ipp_attribute_t *uri);
92 static void	get_subscription_attrs(cupsd_client_t *con, int sub_id);
93 static void	get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
94 static const char *get_username(cupsd_client_t *con);
95 static void	hold_job(cupsd_client_t *con, ipp_attribute_t *uri);
96 static void	hold_new_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
97 static void	move_job(cupsd_client_t *con, ipp_attribute_t *uri);
98 static int	ppd_parse_line(const char *line, char *option, int olen,
99 		               char *choice, int clen);
100 static void	print_job(cupsd_client_t *con, ipp_attribute_t *uri);
101 static void	read_job_ticket(cupsd_client_t *con);
102 static void	reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
103 static void	release_held_new_jobs(cupsd_client_t *con,
104 		                      ipp_attribute_t *uri);
105 static void	release_job(cupsd_client_t *con, ipp_attribute_t *uri);
106 static void	renew_subscription(cupsd_client_t *con, int sub_id);
107 static void	restart_job(cupsd_client_t *con, ipp_attribute_t *uri);
108 static void	save_auth_info(cupsd_client_t *con, cupsd_job_t *job,
109 		               ipp_attribute_t *auth_info);
110 static void	send_document(cupsd_client_t *con, ipp_attribute_t *uri);
111 static void	send_http_error(cupsd_client_t *con, http_status_t status,
112 		                cupsd_printer_t *printer);
113 static void	send_ipp_status(cupsd_client_t *con, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
114 static int	send_response(cupsd_client_t *con);
115 static void	set_default(cupsd_client_t *con, ipp_attribute_t *uri);
116 static void	set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
117 static void	set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
118 static int	set_printer_defaults(cupsd_client_t *con, cupsd_printer_t *printer);
119 static void	start_printer(cupsd_client_t *con, ipp_attribute_t *uri);
120 static void	stop_printer(cupsd_client_t *con, ipp_attribute_t *uri);
121 static void	url_encode_attr(ipp_attribute_t *attr, char *buffer, size_t bufsize);
122 static char	*url_encode_string(const char *s, char *buffer, size_t bufsize);
123 static int	user_allowed(cupsd_printer_t *p, const char *username);
124 static void	validate_job(cupsd_client_t *con, ipp_attribute_t *uri);
125 static int	validate_name(const char *name);
126 static int	validate_user(cupsd_job_t *job, cupsd_client_t *con, const char *owner, char *username, size_t userlen);
127 
128 
129 /*
130  * 'cupsdProcessIPPRequest()' - Process an incoming IPP request.
131  */
132 
133 int					/* O - 1 on success, 0 on failure */
cupsdProcessIPPRequest(cupsd_client_t * con)134 cupsdProcessIPPRequest(
135     cupsd_client_t *con)		/* I - Client connection */
136 {
137   ipp_tag_t		group;		/* Current group tag */
138   ipp_attribute_t	*attr;		/* Current attribute */
139   ipp_attribute_t	*charset;	/* Character set attribute */
140   ipp_attribute_t	*language;	/* Language attribute */
141   ipp_attribute_t	*uri = NULL;	/* Printer or job URI attribute */
142   ipp_attribute_t	*username;	/* requesting-user-name attr */
143   int			sub_id;		/* Subscription ID */
144   int			valid = 1;	/* Valid request? */
145 
146 
147   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdProcessIPPRequest(%p[%d]): operation_id=%04x(%s)", (void *)con, con->number, con->request->request.op.operation_id, ippOpString(con->request->request.op.operation_id));
148 
149   if (LogLevel >= CUPSD_LOG_DEBUG2)
150   {
151     for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(con->request); attr; attr = ippNextAttribute(con->request))
152     {
153       const char  *name;                /* Attribute name */
154       char        value[1024];          /* Attribute value */
155 
156       if (group != ippGetGroupTag(attr))
157       {
158         group = ippGetGroupTag(attr);
159         if (group != IPP_TAG_ZERO)
160           cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdProcessIPPRequest: %s", ippTagString(group));
161       }
162 
163       if ((name = ippGetName(attr)) == NULL)
164         continue;
165 
166       ippAttributeString(attr, value, sizeof(value));
167 
168       cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdProcessIPPRequest: %s %s%s '%s'", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), value);
169     }
170   }
171 
172  /*
173   * First build an empty response message for this request...
174   */
175 
176   con->response = ippNew();
177 
178   con->response->request.status.version[0] = con->request->request.op.version[0];
179   con->response->request.status.version[1] = con->request->request.op.version[1];
180   con->response->request.status.request_id = con->request->request.op.request_id;
181 
182  /*
183   * Then validate the request header and required attributes...
184   */
185 
186   if (con->request->request.any.version[0] != 1 && con->request->request.any.version[0] != 2)
187   {
188    /*
189     * Return an error, since we only support IPP 1.x and 2.x.
190     */
191 
192     cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Bad request version number %d.%d.", IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, con->http->hostname, con->request->request.any.version[0], con->request->request.any.version[1]);
193 
194     send_ipp_status(con, IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED, _("Bad request version number %d.%d."), con->request->request.any.version[0], con->request->request.any.version[1]);
195   }
196   else if (con->request->request.any.request_id < 1)
197   {
198    /*
199     * Return an error, since request IDs must be between 1 and 2^31-1
200     */
201 
202     cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Bad request ID %d.", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname, con->request->request.any.request_id);
203 
204     send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Bad request ID %d."), con->request->request.any.request_id);
205   }
206   else if (!con->request->attrs)
207   {
208     cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s No attributes in request.", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname);
209 
210     send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("No attributes in request."));
211   }
212   else
213   {
214    /*
215     * Make sure that the attributes are provided in the correct order and
216     * don't repeat groups...
217     */
218 
219     for (attr = con->request->attrs, group = attr->group_tag;
220 	 attr;
221 	 attr = attr->next)
222       if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
223       {
224        /*
225 	* Out of order; return an error...
226 	*/
227 
228 	cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Attribute groups are out of order", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname);
229 
230 	send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute groups are out of order (%x < %x)."), attr->group_tag, group);
231 	break;
232       }
233       else
234 	group = attr->group_tag;
235 
236     if (!attr)
237     {
238      /*
239       * Then make sure that the first three attributes are:
240       *
241       *     attributes-charset
242       *     attributes-natural-language
243       *     printer-uri/job-uri
244       */
245 
246       attr = con->request->attrs;
247       if (attr && attr->name && !strcmp(attr->name, "attributes-charset") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET && attr->group_tag == IPP_TAG_OPERATION)
248 	charset = attr;
249       else
250 	charset = NULL;
251 
252       if (attr)
253         attr = attr->next;
254 
255       if (attr && attr->name && !strcmp(attr->name, "attributes-natural-language") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE && attr->group_tag == IPP_TAG_OPERATION)
256       {
257 	language = attr;
258 
259        /*
260         * Reset language for this request if different from Accept-Language.
261         */
262 
263 	if (!con->language ||
264 	    strcmp(attr->values[0].string.text, con->language->language))
265 	{
266 	  cupsLangFree(con->language);
267 	  con->language = cupsLangGet(attr->values[0].string.text);
268 	}
269       }
270       else
271 	language = NULL;
272 
273       if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL && attr->group_tag == IPP_TAG_OPERATION)
274 	uri = attr;
275       else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL && attr->group_tag == IPP_TAG_OPERATION)
276 	uri = attr;
277       else if (con->request->request.op.operation_id == CUPS_GET_PPD && (attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL && attr->group_tag == IPP_TAG_OPERATION)
278         uri = attr;
279       else
280 	uri = NULL;
281 
282       if (charset)
283 	ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, charset->values[0].string.text);
284       else
285 	ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, "utf-8");
286 
287       if (language)
288 	ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, language->values[0].string.text);
289       else
290 	ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, DefaultLanguage);
291 
292       if (charset && _cups_strcasecmp(charset->values[0].string.text, "us-ascii") && _cups_strcasecmp(charset->values[0].string.text, "utf-8"))
293       {
294        /*
295         * Bad character set...
296 	*/
297 
298         cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported character set \"%s\"",
299 	                charset->values[0].string.text);
300 	cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Unsupported attributes-charset value \"%s\".", IPP_STATUS_ERROR_CHARSET, con->http->hostname, charset->values[0].string.text);
301 	send_ipp_status(con, IPP_STATUS_ERROR_CHARSET, _("Unsupported character set \"%s\"."), charset->values[0].string.text);
302       }
303       else if (!charset || !language ||
304 	       (!uri &&
305 	        con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
306 	        con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
307 	        con->request->request.op.operation_id != CUPS_GET_CLASSES &&
308 	        con->request->request.op.operation_id != CUPS_GET_DEVICES &&
309 	        con->request->request.op.operation_id != CUPS_GET_PPDS))
310       {
311        /*
312 	* Return an error, since attributes-charset,
313 	* attributes-natural-language, and printer-uri/job-uri are required
314 	* for all operations.
315 	*/
316 
317         if (!charset)
318 	{
319 	  cupsdLogMessage(CUPSD_LOG_ERROR, "Missing attributes-charset attribute.");
320 
321 	  cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Missing attributes-charset attribute.", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname);
322         }
323 
324         if (!language)
325 	{
326 	  cupsdLogMessage(CUPSD_LOG_ERROR,
327 	                  "Missing attributes-natural-language attribute.");
328 
329 	  cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Missing attributes-natural-language attribute.", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname);
330         }
331 
332         if (!uri)
333 	{
334 	  cupsdLogMessage(CUPSD_LOG_ERROR, "Missing printer-uri, job-uri, or ppd-name attribute.");
335 
336 	  cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Missing printer-uri, job-uri, or ppd-name attribute.", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname);
337         }
338 
339 	cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow...");
340 
341 	for (attr = con->request->attrs; attr; attr = attr->next)
342 	  cupsdLogMessage(CUPSD_LOG_DEBUG,
343 	        	  "attr \"%s\": group_tag = %x, value_tag = %x",
344 	        	  attr->name ? attr->name : "(null)", attr->group_tag,
345 			  attr->value_tag);
346 
347 	cupsdLogMessage(CUPSD_LOG_DEBUG, "End of attributes...");
348 
349 	send_ipp_status(con, IPP_BAD_REQUEST,
350 	                _("Missing required attributes."));
351       }
352       else
353       {
354        /*
355 	* OK, all the checks pass so far; validate "requesting-user-name"
356 	* attribute value...
357 	*/
358 
359         if ((username = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_ZERO)) != NULL)
360         {
361          /*
362           * Validate "requesting-user-name"...
363           */
364 
365           if (username->group_tag != IPP_TAG_OPERATION && StrictConformance)
366           {
367 	    cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s \"requesting-user-name\" attribute in wrong group.", IPP_STATUS_ERROR_BAD_REQUEST, con->http->hostname);
368 	    send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("\"requesting-user-name\" attribute in wrong group."));
369 	    valid = 0;
370           }
371           else if (username->value_tag != IPP_TAG_NAME && username->value_tag != IPP_TAG_NAMELANG)
372           {
373 	    cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s \"requesting-user-name\" attribute with wrong syntax.", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, con->http->hostname);
374 	    send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("\"requesting-user-name\" attribute with wrong syntax."));
375 	    if ((attr = ippCopyAttribute(con->response, username, 0)) != NULL)
376 	      attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
377 	    valid = 0;
378           }
379           else if (!ippValidateAttribute(username))
380           {
381 	    cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s \"requesting-user-name\" attribute with bad value.", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, con->http->hostname);
382 
383             if (StrictConformance)
384             {
385              /*
386               * Throw an error...
387               */
388 
389 	      send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("\"requesting-user-name\" attribute with wrong syntax."));
390               if ((attr = ippCopyAttribute(con->response, username, 0)) != NULL)
391                 attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
392 	      valid = 0;
393 	    }
394 	    else
395 	    {
396 	     /*
397 	      * Map bad "requesting-user-name" to 'anonymous'...
398 	      */
399 
400               ippSetString(con->request, &username, 0, "anonymous");
401 	    }
402           }
403           else if (!strcmp(username->values[0].string.text, "root") && _cups_strcasecmp(con->http->hostname, "localhost") && strcmp(con->username, "root"))
404 	  {
405 	   /*
406 	    * Remote unauthenticated user masquerading as local root...
407 	    */
408 
409             ippSetString(con->request, &username, 0, RemoteRoot);
410 	  }
411 	}
412 
413         if ((attr = ippFindAttribute(con->request, "notify-subscription-id", IPP_TAG_INTEGER)) != NULL)
414 	  sub_id = attr->values[0].integer;
415 	else
416 	  sub_id = 0;
417 
418         if (valid)
419         {
420 	 /*
421 	  * Try processing the operation...
422 	  */
423 
424 	  if (uri)
425 	    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s", ippOpString(con->request->request.op.operation_id), uri->values[0].string.text);
426 	  else
427 	    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s", ippOpString(con->request->request.op.operation_id));
428 
429 	  switch (con->request->request.op.operation_id)
430 	  {
431 	    case IPP_OP_PRINT_JOB :
432 		print_job(con, uri);
433 		break;
434 
435 	    case IPP_OP_VALIDATE_JOB :
436 		validate_job(con, uri);
437 		break;
438 
439 	    case IPP_OP_CREATE_JOB :
440 		create_job(con, uri);
441 		break;
442 
443 	    case IPP_OP_SEND_DOCUMENT :
444 		send_document(con, uri);
445 		break;
446 
447 	    case IPP_OP_CANCEL_JOB :
448 		cancel_job(con, uri);
449 		break;
450 
451 	    case IPP_OP_GET_JOB_ATTRIBUTES :
452 		get_job_attrs(con, uri);
453 		break;
454 
455 	    case IPP_OP_GET_JOBS :
456 		get_jobs(con, uri);
457 		break;
458 
459 	    case IPP_OP_GET_PRINTER_ATTRIBUTES :
460 		get_printer_attrs(con, uri);
461 		break;
462 
463 	    case IPP_OP_GET_PRINTER_SUPPORTED_VALUES :
464 		get_printer_supported(con, uri);
465 		break;
466 
467 	    case IPP_OP_HOLD_JOB :
468 		hold_job(con, uri);
469 		break;
470 
471 	    case IPP_OP_RELEASE_JOB :
472 		release_job(con, uri);
473 		break;
474 
475 	    case IPP_OP_RESTART_JOB :
476 		restart_job(con, uri);
477 		break;
478 
479 	    case IPP_OP_PAUSE_PRINTER :
480 		stop_printer(con, uri);
481 		break;
482 
483 	    case IPP_OP_RESUME_PRINTER :
484 		start_printer(con, uri);
485 		break;
486 
487 	    case IPP_OP_PURGE_JOBS :
488 	    case IPP_OP_CANCEL_JOBS :
489 	    case IPP_OP_CANCEL_MY_JOBS :
490 		cancel_all_jobs(con, uri);
491 		break;
492 
493 	    case IPP_OP_SET_JOB_ATTRIBUTES :
494 		set_job_attrs(con, uri);
495 		break;
496 
497 	    case IPP_OP_SET_PRINTER_ATTRIBUTES :
498 		set_printer_attrs(con, uri);
499 		break;
500 
501 	    case IPP_OP_HOLD_NEW_JOBS :
502 		hold_new_jobs(con, uri);
503 		break;
504 
505 	    case IPP_OP_RELEASE_HELD_NEW_JOBS :
506 		release_held_new_jobs(con, uri);
507 		break;
508 
509 	    case IPP_OP_CLOSE_JOB :
510 		close_job(con, uri);
511 		break;
512 
513 	    case IPP_OP_CUPS_GET_DEFAULT :
514 		get_default(con);
515 		break;
516 
517 	    case IPP_OP_CUPS_GET_PRINTERS :
518 		get_printers(con, 0);
519 		break;
520 
521 	    case IPP_OP_CUPS_GET_CLASSES :
522 		get_printers(con, CUPS_PRINTER_CLASS);
523 		break;
524 
525 	    case IPP_OP_CUPS_ADD_MODIFY_PRINTER :
526 		add_printer(con, uri);
527 		break;
528 
529 	    case IPP_OP_CUPS_DELETE_PRINTER :
530 		delete_printer(con, uri);
531 		break;
532 
533 	    case IPP_OP_CUPS_ADD_MODIFY_CLASS :
534 		add_class(con, uri);
535 		break;
536 
537 	    case IPP_OP_CUPS_DELETE_CLASS :
538 		delete_printer(con, uri);
539 		break;
540 
541 	    case IPP_OP_CUPS_ACCEPT_JOBS :
542 	    case IPP_OP_ENABLE_PRINTER :
543 		accept_jobs(con, uri);
544 		break;
545 
546 	    case IPP_OP_CUPS_REJECT_JOBS :
547 	    case IPP_OP_DISABLE_PRINTER :
548 		reject_jobs(con, uri);
549 		break;
550 
551 	    case IPP_OP_CUPS_SET_DEFAULT :
552 		set_default(con, uri);
553 		break;
554 
555 	    case IPP_OP_CUPS_GET_DEVICES :
556 		get_devices(con);
557 		break;
558 
559 	    case IPP_OP_CUPS_GET_DOCUMENT :
560 		get_document(con, uri);
561 		break;
562 
563 	    case IPP_OP_CUPS_GET_PPD :
564 		get_ppd(con, uri);
565 		break;
566 
567 	    case IPP_OP_CUPS_GET_PPDS :
568 		get_ppds(con);
569 		break;
570 
571 	    case IPP_OP_CUPS_MOVE_JOB :
572 		move_job(con, uri);
573 		break;
574 
575 	    case IPP_OP_CUPS_AUTHENTICATE_JOB :
576 		authenticate_job(con, uri);
577 		break;
578 
579 	    case IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS :
580 	    case IPP_OP_CREATE_JOB_SUBSCRIPTIONS :
581 		create_subscriptions(con, uri);
582 		break;
583 
584 	    case IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES :
585 		get_subscription_attrs(con, sub_id);
586 		break;
587 
588 	    case IPP_OP_GET_SUBSCRIPTIONS :
589 		get_subscriptions(con, uri);
590 		break;
591 
592 	    case IPP_OP_RENEW_SUBSCRIPTION :
593 		renew_subscription(con, sub_id);
594 		break;
595 
596 	    case IPP_OP_CANCEL_SUBSCRIPTION :
597 		cancel_subscription(con, sub_id);
598 		break;
599 
600 	    case IPP_OP_GET_NOTIFICATIONS :
601 		get_notifications(con);
602 		break;
603 
604 	    case IPP_OP_CUPS_CREATE_LOCAL_PRINTER :
605 		create_local_printer(con);
606 		break;
607 
608 	    default :
609 		cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL, "%04X %s Operation %04X (%s) not supported.", IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, con->http->hostname, con->request->request.op.operation_id, ippOpString(con->request->request.op.operation_id));
610 
611 		send_ipp_status(con, IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED, _("%s not supported."), ippOpString(con->request->request.op.operation_id));
612 		break;
613 	  }
614 	}
615       }
616     }
617   }
618 
619   if (!con->bg_pending && con->response)
620   {
621    /*
622     * Sending data from the scheduler...
623     */
624 
625     return (send_response(con));
626   }
627   else
628   {
629    /*
630     * Sending data from a subprocess like cups-deviced; tell the caller
631     * everything is A-OK so far...
632     */
633 
634     return (1);
635   }
636 }
637 
638 
639 /*
640  * 'cupsdTimeoutJob()' - Timeout a job waiting on job files.
641  */
642 
643 int					/* O - 0 on success, -1 on error */
cupsdTimeoutJob(cupsd_job_t * job)644 cupsdTimeoutJob(cupsd_job_t *job)	/* I - Job to timeout */
645 {
646   cupsd_printer_t	*printer;	/* Destination printer or class */
647   ipp_attribute_t	*attr;		/* job-sheets attribute */
648   int			kbytes;		/* Kilobytes in banner */
649 
650 
651   job->pending_timeout = 0;
652 
653  /*
654   * See if we need to add the ending sheet...
655   */
656 
657   if (!cupsdLoadJob(job))
658     return (-1);
659 
660   printer = cupsdFindDest(job->dest);
661   attr    = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
662 
663   if (printer && !(printer->type & CUPS_PRINTER_REMOTE) &&
664       attr && attr->num_values > 1)
665   {
666    /*
667     * Yes...
668     */
669 
670     cupsdLogJob(job, CUPSD_LOG_INFO, "Adding end banner page \"%s\".",
671                 attr->values[1].string.text);
672 
673     if ((kbytes = copy_banner(NULL, job, attr->values[1].string.text)) < 0)
674       return (-1);
675 
676     cupsdUpdateQuota(printer, job->username, 0, kbytes);
677   }
678 
679   return (0);
680 }
681 
682 
683 /*
684  * 'accept_jobs()' - Accept print jobs to a printer.
685  */
686 
687 static void
accept_jobs(cupsd_client_t * con,ipp_attribute_t * uri)688 accept_jobs(cupsd_client_t  *con,	/* I - Client connection */
689             ipp_attribute_t *uri)	/* I - Printer or class URI */
690 {
691   http_status_t	status;			/* Policy status */
692   cups_ptype_t	dtype;			/* Destination type (printer/class) */
693   cupsd_printer_t *printer;		/* Printer data */
694 
695 
696   cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", (void *)con,
697                   con->number, uri->values[0].string.text);
698 
699  /*
700   * Is the destination valid?
701   */
702 
703   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
704   {
705    /*
706     * Bad URI...
707     */
708 
709     send_ipp_status(con, IPP_NOT_FOUND,
710                     _("The printer or class does not exist."));
711     return;
712   }
713 
714  /*
715   * Check policy...
716   */
717 
718   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
719   {
720     send_http_error(con, status, printer);
721     return;
722   }
723 
724  /*
725   * Accept jobs sent to the printer...
726   */
727 
728   printer->accepting        = 1;
729   printer->state_message[0] = '\0';
730 
731   cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
732                 "Now accepting jobs.");
733 
734   if (dtype & CUPS_PRINTER_CLASS)
735   {
736     cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
737 
738     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").",
739                     printer->name, get_username(con));
740   }
741   else
742   {
743     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
744 
745     cupsdLogMessage(CUPSD_LOG_INFO,
746                     "Printer \"%s\" now accepting jobs (\"%s\").",
747                     printer->name, get_username(con));
748   }
749 
750  /*
751   * Everything was ok, so return OK status...
752   */
753 
754   con->response->request.status.status_code = IPP_OK;
755 }
756 
757 
758 /*
759  * 'add_class()' - Add a class to the system.
760  */
761 
762 static void
add_class(cupsd_client_t * con,ipp_attribute_t * uri)763 add_class(cupsd_client_t  *con,		/* I - Client connection */
764           ipp_attribute_t *uri)		/* I - URI of class */
765 {
766   http_status_t	status;			/* Policy status */
767   int		i;			/* Looping var */
768   char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
769 		username[HTTP_MAX_URI],	/* Username portion of URI */
770 		host[HTTP_MAX_URI],	/* Host portion of URI */
771 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
772   int		port;			/* Port portion of URI */
773   cupsd_printer_t *pclass,		/* Class */
774 		*member;		/* Member printer/class */
775   cups_ptype_t	dtype;			/* Destination type */
776   ipp_attribute_t *attr;		/* Printer attribute */
777   int		modify;			/* Non-zero if we just modified */
778   int		need_restart_job;	/* Need to restart job? */
779 
780 
781   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", (void *)con,
782                   con->number, uri->values[0].string.text);
783 
784  /*
785   * Do we have a valid URI?
786   */
787 
788   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
789                   sizeof(scheme), username, sizeof(username), host,
790 		  sizeof(host), &port, resource, sizeof(resource));
791 
792 
793   if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
794   {
795    /*
796     * No, return an error...
797     */
798 
799     send_ipp_status(con, IPP_BAD_REQUEST,
800                     _("The printer-uri must be of the form "
801 		      "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
802     return;
803   }
804 
805  /*
806   * Do we have a valid printer name?
807   */
808 
809   if (!validate_name(resource + 9))
810   {
811    /*
812     * No, return an error...
813     */
814 
815     send_ipp_status(con, IPP_BAD_REQUEST,
816                     _("The printer-uri \"%s\" contains invalid characters."),
817 		    uri->values[0].string.text);
818     return;
819   }
820 
821  /*
822   * See if the class already exists; if not, create a new class...
823   */
824 
825   if ((pclass = cupsdFindClass(resource + 9)) == NULL)
826   {
827    /*
828     * Class doesn't exist; see if we have a printer of the same name...
829     */
830 
831     if (cupsdFindPrinter(resource + 9))
832     {
833      /*
834       * Yes, return an error...
835       */
836 
837       send_ipp_status(con, IPP_NOT_POSSIBLE,
838                       _("A printer named \"%s\" already exists."),
839 		      resource + 9);
840       return;
841     }
842 
843    /*
844     * No, check the default policy and then add the class...
845     */
846 
847     if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
848     {
849       send_http_error(con, status, NULL);
850       return;
851     }
852 
853     pclass = cupsdAddClass(resource + 9);
854     modify = 0;
855 
856     pclass->printer_id = NextPrinterId ++;
857   }
858   else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con,
859                                       NULL)) != HTTP_OK)
860   {
861     send_http_error(con, status, pclass);
862     return;
863   }
864   else
865     modify = 1;
866 
867  /*
868   * Look for attributes and copy them over as needed...
869   */
870 
871   need_restart_job = 0;
872 
873   if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
874     cupsdSetString(&pclass->location, attr->values[0].string.text);
875 
876   if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
877     cupsdSetString(&pclass->geo_location, attr->values[0].string.text);
878 
879   if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
880     cupsdSetString(&pclass->organization, attr->values[0].string.text);
881 
882   if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
883     cupsdSetString(&pclass->organizational_unit, attr->values[0].string.text);
884 
885   if ((attr = ippFindAttribute(con->request, "printer-info",
886                                IPP_TAG_TEXT)) != NULL)
887     cupsdSetString(&pclass->info, attr->values[0].string.text);
888 
889   if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
890                                IPP_TAG_BOOLEAN)) != NULL &&
891       attr->values[0].boolean != pclass->accepting)
892   {
893     cupsdLogMessage(CUPSD_LOG_INFO,
894                     "Setting %s printer-is-accepting-jobs to %d (was %d.)",
895                     pclass->name, attr->values[0].boolean, pclass->accepting);
896 
897     pclass->accepting = attr->values[0].boolean;
898 
899     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s accepting jobs.",
900 		  pclass->accepting ? "Now" : "No longer");
901   }
902 
903   if ((attr = ippFindAttribute(con->request, "printer-is-shared", IPP_TAG_BOOLEAN)) != NULL)
904   {
905     if (pclass->type & CUPS_PRINTER_REMOTE)
906     {
907      /*
908       * Cannot re-share remote printers.
909       */
910 
911       send_ipp_status(con, IPP_BAD_REQUEST, _("Cannot change printer-is-shared for remote queues."));
912       if (!modify)
913 	cupsdDeletePrinter(pclass, 0);
914 
915       return;
916     }
917 
918     if (pclass->shared && !ippGetBoolean(attr, 0))
919       cupsdDeregisterPrinter(pclass, 1);
920 
921     cupsdLogMessage(CUPSD_LOG_INFO,
922                     "Setting %s printer-is-shared to %d (was %d.)",
923                     pclass->name, attr->values[0].boolean, pclass->shared);
924 
925     pclass->shared = ippGetBoolean(attr, 0);
926   }
927 
928   if ((attr = ippFindAttribute(con->request, "printer-state",
929                                IPP_TAG_ENUM)) != NULL)
930   {
931     if (attr->values[0].integer != IPP_PRINTER_IDLE &&
932         attr->values[0].integer != IPP_PRINTER_STOPPED)
933     {
934       send_ipp_status(con, IPP_BAD_REQUEST,
935                       _("Attempt to set %s printer-state to bad value %d."),
936                       pclass->name, attr->values[0].integer);
937       if (!modify)
938 	cupsdDeletePrinter(pclass, 0);
939 
940       return;
941     }
942 
943     cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
944                     pclass->name, attr->values[0].integer, pclass->state);
945 
946     if (attr->values[0].integer == IPP_PRINTER_STOPPED)
947       cupsdStopPrinter(pclass, 0);
948     else
949     {
950       cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
951       need_restart_job = 1;
952     }
953   }
954   if ((attr = ippFindAttribute(con->request, "printer-state-message",
955                                IPP_TAG_TEXT)) != NULL)
956   {
957     strlcpy(pclass->state_message, attr->values[0].string.text,
958             sizeof(pclass->state_message));
959 
960     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s",
961                   pclass->state_message);
962   }
963   if ((attr = ippFindAttribute(con->request, "member-uris",
964                                IPP_TAG_URI)) != NULL)
965   {
966    /*
967     * Clear the printer array as needed...
968     */
969 
970     need_restart_job = 1;
971 
972     if (pclass->num_printers > 0)
973     {
974       free(pclass->printers);
975       pclass->num_printers = 0;
976     }
977 
978    /*
979     * Add each printer or class that is listed...
980     */
981 
982     for (i = 0; i < attr->num_values; i ++)
983     {
984      /*
985       * Search for the printer or class URI...
986       */
987 
988       if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member))
989       {
990        /*
991 	* Bad URI...
992 	*/
993 
994 	send_ipp_status(con, IPP_NOT_FOUND,
995                 	_("The printer or class does not exist."));
996 	if (!modify)
997 	  cupsdDeletePrinter(pclass, 0);
998 
999 	return;
1000       }
1001       else if (dtype & CUPS_PRINTER_CLASS)
1002       {
1003         send_ipp_status(con, IPP_BAD_REQUEST,
1004 			_("Nested classes are not allowed."));
1005 	if (!modify)
1006 	  cupsdDeletePrinter(pclass, 0);
1007 
1008         return;
1009       }
1010 
1011      /*
1012       * Add it to the class...
1013       */
1014 
1015       cupsdAddPrinterToClass(pclass, member);
1016     }
1017   }
1018 
1019   if (!set_printer_defaults(con, pclass))
1020   {
1021     if (!modify)
1022       cupsdDeletePrinter(pclass, 0);
1023 
1024     return;
1025   }
1026 
1027   if ((attr = ippFindAttribute(con->request, "auth-info-required",
1028                                IPP_TAG_KEYWORD)) != NULL)
1029     cupsdSetAuthInfoRequired(pclass, NULL, attr);
1030 
1031   pclass->config_time = time(NULL);
1032 
1033  /*
1034   * Update the printer class attributes and return...
1035   */
1036 
1037   cupsdSetPrinterAttrs(pclass);
1038   cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
1039 
1040   if (need_restart_job && pclass->job)
1041   {
1042    /*
1043     * Reset the current job to a "pending" status...
1044     */
1045 
1046     cupsdSetJobState(pclass->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
1047                      "Job restarted because the class was modified.");
1048   }
1049 
1050   cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
1051 
1052   if (modify)
1053   {
1054     cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
1055 		  pclass, NULL, "Class \"%s\" modified by \"%s\".",
1056 		  pclass->name, get_username(con));
1057 
1058     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
1059                     pclass->name, get_username(con));
1060   }
1061   else
1062   {
1063     cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
1064 		  pclass, NULL, "New class \"%s\" added by \"%s\".",
1065 		  pclass->name, get_username(con));
1066 
1067     cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
1068                     pclass->name, get_username(con));
1069   }
1070 
1071   con->response->request.status.status_code = IPP_OK;
1072 }
1073 
1074 
1075 /*
1076  * 'add_file()' - Add a file to a job.
1077  */
1078 
1079 static int				/* O - 0 on success, -1 on error */
add_file(cupsd_client_t * con,cupsd_job_t * job,mime_type_t * filetype,int compression)1080 add_file(cupsd_client_t *con,		/* I - Connection to client */
1081          cupsd_job_t    *job,		/* I - Job to add to */
1082          mime_type_t    *filetype,	/* I - Type of file */
1083 	 int            compression)	/* I - Compression */
1084 {
1085   mime_type_t	**filetypes;		/* New filetypes array... */
1086   int		*compressions;		/* New compressions array... */
1087 
1088 
1089   cupsdLogMessage(CUPSD_LOG_DEBUG2,
1090         	  "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
1091 		  "compression=%d)", (void *)con, con ? con->number : -1, job->id,
1092 		  filetype->super, filetype->type, compression);
1093 
1094  /*
1095   * Add the file to the job...
1096   */
1097 
1098   if (job->num_files == 0)
1099   {
1100     compressions = (int *)malloc(sizeof(int));
1101     filetypes    = (mime_type_t **)malloc(sizeof(mime_type_t *));
1102   }
1103   else
1104   {
1105     compressions = (int *)realloc(job->compressions,
1106                                   (size_t)(job->num_files + 1) * sizeof(int));
1107     filetypes    = (mime_type_t **)realloc(job->filetypes,
1108                                            (size_t)(job->num_files + 1) *
1109 					   sizeof(mime_type_t *));
1110   }
1111 
1112   if (compressions)
1113     job->compressions = compressions;
1114 
1115   if (filetypes)
1116     job->filetypes = filetypes;
1117 
1118   if (!compressions || !filetypes)
1119   {
1120     cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1121                      "Job aborted because the scheduler ran out of memory.");
1122 
1123     if (con)
1124       send_ipp_status(con, IPP_INTERNAL_ERROR,
1125 		      _("Unable to allocate memory for file types."));
1126 
1127     return (-1);
1128   }
1129 
1130   job->compressions[job->num_files] = compression;
1131   job->filetypes[job->num_files]    = filetype;
1132 
1133   job->num_files ++;
1134 
1135   job->dirty = 1;
1136   cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1137 
1138   return (0);
1139 }
1140 
1141 
1142 /*
1143  * 'add_job()' - Add a job to a print queue.
1144  */
1145 
1146 static cupsd_job_t *			/* O - Job object */
add_job(cupsd_client_t * con,cupsd_printer_t * printer,mime_type_t * filetype)1147 add_job(cupsd_client_t  *con,		/* I - Client connection */
1148 	cupsd_printer_t *printer,	/* I - Destination printer */
1149 	mime_type_t     *filetype)	/* I - First print file type, if any */
1150 {
1151   http_status_t	status;			/* Policy status */
1152   ipp_attribute_t *attr,		/* Current attribute */
1153 		*auth_info;		/* auth-info attribute */
1154   const char	*mandatory;		/* Current mandatory job attribute */
1155   const char	*val;			/* Default option value */
1156   int		priority;		/* Job priority */
1157   cupsd_job_t	*job;			/* Current job */
1158   char		job_uri[HTTP_MAX_URI];	/* Job URI */
1159   int		kbytes;			/* Size of print file */
1160   int		i;			/* Looping var */
1161   int		lowerpagerange;		/* Page range bound */
1162   int		exact;			/* Did we have an exact match? */
1163   ipp_attribute_t *media_col,		/* media-col attribute */
1164 		*media_margin;		/* media-*-margin attribute */
1165   ipp_t		*unsup_col;		/* media-col in unsupported response */
1166   static const char * const readonly[] =/* List of read-only attributes */
1167   {
1168     "date-time-at-completed",
1169     "date-time-at-creation",
1170     "date-time-at-processing",
1171     "job-detailed-status-messages",
1172     "job-document-access-errors",
1173     "job-id",
1174     "job-impressions-completed",
1175     "job-k-octets-completed",
1176     "job-media-sheets-completed",
1177     "job-pages-completed",
1178     "job-printer-up-time",
1179     "job-printer-uri",
1180     "job-state",
1181     "job-state-message",
1182     "job-state-reasons",
1183     "job-uri",
1184     "number-of-documents",
1185     "number-of-intervening-jobs",
1186     "output-device-assigned",
1187     "time-at-completed",
1188     "time-at-creation",
1189     "time-at-processing"
1190   };
1191 
1192 
1193   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
1194                   (void *)con, con->number, (void *)printer, printer->name,
1195 		  (void *)filetype, filetype ? filetype->super : "none",
1196 		  filetype ? filetype->type : "none");
1197 
1198  /*
1199   * Check remote printing to non-shared printer...
1200   */
1201 
1202   if (!printer->shared &&
1203       _cups_strcasecmp(con->http->hostname, "localhost") &&
1204       _cups_strcasecmp(con->http->hostname, ServerName))
1205   {
1206     send_ipp_status(con, IPP_NOT_AUTHORIZED,
1207                     _("The printer or class is not shared."));
1208     return (NULL);
1209   }
1210 
1211  /*
1212   * Check policy...
1213   */
1214 
1215   auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
1216 
1217   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
1218   {
1219     send_http_error(con, status, printer);
1220     return (NULL);
1221   }
1222   else if (printer->num_auth_info_required == 1 &&
1223            !strcmp(printer->auth_info_required[0], "negotiate") &&
1224            !con->username[0])
1225   {
1226     send_http_error(con, HTTP_UNAUTHORIZED, printer);
1227     return (NULL);
1228   }
1229 #ifdef HAVE_TLS
1230   else if (auth_info && !con->http->tls &&
1231            !httpAddrLocalhost(con->http->hostaddr))
1232   {
1233    /*
1234     * Require encryption of auth-info over non-local connections...
1235     */
1236 
1237     send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
1238     return (NULL);
1239   }
1240 #endif /* HAVE_TLS */
1241 
1242  /*
1243   * See if the printer is accepting jobs...
1244   */
1245 
1246   if (!printer->accepting)
1247   {
1248     send_ipp_status(con, IPP_NOT_ACCEPTING,
1249                     _("Destination \"%s\" is not accepting jobs."),
1250                     printer->name);
1251     return (NULL);
1252   }
1253 
1254  /*
1255   * Validate job template attributes; for now just document-format,
1256   * copies, job-sheets, number-up, page-ranges, mandatory attributes, and
1257   * media...
1258   */
1259 
1260   for (i = 0; i < (int)(sizeof(readonly) / sizeof(readonly[0])); i ++)
1261   {
1262     if ((attr = ippFindAttribute(con->request, readonly[i], IPP_TAG_ZERO)) != NULL)
1263     {
1264       ippDeleteAttribute(con->request, attr);
1265 
1266       if (StrictConformance)
1267       {
1268 	send_ipp_status(con, IPP_BAD_REQUEST, _("The '%s' Job Status attribute cannot be supplied in a job creation request."), readonly[i]);
1269 	return (NULL);
1270       }
1271 
1272       cupsdLogMessage(CUPSD_LOG_INFO, "Unexpected '%s' Job Status attribute in a job creation request.", readonly[i]);
1273     }
1274   }
1275 
1276   if (printer->pc)
1277   {
1278     for (mandatory = (char *)cupsArrayFirst(printer->pc->mandatory);
1279 	 mandatory;
1280 	 mandatory = (char *)cupsArrayNext(printer->pc->mandatory))
1281     {
1282       if (!ippFindAttribute(con->request, mandatory, IPP_TAG_ZERO))
1283       {
1284        /*
1285 	* Missing a required attribute...
1286 	*/
1287 
1288 	send_ipp_status(con, IPP_CONFLICT,
1289 			_("The \"%s\" attribute is required for print jobs."),
1290 			mandatory);
1291 	return (NULL);
1292       }
1293     }
1294   }
1295 
1296   if (filetype && printer->filetypes &&
1297       !cupsArrayFind(printer->filetypes, filetype))
1298   {
1299     char	mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
1300 					/* MIME media type string */
1301 
1302 
1303     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
1304              filetype->type);
1305 
1306     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
1307                     _("Unsupported format \"%s\"."), mimetype);
1308 
1309     ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
1310                  "document-format", NULL, mimetype);
1311 
1312     return (NULL);
1313   }
1314 
1315   if ((attr = ippFindAttribute(con->request, "copies",
1316                                IPP_TAG_INTEGER)) != NULL)
1317   {
1318     if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
1319     {
1320       send_ipp_status(con, IPP_ATTRIBUTES, _("Bad copies value %d."),
1321                       attr->values[0].integer);
1322       ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1323 	            "copies", attr->values[0].integer);
1324       return (NULL);
1325     }
1326   }
1327 
1328   if ((attr = ippFindAttribute(con->request, "job-sheets",
1329                                IPP_TAG_ZERO)) != NULL)
1330   {
1331     if (attr->value_tag != IPP_TAG_KEYWORD &&
1332         attr->value_tag != IPP_TAG_NAME)
1333     {
1334       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type."));
1335       return (NULL);
1336     }
1337 
1338     if (attr->num_values > 2)
1339     {
1340       send_ipp_status(con, IPP_BAD_REQUEST,
1341                       _("Too many job-sheets values (%d > 2)."),
1342 		      attr->num_values);
1343       return (NULL);
1344     }
1345 
1346     for (i = 0; i < attr->num_values; i ++)
1347       if (strcmp(attr->values[i].string.text, "none") &&
1348           !cupsdFindBanner(attr->values[i].string.text))
1349       {
1350 	send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\"."),
1351 			attr->values[i].string.text);
1352 	return (NULL);
1353       }
1354   }
1355 
1356   if ((attr = ippFindAttribute(con->request, "number-up",
1357                                IPP_TAG_INTEGER)) != NULL)
1358   {
1359     if (attr->values[0].integer != 1 &&
1360         attr->values[0].integer != 2 &&
1361         attr->values[0].integer != 4 &&
1362         attr->values[0].integer != 6 &&
1363         attr->values[0].integer != 9 &&
1364         attr->values[0].integer != 16)
1365     {
1366       send_ipp_status(con, IPP_ATTRIBUTES, _("Bad number-up value %d."),
1367                       attr->values[0].integer);
1368       ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
1369 	            "number-up", attr->values[0].integer);
1370       return (NULL);
1371     }
1372   }
1373 
1374   if ((attr = ippFindAttribute(con->request, "page-ranges",
1375                                IPP_TAG_RANGE)) != NULL)
1376   {
1377     for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
1378     {
1379       if (attr->values[i].range.lower < lowerpagerange ||
1380 	  attr->values[i].range.lower > attr->values[i].range.upper)
1381       {
1382 	send_ipp_status(con, IPP_BAD_REQUEST,
1383 	                _("Bad page-ranges values %d-%d."),
1384 	                attr->values[i].range.lower,
1385 			attr->values[i].range.upper);
1386 	return (NULL);
1387       }
1388 
1389       lowerpagerange = attr->values[i].range.upper + 1;
1390     }
1391   }
1392 
1393  /*
1394   * Do media selection as needed...
1395   */
1396 
1397   if (!ippFindAttribute(con->request, "PageRegion", IPP_TAG_ZERO) &&
1398       !ippFindAttribute(con->request, "PageSize", IPP_TAG_ZERO) &&
1399       _ppdCacheGetPageSize(printer->pc, con->request, NULL, &exact))
1400   {
1401     if (!exact &&
1402         (media_col = ippFindAttribute(con->request, "media-col",
1403 	                              IPP_TAG_BEGIN_COLLECTION)) != NULL)
1404     {
1405       send_ipp_status(con, IPP_OK_SUBST, _("Unsupported margins."));
1406 
1407       unsup_col = ippNew();
1408       if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1409                                            "media-bottom-margin",
1410 					   IPP_TAG_INTEGER)) != NULL)
1411         ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1412 	              "media-bottom-margin", media_margin->values[0].integer);
1413 
1414       if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1415                                            "media-left-margin",
1416 					   IPP_TAG_INTEGER)) != NULL)
1417         ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1418 	              "media-left-margin", media_margin->values[0].integer);
1419 
1420       if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1421                                            "media-right-margin",
1422 					   IPP_TAG_INTEGER)) != NULL)
1423         ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1424 	              "media-right-margin", media_margin->values[0].integer);
1425 
1426       if ((media_margin = ippFindAttribute(media_col->values[0].collection,
1427                                            "media-top-margin",
1428 					   IPP_TAG_INTEGER)) != NULL)
1429         ippAddInteger(unsup_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
1430 	              "media-top-margin", media_margin->values[0].integer);
1431 
1432       ippAddCollection(con->response, IPP_TAG_UNSUPPORTED_GROUP, "media-col",
1433                        unsup_col);
1434       ippDelete(unsup_col);
1435     }
1436   }
1437 
1438  /*
1439   * Make sure we aren't over our limit...
1440   */
1441 
1442   if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1443     cupsdCleanJobs();
1444 
1445   if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
1446   {
1447     send_ipp_status(con, IPP_NOT_POSSIBLE, _("Too many active jobs."));
1448     return (NULL);
1449   }
1450 
1451   if ((i = check_quotas(con, printer)) < 0)
1452   {
1453     send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
1454     return (NULL);
1455   }
1456   else if (i == 0)
1457   {
1458     send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
1459     return (NULL);
1460   }
1461 
1462  /*
1463   * Create the job and set things up...
1464   */
1465 
1466   if ((attr = ippFindAttribute(con->request, "job-priority",
1467                                IPP_TAG_INTEGER)) != NULL)
1468     priority = attr->values[0].integer;
1469   else
1470   {
1471     if ((val = cupsGetOption("job-priority", printer->num_options,
1472                              printer->options)) != NULL)
1473       priority = atoi(val);
1474     else
1475       priority = 50;
1476 
1477     ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
1478                   priority);
1479   }
1480 
1481   if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) == NULL)
1482     ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1483   else if ((attr->value_tag != IPP_TAG_NAME &&
1484             attr->value_tag != IPP_TAG_NAMELANG) ||
1485            attr->num_values != 1)
1486   {
1487     send_ipp_status(con, IPP_ATTRIBUTES,
1488                     _("Bad job-name value: Wrong type or count."));
1489     if ((attr = ippCopyAttribute(con->response, attr, 0)) != NULL)
1490       attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
1491 
1492     if (StrictConformance)
1493       return (NULL);
1494 
1495     /* Don't use invalid attribute */
1496     ippDeleteAttribute(con->request, attr);
1497 
1498     ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1499   }
1500   else if (!ippValidateAttribute(attr))
1501   {
1502     send_ipp_status(con, IPP_ATTRIBUTES, _("Bad job-name value: %s"),
1503                     cupsLastErrorString());
1504 
1505     if ((attr = ippCopyAttribute(con->response, attr, 0)) != NULL)
1506       attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
1507 
1508     if (StrictConformance)
1509       return (NULL);
1510 
1511     /* Don't use invalid attribute */
1512     ippDeleteAttribute(con->request, attr);
1513 
1514     ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, "Untitled");
1515   }
1516 
1517   attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
1518 
1519   if ((job = cupsdAddJob(priority, printer->name)) == NULL)
1520   {
1521     send_ipp_status(con, IPP_INTERNAL_ERROR,
1522                     _("Unable to add job for destination \"%s\"."),
1523 		    printer->name);
1524     return (NULL);
1525   }
1526 
1527   job->dtype   = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
1528   job->attrs   = con->request;
1529   job->dirty   = 1;
1530   con->request = ippNewRequest(job->attrs->request.op.operation_id);
1531 
1532   cupsdMarkDirty(CUPSD_DIRTY_JOBS);
1533 
1534   add_job_uuid(job);
1535   apply_printer_defaults(printer, job);
1536 
1537   if (con->username[0])
1538   {
1539     cupsdSetString(&job->username, con->username);
1540 
1541     if (attr)
1542       ippSetString(job->attrs, &attr, 0, con->username);
1543   }
1544   else if (attr)
1545   {
1546     cupsdLogMessage(CUPSD_LOG_DEBUG,
1547                     "add_job: requesting-user-name=\"%s\"",
1548                     attr->values[0].string.text);
1549 
1550     cupsdSetString(&job->username, attr->values[0].string.text);
1551   }
1552   else
1553     cupsdSetString(&job->username, "anonymous");
1554 
1555   if (!attr)
1556     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1557                  "job-originating-user-name", NULL, job->username);
1558   else
1559   {
1560     ippSetGroupTag(job->attrs, &attr, IPP_TAG_JOB);
1561     ippSetName(job->attrs, &attr, "job-originating-user-name");
1562   }
1563 
1564   if (con->username[0] || auth_info)
1565   {
1566     save_auth_info(con, job, auth_info);
1567 
1568    /*
1569     * Remove the auth-info attribute from the attribute data...
1570     */
1571 
1572     if (auth_info)
1573       ippDeleteAttribute(job->attrs, auth_info);
1574   }
1575 
1576   if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
1577     cupsdSetString(&(job->name), attr->values[0].string.text);
1578 
1579   if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
1580                                IPP_TAG_ZERO)) != NULL)
1581   {
1582    /*
1583     * Request contains a job-originating-host-name attribute; validate it...
1584     */
1585 
1586     if (attr->value_tag != IPP_TAG_NAME ||
1587         attr->num_values != 1 ||
1588         strcmp(con->http->hostname, "localhost"))
1589     {
1590      /*
1591       * Can't override the value if we aren't connected via localhost.
1592       * Also, we can only have 1 value and it must be a name value.
1593       */
1594 
1595       ippDeleteAttribute(job->attrs, attr);
1596       ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-host-name", NULL, con->http->hostname);
1597     }
1598     else
1599       ippSetGroupTag(job->attrs, &attr, IPP_TAG_JOB);
1600   }
1601   else
1602   {
1603    /*
1604     * No job-originating-host-name attribute, so use the hostname from
1605     * the connection...
1606     */
1607 
1608     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
1609         	 "job-originating-host-name", NULL, con->http->hostname);
1610   }
1611 
1612   ippAddOutOfBand(job->attrs, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-completed");
1613   ippAddDate(job->attrs, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(time(NULL)));
1614   ippAddOutOfBand(job->attrs, IPP_TAG_JOB, IPP_TAG_NOVALUE, "date-time-at-processing");
1615   ippAddOutOfBand(job->attrs, IPP_TAG_JOB, IPP_TAG_NOVALUE, "time-at-completed");
1616   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", time(NULL));
1617   ippAddOutOfBand(job->attrs, IPP_TAG_JOB, IPP_TAG_NOVALUE, "time-at-processing");
1618 
1619  /*
1620   * Add remaining job attributes...
1621   */
1622 
1623   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1624   job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
1625                              "job-state", IPP_JOB_STOPPED);
1626   job->state_value = (ipp_jstate_t)job->state->values[0].integer;
1627   job->reasons = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1628                               "job-state-reasons", NULL, "job-incoming");
1629   job->impressions = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", 0);
1630   job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
1631                               "job-media-sheets-completed", 0);
1632   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
1633                printer->uri);
1634 
1635   if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
1636     attr->values[0].integer = 0;
1637   else
1638     ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", 0);
1639 
1640   if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1641                                IPP_TAG_KEYWORD)) == NULL)
1642     attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1643   if (!attr)
1644   {
1645     if ((val = cupsGetOption("job-hold-until", printer->num_options,
1646                              printer->options)) == NULL)
1647       val = "no-hold";
1648 
1649     attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1650                         "job-hold-until", NULL, val);
1651   }
1652 
1653   if (printer->holding_new_jobs)
1654   {
1655    /*
1656     * Hold all new jobs on this printer...
1657     */
1658 
1659     if (attr && strcmp(attr->values[0].string.text, "no-hold"))
1660       cupsdSetJobHoldUntil(job, ippGetString(attr, 0, NULL), 0);
1661     else
1662       cupsdSetJobHoldUntil(job, "indefinite", 0);
1663 
1664     job->state->values[0].integer = IPP_JOB_HELD;
1665     job->state_value              = IPP_JOB_HELD;
1666 
1667     ippSetString(job->attrs, &job->reasons, 0, "job-held-on-create");
1668   }
1669   else if (attr && strcmp(attr->values[0].string.text, "no-hold"))
1670   {
1671    /*
1672     * Hold job until specified time...
1673     */
1674 
1675     cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
1676 
1677     job->state->values[0].integer = IPP_JOB_HELD;
1678     job->state_value              = IPP_JOB_HELD;
1679 
1680     ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
1681   }
1682   else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
1683   {
1684     job->hold_until               = time(NULL) + MultipleOperationTimeout;
1685     job->state->values[0].integer = IPP_JOB_HELD;
1686     job->state_value              = IPP_JOB_HELD;
1687   }
1688   else
1689   {
1690     job->state->values[0].integer = IPP_JOB_PENDING;
1691     job->state_value              = IPP_JOB_PENDING;
1692 
1693     ippSetString(job->attrs, &job->reasons, 0, "none");
1694   }
1695 
1696   if (!(printer->type & CUPS_PRINTER_REMOTE) || Classification)
1697   {
1698    /*
1699     * Add job sheets options...
1700     */
1701 
1702     if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1703                                  IPP_TAG_ZERO)) == NULL)
1704     {
1705       cupsdLogMessage(CUPSD_LOG_DEBUG,
1706                       "Adding default job-sheets values \"%s,%s\"...",
1707                       printer->job_sheets[0], printer->job_sheets[1]);
1708 
1709       attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
1710                            2, NULL, NULL);
1711       ippSetString(job->attrs, &attr, 0, printer->job_sheets[0]);
1712       ippSetString(job->attrs, &attr, 1, printer->job_sheets[1]);
1713     }
1714 
1715     job->job_sheets = attr;
1716 
1717    /*
1718     * Enforce classification level if set...
1719     */
1720 
1721     if (Classification)
1722     {
1723       cupsdLogMessage(CUPSD_LOG_INFO,
1724                       "Classification=\"%s\", ClassifyOverride=%d",
1725                       Classification ? Classification : "(null)",
1726 		      ClassifyOverride);
1727 
1728       if (ClassifyOverride)
1729       {
1730         if (!strcmp(attr->values[0].string.text, "none") &&
1731 	    (attr->num_values == 1 ||
1732 	     !strcmp(attr->values[1].string.text, "none")))
1733         {
1734 	 /*
1735           * Force the leading banner to have the classification on it...
1736 	  */
1737 
1738           ippSetString(job->attrs, &attr, 0, Classification);
1739 
1740 	  cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1741 	                		     "job-sheets=\"%s,none\", "
1742 					     "job-originating-user-name=\"%s\"",
1743 	              Classification, job->username);
1744 	}
1745 	else if (attr->num_values == 2 &&
1746 	         strcmp(attr->values[0].string.text,
1747 		        attr->values[1].string.text) &&
1748 		 strcmp(attr->values[0].string.text, "none") &&
1749 		 strcmp(attr->values[1].string.text, "none"))
1750         {
1751 	 /*
1752 	  * Can't put two different security markings on the same document!
1753 	  */
1754 
1755           ippSetString(job->attrs, &attr, 1, attr->values[0].string.text);
1756 
1757 	  cupsdLogJob(job, CUPSD_LOG_NOTICE, "CLASSIFICATION FORCED "
1758 	                		     "job-sheets=\"%s,%s\", "
1759 					     "job-originating-user-name=\"%s\"",
1760 		      attr->values[0].string.text,
1761 		      attr->values[1].string.text, job->username);
1762 	}
1763 	else if (strcmp(attr->values[0].string.text, Classification) &&
1764 	         strcmp(attr->values[0].string.text, "none") &&
1765 		 (attr->num_values == 1 ||
1766 	          (strcmp(attr->values[1].string.text, Classification) &&
1767 	           strcmp(attr->values[1].string.text, "none"))))
1768         {
1769 	  if (attr->num_values == 1)
1770             cupsdLogJob(job, CUPSD_LOG_NOTICE,
1771 			"CLASSIFICATION OVERRIDDEN "
1772 			"job-sheets=\"%s\", "
1773 			"job-originating-user-name=\"%s\"",
1774 	                attr->values[0].string.text, job->username);
1775           else
1776             cupsdLogJob(job, CUPSD_LOG_NOTICE,
1777 			"CLASSIFICATION OVERRIDDEN "
1778 			"job-sheets=\"%s,%s\",fffff "
1779 			"job-originating-user-name=\"%s\"",
1780 			attr->values[0].string.text,
1781 			attr->values[1].string.text, job->username);
1782         }
1783       }
1784       else if (strcmp(attr->values[0].string.text, Classification) &&
1785                (attr->num_values == 1 ||
1786 	       strcmp(attr->values[1].string.text, Classification)))
1787       {
1788        /*
1789         * Force the banner to have the classification on it...
1790 	*/
1791 
1792         if (attr->num_values > 1 &&
1793 	    !strcmp(attr->values[0].string.text, attr->values[1].string.text))
1794 	{
1795           ippSetString(job->attrs, &attr, 0, Classification);
1796           ippSetString(job->attrs, &attr, 1, Classification);
1797 	}
1798         else
1799 	{
1800           if (attr->num_values == 1 ||
1801 	      strcmp(attr->values[0].string.text, "none"))
1802             ippSetString(job->attrs, &attr, 0, Classification);
1803 
1804           if (attr->num_values > 1 &&
1805 	      strcmp(attr->values[1].string.text, "none"))
1806 	    ippSetString(job->attrs, &attr, 1, Classification);
1807         }
1808 
1809         if (attr->num_values > 1)
1810 	  cupsdLogJob(job, CUPSD_LOG_NOTICE,
1811 		      "CLASSIFICATION FORCED "
1812 		      "job-sheets=\"%s,%s\", "
1813 		      "job-originating-user-name=\"%s\"",
1814 		      attr->values[0].string.text,
1815 		      attr->values[1].string.text, job->username);
1816         else
1817 	  cupsdLogJob(job, CUPSD_LOG_NOTICE,
1818 		      "CLASSIFICATION FORCED "
1819 		      "job-sheets=\"%s\", "
1820 		      "job-originating-user-name=\"%s\"",
1821 		      Classification, job->username);
1822       }
1823     }
1824 
1825    /*
1826     * See if we need to add the starting sheet...
1827     */
1828 
1829     if (!(printer->type & CUPS_PRINTER_REMOTE))
1830     {
1831       cupsdLogJob(job, CUPSD_LOG_INFO, "Adding start banner page \"%s\".",
1832 		  attr->values[0].string.text);
1833 
1834       if ((kbytes = copy_banner(con, job, attr->values[0].string.text)) < 0)
1835       {
1836         cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
1837 	                 "Aborting job because the start banner could not be "
1838 			 "copied.");
1839         return (NULL);
1840       }
1841 
1842       cupsdUpdateQuota(printer, job->username, 0, kbytes);
1843     }
1844   }
1845   else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1846                                     IPP_TAG_ZERO)) != NULL)
1847     job->job_sheets = attr;
1848 
1849  /*
1850   * Fill in the response info...
1851   */
1852 
1853   httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL, con->clientname, con->clientport, "/jobs/%d", job->id);
1854   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
1855 
1856   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
1857 
1858   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
1859   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, "");
1860   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, job->reasons->values[0].string.text);
1861 
1862   con->response->request.status.status_code = IPP_OK;
1863 
1864  /*
1865   * Add any job subscriptions...
1866   */
1867 
1868   add_job_subscriptions(con, job);
1869 
1870  /*
1871   * Set all but the first two attributes to the job attributes group...
1872   */
1873 
1874   for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
1875     attr->group_tag = IPP_TAG_JOB;
1876 
1877  /*
1878   * Fire the "job created" event...
1879   */
1880 
1881   cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
1882 
1883  /*
1884   * Return the new job...
1885   */
1886 
1887   return (job);
1888 }
1889 
1890 
1891 /*
1892  * 'add_job_subscriptions()' - Add any subscriptions for a job.
1893  */
1894 
1895 static void
add_job_subscriptions(cupsd_client_t * con,cupsd_job_t * job)1896 add_job_subscriptions(
1897     cupsd_client_t *con,		/* I - Client connection */
1898     cupsd_job_t    *job)		/* I - Newly created job */
1899 {
1900   int			i;		/* Looping var */
1901   ipp_attribute_t	*prev,		/* Previous attribute */
1902 			*next,		/* Next attribute */
1903 			*attr;		/* Current attribute */
1904   cupsd_subscription_t	*sub;		/* Subscription object */
1905   const char		*recipient,	/* notify-recipient-uri */
1906 			*pullmethod;	/* notify-pull-method */
1907   ipp_attribute_t	*user_data;	/* notify-user-data */
1908   int			interval;	/* notify-time-interval */
1909   unsigned		mask;		/* notify-events */
1910 
1911 
1912  /*
1913   * Find the first subscription group attribute; return if we have
1914   * none...
1915   */
1916 
1917   for (attr = job->attrs->attrs; attr; attr = attr->next)
1918     if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
1919       break;
1920 
1921   if (!attr)
1922     return;
1923 
1924  /*
1925   * Process the subscription attributes in the request...
1926   */
1927 
1928   while (attr)
1929   {
1930     recipient = NULL;
1931     pullmethod = NULL;
1932     user_data  = NULL;
1933     interval   = 0;
1934     mask       = CUPSD_EVENT_NONE;
1935 
1936     while (attr && attr->group_tag != IPP_TAG_ZERO)
1937     {
1938       if (!strcmp(attr->name, "notify-recipient-uri") &&
1939           attr->value_tag == IPP_TAG_URI)
1940       {
1941        /*
1942         * Validate the recipient scheme against the ServerBin/notifier
1943 	* directory...
1944 	*/
1945 
1946 	char	notifier[1024],		/* Notifier filename */
1947 		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
1948 		userpass[HTTP_MAX_URI],	/* Username portion of URI */
1949 		host[HTTP_MAX_URI],	/* Host portion of URI */
1950 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
1951         int	port;			/* Port portion of URI */
1952         struct stat info;		/* File information */
1953 
1954         recipient = attr->values[0].string.text;
1955 
1956 	if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
1957 	                    scheme, sizeof(scheme), userpass, sizeof(userpass),
1958 			    host, sizeof(host), &port,
1959 			    resource, sizeof(resource)) < HTTP_URI_OK)
1960         {
1961           send_ipp_status(con, IPP_NOT_POSSIBLE,
1962 	                  _("Bad notify-recipient-uri \"%s\"."), recipient);
1963 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
1964 	                "notify-status-code", IPP_URI_SCHEME);
1965 	  return;
1966 	}
1967 
1968         snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin, scheme);
1969         if (access(notifier, X_OK) || stat(notifier, &info) || !S_ISREG(info.st_mode))
1970 	{
1971           send_ipp_status(con, IPP_NOT_POSSIBLE,
1972 	                  _("notify-recipient-uri URI \"%s\" uses unknown "
1973 			    "scheme."), recipient);
1974 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
1975 	                "notify-status-code", IPP_URI_SCHEME);
1976 	  return;
1977 	}
1978 
1979         if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
1980 	{
1981           send_ipp_status(con, IPP_NOT_POSSIBLE,
1982 	                  _("notify-recipient-uri URI \"%s\" is already used."),
1983 			  recipient);
1984 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
1985 	                "notify-status-code", IPP_ATTRIBUTES);
1986 	  return;
1987 	}
1988       }
1989       else if (!strcmp(attr->name, "notify-pull-method") &&
1990                attr->value_tag == IPP_TAG_KEYWORD)
1991       {
1992         pullmethod = attr->values[0].string.text;
1993 
1994         if (strcmp(pullmethod, "ippget"))
1995 	{
1996           send_ipp_status(con, IPP_NOT_POSSIBLE,
1997 	                  _("Bad notify-pull-method \"%s\"."), pullmethod);
1998 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
1999 	                "notify-status-code", IPP_ATTRIBUTES);
2000 	  return;
2001 	}
2002       }
2003       else if (!strcmp(attr->name, "notify-charset") &&
2004                attr->value_tag == IPP_TAG_CHARSET &&
2005 	       strcmp(attr->values[0].string.text, "us-ascii") &&
2006 	       strcmp(attr->values[0].string.text, "utf-8"))
2007       {
2008         send_ipp_status(con, IPP_CHARSET,
2009 	                _("Character set \"%s\" not supported."),
2010 			attr->values[0].string.text);
2011 	return;
2012       }
2013       else if (!strcmp(attr->name, "notify-natural-language") &&
2014                (attr->value_tag != IPP_TAG_LANGUAGE ||
2015 	        strcmp(attr->values[0].string.text, DefaultLanguage)))
2016       {
2017         send_ipp_status(con, IPP_CHARSET,
2018 	                _("Language \"%s\" not supported."),
2019 			attr->values[0].string.text);
2020 	return;
2021       }
2022       else if (!strcmp(attr->name, "notify-user-data") &&
2023                attr->value_tag == IPP_TAG_STRING)
2024       {
2025         if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
2026 	{
2027           send_ipp_status(con, IPP_REQUEST_VALUE,
2028 	                  _("The notify-user-data value is too large "
2029 			    "(%d > 63 octets)."),
2030 			  attr->values[0].unknown.length);
2031 	  return;
2032 	}
2033 
2034         user_data = attr;
2035       }
2036       else if (!strcmp(attr->name, "notify-events") &&
2037                attr->value_tag == IPP_TAG_KEYWORD)
2038       {
2039         for (i = 0; i < attr->num_values; i ++)
2040 	  mask |= cupsdEventValue(attr->values[i].string.text);
2041       }
2042       else if (!strcmp(attr->name, "notify-lease-duration"))
2043       {
2044         send_ipp_status(con, IPP_BAD_REQUEST,
2045 	                _("The notify-lease-duration attribute cannot be "
2046 			  "used with job subscriptions."));
2047 	return;
2048       }
2049       else if (!strcmp(attr->name, "notify-time-interval") &&
2050                attr->value_tag == IPP_TAG_INTEGER)
2051         interval = attr->values[0].integer;
2052 
2053       attr = attr->next;
2054     }
2055 
2056     if (!recipient && !pullmethod)
2057       break;
2058 
2059     if (mask == CUPSD_EVENT_NONE)
2060       mask = CUPSD_EVENT_JOB_COMPLETED;
2061 
2062     if ((sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job,
2063                                     recipient, 0)) != NULL)
2064     {
2065       sub->interval = interval;
2066 
2067       cupsdSetString(&sub->owner, job->username);
2068 
2069       if (user_data)
2070       {
2071 	sub->user_data_len = user_data->values[0].unknown.length;
2072 	memcpy(sub->user_data, user_data->values[0].unknown.data,
2073 	       (size_t)sub->user_data_len);
2074       }
2075 
2076       ippAddSeparator(con->response);
2077       ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
2078 		    "notify-subscription-id", sub->id);
2079 
2080       cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
2081                       sub->id, job->id);
2082     }
2083 
2084     if (attr)
2085       attr = attr->next;
2086   }
2087 
2088   cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
2089 
2090  /*
2091   * Remove all of the subscription attributes from the job request...
2092   *
2093   * TODO: Optimize this since subscription groups have to come at the
2094   * end of the request...
2095   */
2096 
2097   for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
2098   {
2099     next = attr->next;
2100 
2101     if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
2102         attr->group_tag == IPP_TAG_ZERO)
2103     {
2104      /*
2105       * Free and remove this attribute...
2106       */
2107 
2108       ippDeleteAttribute(NULL, attr);
2109 
2110       if (prev)
2111         prev->next = next;
2112       else
2113         job->attrs->attrs = next;
2114     }
2115     else
2116       prev = attr;
2117   }
2118 
2119   job->attrs->last    = prev;
2120   job->attrs->current = prev;
2121 }
2122 
2123 
2124 /*
2125  * 'add_job_uuid()' - Add job-uuid attribute to a job.
2126  *
2127  * See RFC 4122 for the definition of UUIDs and the format.
2128  */
2129 
2130 static void
add_job_uuid(cupsd_job_t * job)2131 add_job_uuid(cupsd_job_t *job)		/* I - Job */
2132 {
2133   char			uuid[64];	/* job-uuid string */
2134 
2135 
2136  /*
2137   * Add a job-uuid attribute if none exists...
2138   */
2139 
2140   if (!ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
2141     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL,
2142 		 httpAssembleUUID(ServerName, RemotePort, job->dest, job->id,
2143 		                  uuid, sizeof(uuid)));
2144 }
2145 
2146 
2147 /*
2148  * 'add_printer()' - Add a printer to the system.
2149  */
2150 
2151 static void
add_printer(cupsd_client_t * con,ipp_attribute_t * uri)2152 add_printer(cupsd_client_t  *con,	/* I - Client connection */
2153             ipp_attribute_t *uri)	/* I - URI of printer */
2154 {
2155   http_status_t	status;			/* Policy status */
2156   int		i = 0;			/* Looping var */
2157   char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
2158 		username[HTTP_MAX_URI],	/* Username portion of URI */
2159 		host[HTTP_MAX_URI],	/* Host portion of URI */
2160 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
2161   int		port;			/* Port portion of URI */
2162   cupsd_printer_t *printer;		/* Printer/class */
2163   ipp_attribute_t *attr;		/* Printer attribute */
2164   cups_file_t	*fp;			/* Script/PPD file */
2165   char		line[1024];		/* Line from file... */
2166   char		srcfile[1024],		/* Source Script/PPD file */
2167 		dstfile[1024];		/* Destination Script/PPD file */
2168   int		modify;			/* Non-zero if we are modifying */
2169   int		changed_driver,		/* Changed the PPD? */
2170 		need_restart_job,	/* Need to restart job? */
2171 		set_device_uri,		/* Did we set the device URI? */
2172 		set_port_monitor;	/* Did we set the port monitor? */
2173 
2174 
2175   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", (void *)con,
2176                   con->number, uri->values[0].string.text);
2177 
2178  /*
2179   * Do we have a valid URI?
2180   */
2181 
2182   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
2183                   sizeof(scheme), username, sizeof(username), host,
2184 		  sizeof(host), &port, resource, sizeof(resource));
2185 
2186   if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10)
2187   {
2188    /*
2189     * No, return an error...
2190     */
2191 
2192     send_ipp_status(con, IPP_BAD_REQUEST,
2193                     _("The printer-uri must be of the form "
2194 		      "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
2195     return;
2196   }
2197 
2198  /*
2199   * Do we have a valid printer name?
2200   */
2201 
2202   if (!validate_name(resource + 10))
2203   {
2204    /*
2205     * No, return an error...
2206     */
2207 
2208     send_ipp_status(con, IPP_BAD_REQUEST,
2209                     _("The printer-uri \"%s\" contains invalid characters."),
2210 		    uri->values[0].string.text);
2211     return;
2212   }
2213 
2214  /*
2215   * See if the printer already exists; if not, create a new printer...
2216   */
2217 
2218   if ((printer = cupsdFindPrinter(resource + 10)) == NULL)
2219   {
2220    /*
2221     * Printer doesn't exist; see if we have a class of the same name...
2222     */
2223 
2224     if (cupsdFindClass(resource + 10))
2225     {
2226      /*
2227       * Yes, return an error...
2228       */
2229 
2230       send_ipp_status(con, IPP_NOT_POSSIBLE,
2231                       _("A class named \"%s\" already exists."),
2232         	      resource + 10);
2233       return;
2234     }
2235 
2236    /*
2237     * No, check the default policy then add the printer...
2238     */
2239 
2240     if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
2241     {
2242       send_http_error(con, status, NULL);
2243       return;
2244     }
2245 
2246     printer = cupsdAddPrinter(resource + 10);
2247     modify  = 0;
2248 
2249     printer->printer_id = NextPrinterId ++;
2250   }
2251   else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
2252                                       NULL)) != HTTP_OK)
2253   {
2254     send_http_error(con, status, printer);
2255     return;
2256   }
2257   else
2258     modify = 1;
2259 
2260  /*
2261   * Look for attributes and copy them over as needed...
2262   */
2263 
2264   changed_driver   = 0;
2265   need_restart_job = 0;
2266 
2267   if ((attr = ippFindAttribute(con->request, "printer-is-temporary", IPP_TAG_BOOLEAN)) != NULL)
2268     printer->temporary = ippGetBoolean(attr, 0);
2269 
2270   if ((attr = ippFindAttribute(con->request, "printer-location",
2271                                IPP_TAG_TEXT)) != NULL)
2272     cupsdSetString(&printer->location, attr->values[0].string.text);
2273 
2274   if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
2275     cupsdSetString(&printer->geo_location, attr->values[0].string.text);
2276 
2277   if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
2278     cupsdSetString(&printer->organization, attr->values[0].string.text);
2279 
2280   if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
2281     cupsdSetString(&printer->organizational_unit, attr->values[0].string.text);
2282 
2283   if ((attr = ippFindAttribute(con->request, "printer-info",
2284                                IPP_TAG_TEXT)) != NULL)
2285     cupsdSetString(&printer->info, attr->values[0].string.text);
2286 
2287   set_device_uri = 0;
2288 
2289   if ((attr = ippFindAttribute(con->request, "ColorModel", IPP_TAG_NAME)) != NULL)
2290   {
2291     const char * keyword = NULL;
2292 
2293     if (!strcmp(attr->values[0].string.text, "FastGray") || !strcmp(attr->values[0].string.text, "Gray") || !strcmp(attr->values[0].string.text, "DeviceGray"))
2294       keyword = "monochrome";
2295     else
2296       keyword = "color";
2297 
2298     printer->num_options = cupsAddOption("print-color-mode", keyword, printer->num_options, &printer->options);
2299   }
2300 
2301   if ((attr = ippFindAttribute(con->request, "device-uri",
2302                                IPP_TAG_URI)) != NULL)
2303   {
2304    /*
2305     * Do we have a valid device URI?
2306     */
2307 
2308     http_uri_status_t	uri_status;	/* URI separation status */
2309     char		old_device_uri[1024];
2310 					/* Old device URI */
2311 
2312     need_restart_job = 1;
2313 
2314     uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
2315 				 attr->values[0].string.text,
2316 				 scheme, sizeof(scheme),
2317 				 username, sizeof(username),
2318 				 host, sizeof(host), &port,
2319 				 resource, sizeof(resource));
2320 
2321     cupsdLogMessage(CUPSD_LOG_DEBUG, "%s device-uri: %s", printer->name, httpURIStatusString(uri_status));
2322 
2323     if (uri_status < HTTP_URI_OK)
2324     {
2325       send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"."),
2326 		      attr->values[0].string.text);
2327       if (!modify)
2328         cupsdDeletePrinter(printer, 0);
2329 
2330       return;
2331     }
2332 
2333     if (!strcmp(scheme, "file"))
2334     {
2335      /*
2336       * See if the administrator has enabled file devices...
2337       */
2338 
2339       if (!FileDevice && strcmp(resource, "/dev/null"))
2340       {
2341        /*
2342         * File devices are disabled and the URL is not file:/dev/null...
2343 	*/
2344 
2345 	send_ipp_status(con, IPP_NOT_POSSIBLE,
2346 	                _("File device URIs have been disabled. "
2347 	                  "To enable, see the FileDevice directive in "
2348 			  "\"%s/cups-files.conf\"."),
2349 			ServerRoot);
2350 	if (!modify)
2351 	  cupsdDeletePrinter(printer, 0);
2352 
2353 	return;
2354       }
2355     }
2356     else
2357     {
2358      /*
2359       * See if the backend exists and is executable...
2360       */
2361 
2362       snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, scheme);
2363       if (access(srcfile, X_OK))
2364       {
2365        /*
2366         * Could not find device in list!
2367 	*/
2368 
2369 	send_ipp_status(con, IPP_NOT_POSSIBLE,
2370                         _("Bad device-uri scheme \"%s\"."), scheme);
2371 	if (!modify)
2372 	  cupsdDeletePrinter(printer, 0);
2373 
2374 	return;
2375       }
2376     }
2377 
2378     if (printer->sanitized_device_uri)
2379       strlcpy(old_device_uri, printer->sanitized_device_uri,
2380               sizeof(old_device_uri));
2381     else
2382       old_device_uri[0] = '\0';
2383 
2384     cupsdSetDeviceURI(printer, attr->values[0].string.text);
2385 
2386     cupsdLogMessage(CUPSD_LOG_INFO,
2387                     "Setting %s device-uri to \"%s\" (was \"%s\".)",
2388         	    printer->name, printer->sanitized_device_uri,
2389 		    old_device_uri);
2390 
2391     set_device_uri = 1;
2392   }
2393 
2394   set_port_monitor = 0;
2395 
2396   if ((attr = ippFindAttribute(con->request, "port-monitor",
2397                                IPP_TAG_NAME)) != NULL)
2398   {
2399     ipp_attribute_t	*supported;	/* port-monitor-supported attribute */
2400 
2401 
2402     need_restart_job = 1;
2403 
2404     supported = ippFindAttribute(printer->ppd_attrs, "port-monitor-supported",
2405                                  IPP_TAG_NAME);
2406     if (supported)
2407     {
2408       for (i = 0; i < supported->num_values; i ++)
2409         if (!strcmp(supported->values[i].string.text,
2410                     attr->values[0].string.text))
2411           break;
2412     }
2413 
2414     if (!supported || i >= supported->num_values)
2415     {
2416       send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"."),
2417         	      attr->values[0].string.text);
2418       if (!modify)
2419         cupsdDeletePrinter(printer, 0);
2420 
2421       return;
2422     }
2423 
2424     cupsdLogMessage(CUPSD_LOG_INFO,
2425                     "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2426                     printer->name, attr->values[0].string.text,
2427 	            printer->port_monitor ? printer->port_monitor : "none");
2428 
2429     if (strcmp(attr->values[0].string.text, "none"))
2430       cupsdSetString(&printer->port_monitor, attr->values[0].string.text);
2431     else
2432       cupsdClearString(&printer->port_monitor);
2433 
2434     set_port_monitor = 1;
2435   }
2436 
2437   if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
2438                                IPP_TAG_BOOLEAN)) != NULL &&
2439       attr->values[0].boolean != printer->accepting)
2440   {
2441     cupsdLogMessage(CUPSD_LOG_INFO,
2442                     "Setting %s printer-is-accepting-jobs to %d (was %d.)",
2443                     printer->name, attr->values[0].boolean, printer->accepting);
2444 
2445     printer->accepting = attr->values[0].boolean;
2446 
2447     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2448                   "%s accepting jobs.",
2449 		  printer->accepting ? "Now" : "No longer");
2450   }
2451 
2452   if ((attr = ippFindAttribute(con->request, "printer-is-shared", IPP_TAG_BOOLEAN)) != NULL)
2453   {
2454     if (ippGetBoolean(attr, 0) &&
2455         printer->num_auth_info_required == 1 &&
2456 	!strcmp(printer->auth_info_required[0], "negotiate"))
2457     {
2458       send_ipp_status(con, IPP_BAD_REQUEST,
2459                       _("Cannot share a remote Kerberized printer."));
2460       if (!modify)
2461         cupsdDeletePrinter(printer, 0);
2462 
2463       return;
2464     }
2465 
2466     if (printer->type & CUPS_PRINTER_REMOTE)
2467     {
2468      /*
2469       * Cannot re-share remote printers.
2470       */
2471 
2472       send_ipp_status(con, IPP_BAD_REQUEST, _("Cannot change printer-is-shared for remote queues."));
2473       if (!modify)
2474         cupsdDeletePrinter(printer, 0);
2475 
2476       return;
2477     }
2478 
2479     if (printer->shared && !ippGetBoolean(attr, 0))
2480       cupsdDeregisterPrinter(printer, 1);
2481 
2482     cupsdLogMessage(CUPSD_LOG_INFO,
2483                     "Setting %s printer-is-shared to %d (was %d.)",
2484                     printer->name, attr->values[0].boolean, printer->shared);
2485 
2486     printer->shared = ippGetBoolean(attr, 0);
2487     if (printer->shared && printer->temporary)
2488       printer->temporary = 0;
2489   }
2490 
2491   if ((attr = ippFindAttribute(con->request, "printer-state",
2492                                IPP_TAG_ENUM)) != NULL)
2493   {
2494     if (attr->values[0].integer != IPP_PRINTER_IDLE &&
2495         attr->values[0].integer != IPP_PRINTER_STOPPED)
2496     {
2497       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d."),
2498                       attr->values[0].integer);
2499       if (!modify)
2500         cupsdDeletePrinter(printer, 0);
2501 
2502       return;
2503     }
2504 
2505     cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)",
2506                     printer->name, attr->values[0].integer, printer->state);
2507 
2508     if (attr->values[0].integer == IPP_PRINTER_STOPPED)
2509       cupsdStopPrinter(printer, 0);
2510     else
2511     {
2512       need_restart_job = 1;
2513       cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
2514     }
2515   }
2516 
2517   if ((attr = ippFindAttribute(con->request, "printer-state-message",
2518                                IPP_TAG_TEXT)) != NULL)
2519   {
2520     strlcpy(printer->state_message, attr->values[0].string.text,
2521             sizeof(printer->state_message));
2522 
2523     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, "%s",
2524                   printer->state_message);
2525   }
2526 
2527   if ((attr = ippFindAttribute(con->request, "printer-state-reasons",
2528                                IPP_TAG_KEYWORD)) != NULL)
2529   {
2530     if (attr->num_values >
2531             (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0])))
2532     {
2533       send_ipp_status(con, IPP_NOT_POSSIBLE,
2534                       _("Too many printer-state-reasons values (%d > %d)."),
2535 		      attr->num_values,
2536 		      (int)(sizeof(printer->reasons) /
2537 		            sizeof(printer->reasons[0])));
2538       if (!modify)
2539         cupsdDeletePrinter(printer, 0);
2540 
2541       return;
2542     }
2543 
2544     for (i = 0; i < printer->num_reasons; i ++)
2545       _cupsStrFree(printer->reasons[i]);
2546 
2547     printer->num_reasons = 0;
2548     for (i = 0; i < attr->num_values; i ++)
2549     {
2550       if (!strcmp(attr->values[i].string.text, "none"))
2551         continue;
2552 
2553       printer->reasons[printer->num_reasons] = _cupsStrAlloc(attr->values[i].string.text);
2554       printer->num_reasons ++;
2555 
2556       if (!strcmp(attr->values[i].string.text, "paused") &&
2557           printer->state != IPP_PRINTER_STOPPED)
2558       {
2559 	cupsdLogMessage(CUPSD_LOG_INFO,
2560 	                "Setting %s printer-state to %d (was %d.)",
2561 			printer->name, IPP_PRINTER_STOPPED, printer->state);
2562 	cupsdStopPrinter(printer, 0);
2563       }
2564     }
2565 
2566     if (PrintcapFormat == PRINTCAP_PLIST)
2567       cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
2568 
2569     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
2570                   "Printer \"%s\" state changed.", printer->name);
2571   }
2572 
2573   if (!set_printer_defaults(con, printer))
2574   {
2575     if (!modify)
2576       cupsdDeletePrinter(printer, 0);
2577 
2578     return;
2579   }
2580 
2581   if ((attr = ippFindAttribute(con->request, "auth-info-required",
2582                                IPP_TAG_KEYWORD)) != NULL)
2583     cupsdSetAuthInfoRequired(printer, NULL, attr);
2584 
2585  /*
2586   * See if we have all required attributes...
2587   */
2588 
2589   if (!printer->device_uri)
2590     cupsdSetString(&printer->device_uri, "file:///dev/null");
2591 
2592  /*
2593   * See if we have a PPD file attached to the request...
2594   */
2595 
2596   if (con->filename)
2597   {
2598     need_restart_job = 1;
2599     changed_driver   = 1;
2600 
2601     strlcpy(srcfile, con->filename, sizeof(srcfile));
2602 
2603     if ((fp = cupsFileOpen(srcfile, "rb")))
2604     {
2605      /*
2606       * Yes; get the first line from it...
2607       */
2608 
2609       line[0] = '\0';
2610       cupsFileGets(fp, line, sizeof(line));
2611       cupsFileClose(fp);
2612 
2613      /*
2614       * Then see what kind of file it is...
2615       */
2616 
2617       if (strncmp(line, "*PPD-Adobe", 10))
2618       {
2619 	send_ipp_status(con, IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED, _("Bad PPD file."));
2620 	if (!modify)
2621 	  cupsdDeletePrinter(printer, 0);
2622 
2623 	return;
2624       }
2625 
2626       snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
2627                printer->name);
2628 
2629      /*
2630       * The new file is a PPD file, so move the file over to the ppd
2631       * directory...
2632       */
2633 
2634       if (copy_file(srcfile, dstfile, ConfigFilePerm))
2635       {
2636 	send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file - %s"), strerror(errno));
2637 	if (!modify)
2638 	  cupsdDeletePrinter(printer, 0);
2639 
2640 	return;
2641       }
2642 
2643       cupsdLogMessage(CUPSD_LOG_DEBUG, "Copied PPD file successfully");
2644     }
2645   }
2646   else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
2647   {
2648     const char *ppd_name = ippGetString(attr, 0, NULL);
2649 					/* ppd-name value */
2650 
2651     need_restart_job = 1;
2652     changed_driver   = 1;
2653 
2654     if (!strcmp(ppd_name, "everywhere"))
2655     {
2656       // Create IPP Everywhere PPD...
2657       if (!printer->device_uri || (strncmp(printer->device_uri, "dnssd://", 8) && strncmp(printer->device_uri, "ipp://", 6) && strncmp(printer->device_uri, "ipps://", 7) && strncmp(printer->device_uri, "ippusb://", 9)))
2658       {
2659 	send_ipp_status(con, IPP_INTERNAL_ERROR, _("IPP Everywhere driver requires an IPP connection."));
2660 	if (!modify)
2661 	  cupsdDeletePrinter(printer, 0);
2662 
2663 	return;
2664       }
2665 
2666       if (!printer->printer_id)
2667 	printer->printer_id = NextPrinterId ++;
2668 
2669       cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
2670 
2671       cupsdSetPrinterAttrs(printer);
2672 
2673       /* Run a background thread to create the PPD... */
2674       cupsdLogClient(con, CUPSD_LOG_DEBUG, "Creating PPD in background thread.");
2675 
2676       con->bg_pending = 1;
2677       con->bg_printer = printer;
2678 
2679       _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, con);
2680       return;
2681     }
2682     else if (!strcmp(ppd_name, "raw"))
2683     {
2684      /*
2685       * Raw driver, remove any existing PPD file.
2686       */
2687 
2688       snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot, printer->name);
2689       unlink(dstfile);
2690     }
2691     else if (strstr(ppd_name, "../"))
2692     {
2693       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Invalid ppd-name value."));
2694       if (!modify)
2695 	cupsdDeletePrinter(printer, 0);
2696 
2697       return;
2698     }
2699     else
2700     {
2701      /*
2702       * PPD model file...
2703       */
2704 
2705       snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot, printer->name);
2706 
2707       if (copy_model(con, ppd_name, dstfile))
2708       {
2709 	if (!modify)
2710 	  cupsdDeletePrinter(printer, 0);
2711 
2712 	return;
2713       }
2714 
2715       cupsdLogMessage(CUPSD_LOG_DEBUG, "Copied PPD file successfully");
2716     }
2717   }
2718 
2719   if (changed_driver)
2720   {
2721    /*
2722     * If we changed the PPD, then remove the printer's cache file and clear the
2723     * printer-state-reasons...
2724     */
2725 
2726     char cache_name[1024];		/* Cache filename for printer attrs */
2727 
2728     snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir, printer->name);
2729     unlink(cache_name);
2730 
2731     cupsdSetPrinterReasons(printer, "none");
2732 
2733    /*
2734     * (Re)register color profiles...
2735     */
2736 
2737     cupsdRegisterColor(printer);
2738   }
2739 
2740  /*
2741   * If we set the device URI but not the port monitor, check which port
2742   * monitor to use by default...
2743   */
2744 
2745   if (set_device_uri && !set_port_monitor)
2746   {
2747     ppd_file_t	*ppd;			/* PPD file */
2748     ppd_attr_t	*ppdattr;		/* cupsPortMonitor attribute */
2749 
2750 
2751     httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
2752                     sizeof(scheme), username, sizeof(username), host,
2753 		    sizeof(host), &port, resource, sizeof(resource));
2754 
2755     snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
2756 	     printer->name);
2757     if ((ppd = _ppdOpenFile(srcfile, _PPD_LOCALIZATION_NONE)) != NULL)
2758     {
2759       for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
2760 	   ppdattr;
2761 	   ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
2762         if (!strcmp(scheme, ppdattr->spec))
2763 	{
2764 	  cupsdLogMessage(CUPSD_LOG_INFO,
2765 			  "Setting %s port-monitor to \"%s\" (was \"%s\".)",
2766 			  printer->name, ppdattr->value,
2767 			  printer->port_monitor ? printer->port_monitor
2768 			                        : "none");
2769 
2770 	  if (strcmp(ppdattr->value, "none"))
2771 	    cupsdSetString(&printer->port_monitor, ppdattr->value);
2772 	  else
2773 	    cupsdClearString(&printer->port_monitor);
2774 
2775 	  break;
2776 	}
2777 
2778       ppdClose(ppd);
2779     }
2780   }
2781 
2782   printer->config_time = time(NULL);
2783 
2784  /*
2785   * Update the printer attributes and return...
2786   */
2787 
2788   if (!printer->temporary)
2789   {
2790     if (!printer->printer_id)
2791       printer->printer_id = NextPrinterId ++;
2792 
2793     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
2794   }
2795 
2796   cupsdSetPrinterAttrs(printer);
2797 
2798   if (need_restart_job && printer->job)
2799   {
2800    /*
2801     * Restart the current job...
2802     */
2803 
2804     cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
2805                      "Job restarted because the printer was modified.");
2806   }
2807 
2808   cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
2809 
2810   if (modify)
2811   {
2812     cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
2813                   printer, NULL, "Printer \"%s\" modified by \"%s\".",
2814 		  printer->name, get_username(con));
2815 
2816     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
2817                     printer->name, get_username(con));
2818   }
2819   else
2820   {
2821     cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
2822                   printer, NULL, "New printer \"%s\" added by \"%s\".",
2823 		  printer->name, get_username(con));
2824 
2825     cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
2826                     printer->name, get_username(con));
2827   }
2828 
2829   con->response->request.status.status_code = IPP_OK;
2830 }
2831 
2832 
2833 /*
2834  * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
2835  *                                 based upon the printer state...
2836  */
2837 
2838 static void
add_printer_state_reasons(cupsd_client_t * con,cupsd_printer_t * p)2839 add_printer_state_reasons(
2840     cupsd_client_t  *con,		/* I - Client connection */
2841     cupsd_printer_t *p)			/* I - Printer info */
2842 {
2843   cupsdLogMessage(CUPSD_LOG_DEBUG2,
2844                   "add_printer_state_reasons(%p[%d], %p[%s])",
2845                   (void *)con, con->number, (void *)p, p->name);
2846 
2847   if (p->num_reasons == 0)
2848     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2849                  "printer-state-reasons", NULL, "none");
2850   else
2851     ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
2852                   "printer-state-reasons", p->num_reasons, NULL,
2853 		  (const char * const *)p->reasons);
2854 }
2855 
2856 
2857 /*
2858  * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
2859  *                            the specified printer or class.
2860  */
2861 
2862 static void
add_queued_job_count(cupsd_client_t * con,cupsd_printer_t * p)2863 add_queued_job_count(
2864     cupsd_client_t  *con,		/* I - Client connection */
2865     cupsd_printer_t *p)			/* I - Printer or class */
2866 {
2867   int		count;			/* Number of jobs on destination */
2868 
2869 
2870   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
2871                   (void *)con, con->number, (void *)p, p->name);
2872 
2873   count = cupsdGetPrinterJobCount(p->name);
2874 
2875   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
2876                 "queued-job-count", count);
2877 }
2878 
2879 
2880 /*
2881  * 'apply_printer_defaults()' - Apply printer default options to a job.
2882  */
2883 
2884 static void
apply_printer_defaults(cupsd_printer_t * printer,cupsd_job_t * job)2885 apply_printer_defaults(
2886     cupsd_printer_t *printer,		/* I - Printer */
2887     cupsd_job_t     *job)		/* I - Job */
2888 {
2889   int		i,			/* Looping var */
2890 		num_options;		/* Number of default options */
2891   cups_option_t	*options,		/* Default options */
2892 		*option;		/* Current option */
2893 
2894 
2895   cupsdLogJob(job, CUPSD_LOG_DEBUG, "Applying default options...");
2896 
2897  /*
2898   * Collect all of the default options and add the missing ones to the
2899   * job object...
2900   */
2901 
2902   for (i = printer->num_options, num_options = 0, options = NULL,
2903            option = printer->options;
2904        i > 0;
2905        i --, option ++)
2906     if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
2907     {
2908       if (!strcmp(option->name, "media") && ippFindAttribute(job->attrs, "PageSize", IPP_TAG_NAME))
2909         continue;                     /* Don't override PageSize */
2910 
2911       if (!strcmp(option->name, "output-bin") && ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_NAME))
2912         continue;                     /* Don't override OutputBin */
2913 
2914       if (!strcmp(option->name, "print-quality") && ippFindAttribute(job->attrs, "cupsPrintQuality", IPP_TAG_NAME))
2915         continue;                     /* Don't override cupsPrintQuality */
2916 
2917       if (!strcmp(option->name, "print-color-mode") && ippFindAttribute(job->attrs, "ColorModel", IPP_TAG_NAME))
2918         continue;                     /* Don't override ColorModel */
2919 
2920       if (!strcmp(option->name, "sides") && ippFindAttribute(job->attrs, "Duplex", IPP_TAG_NAME))
2921         continue;                     /* Don't override Duplex */
2922 
2923       cupsdLogJob(job, CUPSD_LOG_DEBUG, "Adding default %s=%s", option->name, option->value);
2924 
2925       num_options = cupsAddOption(option->name, option->value, num_options, &options);
2926     }
2927 
2928  /*
2929   * Encode these options as attributes in the job object...
2930   */
2931 
2932   cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
2933   cupsFreeOptions(num_options, options);
2934 }
2935 
2936 
2937 /*
2938  * 'authenticate_job()' - Set job authentication info.
2939  */
2940 
2941 static void
authenticate_job(cupsd_client_t * con,ipp_attribute_t * uri)2942 authenticate_job(cupsd_client_t  *con,	/* I - Client connection */
2943 	         ipp_attribute_t *uri)	/* I - Job URI */
2944 {
2945   ipp_attribute_t	*attr,		/* job-id attribute */
2946 			*auth_info;	/* auth-info attribute */
2947   int			jobid;		/* Job ID */
2948   cupsd_job_t		*job;		/* Current job */
2949   char			scheme[HTTP_MAX_URI],
2950 					/* Method portion of URI */
2951 			username[HTTP_MAX_URI],
2952 					/* Username portion of URI */
2953 			host[HTTP_MAX_URI],
2954 					/* Host portion of URI */
2955 			resource[HTTP_MAX_URI];
2956 					/* Resource portion of URI */
2957   int			port;		/* Port portion of URI */
2958 
2959 
2960   cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
2961                   (void *)con, con->number, uri->values[0].string.text);
2962 
2963  /*
2964   * Start with "everything is OK" status...
2965   */
2966 
2967   con->response->request.status.status_code = IPP_OK;
2968 
2969  /*
2970   * See if we have a job URI or a printer URI...
2971   */
2972 
2973   if (!strcmp(uri->name, "printer-uri"))
2974   {
2975    /*
2976     * Got a printer URI; see if we also have a job-id attribute...
2977     */
2978 
2979     if ((attr = ippFindAttribute(con->request, "job-id",
2980                                  IPP_TAG_INTEGER)) == NULL)
2981     {
2982       send_ipp_status(con, IPP_BAD_REQUEST,
2983                       _("Got a printer-uri attribute but no job-id."));
2984       return;
2985     }
2986 
2987     jobid = attr->values[0].integer;
2988   }
2989   else
2990   {
2991    /*
2992     * Got a job URI; parse it to get the job ID...
2993     */
2994 
2995     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
2996                     sizeof(scheme), username, sizeof(username), host,
2997 		    sizeof(host), &port, resource, sizeof(resource));
2998 
2999     if (strncmp(resource, "/jobs/", 6))
3000     {
3001      /*
3002       * Not a valid URI!
3003       */
3004 
3005       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
3006                       uri->values[0].string.text);
3007       return;
3008     }
3009 
3010     jobid = atoi(resource + 6);
3011   }
3012 
3013  /*
3014   * See if the job exists...
3015   */
3016 
3017   if ((job = cupsdFindJob(jobid)) == NULL)
3018   {
3019    /*
3020     * Nope - return a "not found" error...
3021     */
3022 
3023     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
3024     return;
3025   }
3026 
3027  /*
3028   * See if the job has been completed...
3029   */
3030 
3031   if (job->state_value != IPP_JOB_HELD)
3032   {
3033    /*
3034     * Return a "not-possible" error...
3035     */
3036 
3037     send_ipp_status(con, IPP_NOT_POSSIBLE,
3038                     _("Job #%d is not held for authentication."),
3039 		    jobid);
3040     return;
3041   }
3042 
3043  /*
3044   * See if we have already authenticated...
3045   */
3046 
3047   auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
3048 
3049   if (!con->username[0] && !auth_info)
3050   {
3051     cupsd_printer_t	*printer;	/* Job destination */
3052 
3053    /*
3054     * No auth data.  If we need to authenticate via Kerberos, send a
3055     * HTTP auth challenge, otherwise just return an IPP error...
3056     */
3057 
3058     printer = cupsdFindDest(job->dest);
3059 
3060     if (printer && printer->num_auth_info_required > 0 &&
3061         !strcmp(printer->auth_info_required[0], "negotiate"))
3062       send_http_error(con, HTTP_UNAUTHORIZED, printer);
3063     else
3064       send_ipp_status(con, IPP_NOT_AUTHORIZED,
3065 		      _("No authentication information provided."));
3066     return;
3067   }
3068 
3069  /*
3070   * See if the job is owned by the requesting user...
3071   */
3072 
3073   if (!validate_user(job, con, job->username, username, sizeof(username)))
3074   {
3075     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3076                     cupsdFindDest(job->dest));
3077     return;
3078   }
3079 
3080  /*
3081   * Save the authentication information for this job...
3082   */
3083 
3084   save_auth_info(con, job, auth_info);
3085 
3086  /*
3087   * Reset the job-hold-until value to "no-hold"...
3088   */
3089 
3090   if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3091                                IPP_TAG_KEYWORD)) == NULL)
3092     attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3093 
3094   if (attr)
3095   {
3096     ippSetValueTag(job->attrs, &attr, IPP_TAG_KEYWORD);
3097     ippSetString(job->attrs, &attr, 0, "no-hold");
3098   }
3099 
3100  /*
3101   * Release the job and return...
3102   */
3103 
3104   cupsdReleaseJob(job);
3105 
3106   cupsdAddEvent(CUPSD_EVENT_JOB_STATE, NULL, job, "Job authenticated by user");
3107 
3108   cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
3109 
3110   cupsdCheckJobs();
3111 }
3112 
3113 
3114 /*
3115  * 'cancel_all_jobs()' - Cancel all or selected print jobs.
3116  */
3117 
3118 static void
cancel_all_jobs(cupsd_client_t * con,ipp_attribute_t * uri)3119 cancel_all_jobs(cupsd_client_t  *con,	/* I - Client connection */
3120 	        ipp_attribute_t *uri)	/* I - Job or Printer URI */
3121 {
3122   int		i;			/* Looping var */
3123   http_status_t	status;			/* Policy status */
3124   cups_ptype_t	dtype;			/* Destination type */
3125   char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
3126 		userpass[HTTP_MAX_URI],	/* Username portion of URI */
3127 		hostname[HTTP_MAX_URI],	/* Host portion of URI */
3128 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
3129   int		port;			/* Port portion of URI */
3130   ipp_attribute_t *attr;		/* Attribute in request */
3131   const char	*username = NULL;	/* Username */
3132   cupsd_jobaction_t purge = CUPSD_JOB_DEFAULT;
3133 					/* Purge? */
3134   cupsd_printer_t *printer;		/* Printer */
3135   ipp_attribute_t *job_ids;		/* job-ids attribute */
3136   cupsd_job_t	*job;			/* Job */
3137 
3138 
3139   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", (void *)con,
3140                   con->number, uri->values[0].string.text);
3141 
3142  /*
3143   * Get the jobs to cancel/purge...
3144   */
3145 
3146   switch (con->request->request.op.operation_id)
3147   {
3148     case IPP_PURGE_JOBS :
3149        /*
3150 	* Get the username (if any) for the jobs we want to cancel (only if
3151 	* "my-jobs" is specified...
3152 	*/
3153 
3154         if ((attr = ippFindAttribute(con->request, "my-jobs",
3155                                      IPP_TAG_BOOLEAN)) != NULL &&
3156             attr->values[0].boolean)
3157 	{
3158 	  if ((attr = ippFindAttribute(con->request, "requesting-user-name",
3159 				       IPP_TAG_NAME)) != NULL)
3160 	    username = attr->values[0].string.text;
3161 	  else
3162 	  {
3163 	    send_ipp_status(con, IPP_BAD_REQUEST,
3164 			    _("Missing requesting-user-name attribute."));
3165 	    return;
3166 	  }
3167 	}
3168 
3169        /*
3170 	* Look for the "purge-jobs" attribute...
3171 	*/
3172 
3173 	if ((attr = ippFindAttribute(con->request, "purge-jobs",
3174 				     IPP_TAG_BOOLEAN)) != NULL)
3175 	  purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
3176 	else
3177 	  purge = CUPSD_JOB_PURGE;
3178 	break;
3179 
3180     case IPP_CANCEL_MY_JOBS :
3181         if (con->username[0])
3182           username = con->username;
3183         else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
3184 					  IPP_TAG_NAME)) != NULL)
3185           username = attr->values[0].string.text;
3186         else
3187         {
3188 	  send_ipp_status(con, IPP_BAD_REQUEST,
3189 			  _("Missing requesting-user-name attribute."));
3190 	  return;
3191         }
3192 
3193     default :
3194         break;
3195   }
3196 
3197   job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
3198 
3199  /*
3200   * See if we have a printer URI...
3201   */
3202 
3203   if (strcmp(uri->name, "printer-uri"))
3204   {
3205     send_ipp_status(con, IPP_BAD_REQUEST,
3206                     _("The printer-uri attribute is required."));
3207     return;
3208   }
3209 
3210  /*
3211   * And if the destination is valid...
3212   */
3213 
3214   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
3215   {
3216    /*
3217     * Bad URI?
3218     */
3219 
3220     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
3221                     scheme, sizeof(scheme), userpass, sizeof(userpass),
3222 		    hostname, sizeof(hostname), &port,
3223 		    resource, sizeof(resource));
3224 
3225     if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
3226         (!strncmp(resource, "/classes/", 9) && resource[9]))
3227     {
3228       send_ipp_status(con, IPP_NOT_FOUND,
3229                       _("The printer or class does not exist."));
3230       return;
3231     }
3232 
3233    /*
3234     * Check policy...
3235     */
3236 
3237     if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
3238     {
3239       send_http_error(con, status, NULL);
3240       return;
3241     }
3242 
3243     if (job_ids)
3244     {
3245       for (i = 0; i < job_ids->num_values; i ++)
3246       {
3247 	if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL)
3248 	  break;
3249 
3250         if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
3251             _cups_strcasecmp(job->username, username))
3252           break;
3253       }
3254 
3255       if (i < job_ids->num_values)
3256       {
3257 	send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
3258 			job_ids->values[i].integer);
3259 	return;
3260       }
3261 
3262       for (i = 0; i < job_ids->num_values; i ++)
3263       {
3264 	job = cupsdFindJob(job_ids->values[i].integer);
3265 
3266 	cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
3267 	                 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
3268 	                                            "Job canceled by user.");
3269       }
3270 
3271       cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
3272 		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3273 		      get_username(con));
3274     }
3275     else
3276     {
3277      /*
3278       * Cancel all jobs on all printers...
3279       */
3280 
3281       cupsdCancelJobs(NULL, username, purge != CUPSD_JOB_DEFAULT);
3282 
3283       cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
3284 		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3285 		      get_username(con));
3286     }
3287   }
3288   else
3289   {
3290    /*
3291     * Check policy...
3292     */
3293 
3294     if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
3295                                    NULL)) != HTTP_OK)
3296     {
3297       send_http_error(con, status, printer);
3298       return;
3299     }
3300 
3301     if (job_ids)
3302     {
3303       for (i = 0; i < job_ids->num_values; i ++)
3304       {
3305 	if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL ||
3306 	    _cups_strcasecmp(job->dest, printer->name))
3307 	  break;
3308 
3309         if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
3310             _cups_strcasecmp(job->username, username))
3311           break;
3312       }
3313 
3314       if (i < job_ids->num_values)
3315       {
3316 	send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
3317 			job_ids->values[i].integer);
3318 	return;
3319       }
3320 
3321       for (i = 0; i < job_ids->num_values; i ++)
3322       {
3323 	job = cupsdFindJob(job_ids->values[i].integer);
3324 
3325 	cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
3326 	                 purge == CUPSD_JOB_PURGE ? "Job purged by user." :
3327 	                                            "Job canceled by user.");
3328       }
3329 
3330       cupsdLogMessage(CUPSD_LOG_INFO, "Selected jobs were %s by \"%s\".",
3331 		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3332 		      get_username(con));
3333     }
3334     else
3335     {
3336      /*
3337       * Cancel all of the jobs on the named printer...
3338       */
3339 
3340       cupsdCancelJobs(printer->name, username, purge != CUPSD_JOB_DEFAULT);
3341 
3342       cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
3343 		      printer->name,
3344 		      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
3345 		      get_username(con));
3346     }
3347   }
3348 
3349   con->response->request.status.status_code = IPP_OK;
3350 
3351   cupsdCheckJobs();
3352 }
3353 
3354 
3355 /*
3356  * 'cancel_job()' - Cancel a print job.
3357  */
3358 
3359 static void
cancel_job(cupsd_client_t * con,ipp_attribute_t * uri)3360 cancel_job(cupsd_client_t  *con,	/* I - Client connection */
3361 	   ipp_attribute_t *uri)	/* I - Job or Printer URI */
3362 {
3363   ipp_attribute_t *attr;		/* Current attribute */
3364   int		jobid;			/* Job ID */
3365   char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
3366 		username[HTTP_MAX_URI],	/* Username portion of URI */
3367 		host[HTTP_MAX_URI],	/* Host portion of URI */
3368 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
3369   int		port;			/* Port portion of URI */
3370   cupsd_job_t	*job;			/* Job information */
3371   cups_ptype_t	dtype;			/* Destination type (printer/class) */
3372   cupsd_printer_t *printer;		/* Printer data */
3373   cupsd_jobaction_t purge;		/* Purge the job? */
3374 
3375 
3376   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", (void *)con,
3377                   con->number, uri->values[0].string.text);
3378 
3379  /*
3380   * See if we have a job URI or a printer URI...
3381   */
3382 
3383   if (!strcmp(uri->name, "printer-uri"))
3384   {
3385    /*
3386     * Got a printer URI; see if we also have a job-id attribute...
3387     */
3388 
3389     if ((attr = ippFindAttribute(con->request, "job-id",
3390                                  IPP_TAG_INTEGER)) == NULL)
3391     {
3392       send_ipp_status(con, IPP_BAD_REQUEST,
3393                       _("Got a printer-uri attribute but no job-id."));
3394       return;
3395     }
3396 
3397     if ((jobid = attr->values[0].integer) == 0)
3398     {
3399      /*
3400       * Find the current job on the specified printer...
3401       */
3402 
3403       if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
3404       {
3405        /*
3406 	* Bad URI...
3407 	*/
3408 
3409 	send_ipp_status(con, IPP_NOT_FOUND,
3410                 	_("The printer or class does not exist."));
3411 	return;
3412       }
3413 
3414      /*
3415       * See if there are any pending jobs...
3416       */
3417 
3418       for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3419 	   job;
3420 	   job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3421 	if (job->state_value <= IPP_JOB_PROCESSING &&
3422 	    !_cups_strcasecmp(job->dest, printer->name))
3423 	  break;
3424 
3425       if (job)
3426 	jobid = job->id;
3427       else
3428       {
3429        /*
3430         * No, try stopped jobs...
3431 	*/
3432 
3433 	for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
3434 	     job;
3435 	     job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
3436 	  if (job->state_value == IPP_JOB_STOPPED &&
3437 	      !_cups_strcasecmp(job->dest, printer->name))
3438 	    break;
3439 
3440 	if (job)
3441 	  jobid = job->id;
3442 	else
3443 	{
3444 	  send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s."),
3445 			  printer->name);
3446 	  return;
3447 	}
3448       }
3449     }
3450   }
3451   else
3452   {
3453    /*
3454     * Got a job URI; parse it to get the job ID...
3455     */
3456 
3457     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
3458                     sizeof(scheme), username, sizeof(username), host,
3459 		    sizeof(host), &port, resource, sizeof(resource));
3460 
3461     if (strncmp(resource, "/jobs/", 6))
3462     {
3463      /*
3464       * Not a valid URI!
3465       */
3466 
3467       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
3468                       uri->values[0].string.text);
3469       return;
3470     }
3471 
3472     jobid = atoi(resource + 6);
3473   }
3474 
3475  /*
3476   * Look for the "purge-job" attribute...
3477   */
3478 
3479   if ((attr = ippFindAttribute(con->request, "purge-job",
3480                                IPP_TAG_BOOLEAN)) != NULL)
3481     purge = attr->values[0].boolean ? CUPSD_JOB_PURGE : CUPSD_JOB_DEFAULT;
3482   else
3483     purge = CUPSD_JOB_DEFAULT;
3484 
3485  /*
3486   * See if the job exists...
3487   */
3488 
3489   if ((job = cupsdFindJob(jobid)) == NULL)
3490   {
3491    /*
3492     * Nope - return a "not found" error...
3493     */
3494 
3495     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
3496     return;
3497   }
3498 
3499  /*
3500   * See if the job is owned by the requesting user...
3501   */
3502 
3503   if (!validate_user(job, con, job->username, username, sizeof(username)))
3504   {
3505     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3506                     cupsdFindDest(job->dest));
3507     return;
3508   }
3509 
3510  /*
3511   * See if the job is already completed, canceled, or aborted; if so,
3512   * we can't cancel...
3513   */
3514 
3515   if (job->state_value >= IPP_JOB_CANCELED && purge != CUPSD_JOB_PURGE)
3516   {
3517     switch (job->state_value)
3518     {
3519       case IPP_JOB_CANCELED :
3520 	  send_ipp_status(con, IPP_NOT_POSSIBLE,
3521                 	  _("Job #%d is already canceled - can\'t cancel."),
3522 			  jobid);
3523           break;
3524 
3525       case IPP_JOB_ABORTED :
3526 	  send_ipp_status(con, IPP_NOT_POSSIBLE,
3527                 	  _("Job #%d is already aborted - can\'t cancel."),
3528 			  jobid);
3529           break;
3530 
3531       default :
3532 	  send_ipp_status(con, IPP_NOT_POSSIBLE,
3533                 	  _("Job #%d is already completed - can\'t cancel."),
3534 			  jobid);
3535           break;
3536     }
3537 
3538     return;
3539   }
3540 
3541  /*
3542   * Cancel the job and return...
3543   */
3544 
3545   cupsdSetJobState(job, IPP_JOB_CANCELED, purge,
3546                    purge == CUPSD_JOB_PURGE ? "Job purged by \"%s\"" :
3547 		                              "Job canceled by \"%s\"",
3548 		   username);
3549   cupsdCheckJobs();
3550 
3551   if (purge == CUPSD_JOB_PURGE)
3552     cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
3553 		    username);
3554   else
3555     cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
3556 		    username);
3557 
3558   con->response->request.status.status_code = IPP_OK;
3559 }
3560 
3561 
3562 /*
3563  * 'cancel_subscription()' - Cancel a subscription.
3564  */
3565 
3566 static void
cancel_subscription(cupsd_client_t * con,int sub_id)3567 cancel_subscription(
3568     cupsd_client_t *con,		/* I - Client connection */
3569     int            sub_id)		/* I - Subscription ID */
3570 {
3571   http_status_t		status;		/* Policy status */
3572   cupsd_subscription_t	*sub;		/* Subscription */
3573 
3574 
3575   cupsdLogMessage(CUPSD_LOG_DEBUG2,
3576                   "cancel_subscription(con=%p[%d], sub_id=%d)",
3577                   (void *)con, con->number, sub_id);
3578 
3579  /*
3580   * Is the subscription ID valid?
3581   */
3582 
3583   if ((sub = cupsdFindSubscription(sub_id)) == NULL)
3584   {
3585    /*
3586     * Bad subscription ID...
3587     */
3588 
3589     send_ipp_status(con, IPP_NOT_FOUND,
3590                     _("Subscription #%d does not exist."), sub_id);
3591     return;
3592   }
3593 
3594  /*
3595   * Check policy...
3596   */
3597 
3598   if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
3599                                              DefaultPolicyPtr,
3600                                  con, sub->owner)) != HTTP_OK)
3601   {
3602     send_http_error(con, status, sub->dest);
3603     return;
3604   }
3605 
3606  /*
3607   * Cancel the subscription...
3608   */
3609 
3610   cupsdDeleteSubscription(sub, 1);
3611 
3612   con->response->request.status.status_code = IPP_OK;
3613 }
3614 
3615 
3616 /*
3617  * 'check_rss_recipient()' - Check that we do not have a duplicate RSS feed URI.
3618  */
3619 
3620 static int				/* O - 1 if OK, 0 if not */
check_rss_recipient(const char * recipient)3621 check_rss_recipient(
3622     const char *recipient)		/* I - Recipient URI */
3623 {
3624   cupsd_subscription_t	*sub;		/* Current subscription */
3625 
3626 
3627   for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
3628        sub;
3629        sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
3630     if (sub->recipient)
3631     {
3632      /*
3633       * Compare the URIs up to the first ?...
3634       */
3635 
3636       const char *r1, *r2;
3637 
3638       for (r1 = recipient, r2 = sub->recipient;
3639            *r1 == *r2 && *r1 && *r1 != '?' && *r2 && *r2 != '?';
3640 	   r1 ++, r2 ++);
3641 
3642       if (*r1 == *r2)
3643         return (0);
3644     }
3645 
3646   return (1);
3647 }
3648 
3649 
3650 /*
3651  * 'check_quotas()' - Check quotas for a printer and user.
3652  */
3653 
3654 static int				/* O - 1 if OK, 0 if forbidden,
3655 					       -1 if limit reached */
check_quotas(cupsd_client_t * con,cupsd_printer_t * p)3656 check_quotas(cupsd_client_t  *con,	/* I - Client connection */
3657              cupsd_printer_t *p)	/* I - Printer or class */
3658 {
3659   char		username[33],		/* Username */
3660 		*name;			/* Current user name */
3661   cupsd_quota_t	*q;			/* Quota data */
3662 #ifdef HAVE_MBR_UID_TO_UUID
3663  /*
3664   * Use Apple membership APIs which require that all names represent
3665   * valid user account or group records accessible by the server.
3666   */
3667 
3668   uuid_t	usr_uuid;		/* UUID for job requesting user  */
3669   uuid_t	usr2_uuid;		/* UUID for ACL user name entry  */
3670   uuid_t	grp_uuid;		/* UUID for ACL group name entry */
3671   int		mbr_err;		/* Error from membership function */
3672   int		is_member;		/* Is this user a member? */
3673 #else
3674  /*
3675   * Use standard POSIX APIs for checking users and groups...
3676   */
3677 
3678   struct passwd	*pw;			/* User password data */
3679 #endif /* HAVE_MBR_UID_TO_UUID */
3680 
3681 
3682   cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
3683                   (void *)con, con->number, (void *)p, p->name);
3684 
3685  /*
3686   * Figure out who is printing...
3687   */
3688 
3689   strlcpy(username, get_username(con), sizeof(username));
3690 
3691   if ((name = strchr(username, '@')) != NULL)
3692     *name = '\0';			/* Strip @REALM */
3693 
3694  /*
3695   * Check global active job limits for printers and users...
3696   */
3697 
3698   if (MaxJobsPerPrinter)
3699   {
3700    /*
3701     * Check if there are too many pending jobs on this printer...
3702     */
3703 
3704     if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
3705     {
3706       cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
3707                       p->name);
3708       return (-1);
3709     }
3710   }
3711 
3712   if (MaxJobsPerUser)
3713   {
3714    /*
3715     * Check if there are too many pending jobs for this user...
3716     */
3717 
3718     if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
3719     {
3720       cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
3721                       username);
3722       return (-1);
3723     }
3724   }
3725 
3726  /*
3727   * Check against users...
3728   */
3729 
3730   if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
3731     return (1);
3732 
3733   if (cupsArrayCount(p->users))
3734   {
3735 #ifdef HAVE_MBR_UID_TO_UUID
3736    /*
3737     * Get UUID for job requesting user...
3738     */
3739 
3740     if (mbr_user_name_to_uuid((char *)username, usr_uuid))
3741     {
3742      /*
3743       * Unknown user...
3744       */
3745 
3746       cupsdLogMessage(CUPSD_LOG_DEBUG,
3747 		      "check_quotas: UUID lookup failed for user \"%s\"",
3748 		      username);
3749       cupsdLogMessage(CUPSD_LOG_INFO,
3750 		      "Denying user \"%s\" access to printer \"%s\" "
3751 		      "(unknown user)...",
3752 		      username, p->name);
3753       return (0);
3754     }
3755 #else
3756    /*
3757     * Get UID and GID of requesting user...
3758     */
3759 
3760     pw = getpwnam(username);
3761     endpwent();
3762 #endif /* HAVE_MBR_UID_TO_UUID */
3763 
3764     for (name = (char *)cupsArrayFirst(p->users);
3765          name;
3766 	 name = (char *)cupsArrayNext(p->users))
3767       if (name[0] == '@')
3768       {
3769        /*
3770         * Check group membership...
3771 	*/
3772 
3773 #ifdef HAVE_MBR_UID_TO_UUID
3774         if (name[1] == '#')
3775 	{
3776 	  if (uuid_parse(name + 2, grp_uuid))
3777 	    uuid_clear(grp_uuid);
3778 	}
3779 	else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
3780 	{
3781 	 /*
3782 	  * Invalid ACL entries are ignored for matching; just record a
3783 	  * warning in the log...
3784 	  */
3785 
3786 	  cupsdLogMessage(CUPSD_LOG_DEBUG,
3787 	                  "check_quotas: UUID lookup failed for ACL entry "
3788 			  "\"%s\" (err=%d)", name, mbr_err);
3789 	  cupsdLogMessage(CUPSD_LOG_WARN,
3790 	                  "Access control entry \"%s\" not a valid group name; "
3791 			  "entry ignored", name);
3792 	}
3793 
3794 	if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
3795 					    &is_member)) != 0)
3796 	{
3797 	 /*
3798 	  * At this point, there should be no errors, but check anyways...
3799 	  */
3800 
3801 	  cupsdLogMessage(CUPSD_LOG_DEBUG,
3802 			  "check_quotas: group \"%s\" membership check "
3803 			  "failed (err=%d)", name + 1, mbr_err);
3804 	  is_member = 0;
3805 	}
3806 
3807        /*
3808 	* Stop if we found a match...
3809 	*/
3810 
3811 	if (is_member)
3812 	  break;
3813 
3814 #else
3815         if (cupsdCheckGroup(username, pw, name + 1))
3816 	  break;
3817 #endif /* HAVE_MBR_UID_TO_UUID */
3818       }
3819 #ifdef HAVE_MBR_UID_TO_UUID
3820       else
3821       {
3822         if (name[0] == '#')
3823 	{
3824 	  if (uuid_parse(name + 1, usr2_uuid))
3825 	    uuid_clear(usr2_uuid);
3826         }
3827         else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
3828     	{
3829 	 /*
3830 	  * Invalid ACL entries are ignored for matching; just record a
3831 	  * warning in the log...
3832 	  */
3833 
3834           cupsdLogMessage(CUPSD_LOG_DEBUG,
3835 	                  "check_quotas: UUID lookup failed for ACL entry "
3836 			  "\"%s\" (err=%d)", name, mbr_err);
3837           cupsdLogMessage(CUPSD_LOG_WARN,
3838 	                  "Access control entry \"%s\" not a valid user name; "
3839 			  "entry ignored", name);
3840 	}
3841 
3842 	if (!uuid_compare(usr_uuid, usr2_uuid))
3843 	  break;
3844       }
3845 #else
3846       else if (!_cups_strcasecmp(username, name))
3847 	break;
3848 #endif /* HAVE_MBR_UID_TO_UUID */
3849 
3850     if ((name != NULL) == p->deny_users)
3851     {
3852       cupsdLogMessage(CUPSD_LOG_INFO,
3853                       "Denying user \"%s\" access to printer \"%s\"...",
3854         	      username, p->name);
3855       return (0);
3856     }
3857   }
3858 
3859  /*
3860   * Check quotas...
3861   */
3862 
3863   if (p->k_limit || p->page_limit)
3864   {
3865     if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
3866     {
3867       cupsdLogMessage(CUPSD_LOG_ERROR,
3868                       "Unable to allocate quota data for user \"%s\"",
3869                       username);
3870       return (-1);
3871     }
3872 
3873     if ((q->k_count >= p->k_limit && p->k_limit) ||
3874         (q->page_count >= p->page_limit && p->page_limit))
3875     {
3876       cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
3877                       username);
3878       return (-1);
3879     }
3880   }
3881 
3882  /*
3883   * If we have gotten this far, we're done!
3884   */
3885 
3886   return (1);
3887 }
3888 
3889 
3890 /*
3891  * 'close_job()' - Close a multi-file job.
3892  */
3893 
3894 static void
close_job(cupsd_client_t * con,ipp_attribute_t * uri)3895 close_job(cupsd_client_t  *con,		/* I - Client connection */
3896           ipp_attribute_t *uri)		/* I - Printer URI */
3897 {
3898   cupsd_job_t		*job;		/* Job */
3899   ipp_attribute_t	*attr;		/* Attribute */
3900   char			job_uri[HTTP_MAX_URI],
3901 					/* Job URI */
3902 			username[256];	/* User name */
3903 
3904 
3905   cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", (void *)con,
3906                   con->number, uri->values[0].string.text);
3907 
3908  /*
3909   * See if we have a job URI or a printer URI...
3910   */
3911 
3912   if (strcmp(uri->name, "printer-uri"))
3913   {
3914    /*
3915     * job-uri is not supported by Close-Job!
3916     */
3917 
3918     send_ipp_status(con, IPP_BAD_REQUEST,
3919 		    _("Close-Job doesn't support the job-uri attribute."));
3920     return;
3921   }
3922 
3923  /*
3924   * Got a printer URI; see if we also have a job-id attribute...
3925   */
3926 
3927   if ((attr = ippFindAttribute(con->request, "job-id",
3928 			       IPP_TAG_INTEGER)) == NULL)
3929   {
3930     send_ipp_status(con, IPP_BAD_REQUEST,
3931 		    _("Got a printer-uri attribute but no job-id."));
3932     return;
3933   }
3934 
3935   if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
3936   {
3937    /*
3938     * Nope - return a "not found" error...
3939     */
3940 
3941     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
3942                     attr->values[0].integer);
3943     return;
3944   }
3945 
3946  /*
3947   * See if the job is owned by the requesting user...
3948   */
3949 
3950   if (!validate_user(job, con, job->username, username, sizeof(username)))
3951   {
3952     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
3953                     cupsdFindDest(job->dest));
3954     return;
3955   }
3956 
3957  /*
3958   * Add any ending sheet...
3959   */
3960 
3961   if (cupsdTimeoutJob(job))
3962     return;
3963 
3964   if (job->state_value == IPP_JOB_STOPPED)
3965   {
3966     job->state->values[0].integer = IPP_JOB_PENDING;
3967     job->state_value              = IPP_JOB_PENDING;
3968   }
3969   else if (job->state_value == IPP_JOB_HELD)
3970   {
3971     if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
3972 				 IPP_TAG_KEYWORD)) == NULL)
3973       attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3974 
3975     if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
3976     {
3977       job->state->values[0].integer = IPP_JOB_PENDING;
3978       job->state_value              = IPP_JOB_PENDING;
3979     }
3980   }
3981 
3982   job->dirty = 1;
3983   cupsdMarkDirty(CUPSD_DIRTY_JOBS);
3984 
3985  /*
3986   * Fill in the response info...
3987   */
3988 
3989   httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
3990                    con->clientname, con->clientport, "/jobs/%d", job->id);
3991   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
3992                job_uri);
3993 
3994   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3995 
3996   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
3997 
3998   con->response->request.status.status_code = IPP_OK;
3999 
4000  /*
4001   * Start the job if necessary...
4002   */
4003 
4004   cupsdCheckJobs();
4005 }
4006 
4007 
4008 /*
4009  * 'copy_attrs()' - Copy attributes from one request to another.
4010  */
4011 
4012 static void
copy_attrs(ipp_t * to,ipp_t * from,cups_array_t * ra,ipp_tag_t group,int quickcopy,cups_array_t * exclude)4013 copy_attrs(ipp_t        *to,		/* I - Destination request */
4014            ipp_t        *from,		/* I - Source request */
4015            cups_array_t *ra,		/* I - Requested attributes */
4016 	   ipp_tag_t    group,		/* I - Group to copy */
4017 	   int          quickcopy,	/* I - Do a quick copy? */
4018 	   cups_array_t *exclude)	/* I - Attributes to exclude? */
4019 {
4020   ipp_attribute_t	*fromattr;	/* Source attribute */
4021 
4022 
4023   cupsdLogMessage(CUPSD_LOG_DEBUG2,
4024                   "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
4025 		  (void *)to, (void *)from, (void *)ra, group, quickcopy);
4026 
4027   if (!to || !from)
4028     return;
4029 
4030   for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
4031   {
4032    /*
4033     * Filter attributes as needed...
4034     */
4035 
4036     if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
4037          fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
4038       continue;
4039 
4040     if (!strcmp(fromattr->name, "document-password") ||
4041         !strcmp(fromattr->name, "job-authorization-uri") ||
4042         !strcmp(fromattr->name, "job-password") ||
4043         !strcmp(fromattr->name, "job-password-encryption") ||
4044         !strcmp(fromattr->name, "job-printer-uri"))
4045       continue;
4046 
4047     if (exclude &&
4048         (cupsArrayFind(exclude, fromattr->name) ||
4049 	 cupsArrayFind(exclude, "all")))
4050     {
4051      /*
4052       * We need to exclude this attribute for security reasons; we require the
4053       * job-id attribute regardless of the security settings for IPP
4054       * conformance.
4055       *
4056       * The job-printer-uri attribute is handled by copy_job_attrs().
4057       *
4058       * Subscription attribute security is handled by copy_subscription_attrs().
4059       */
4060 
4061       if (strcmp(fromattr->name, "job-id"))
4062         continue;
4063     }
4064 
4065     if (!ra || cupsArrayFind(ra, fromattr->name))
4066     {
4067      /*
4068       * Don't send collection attributes by default to IPP/1.x clients
4069       * since many do not support collections.  Also don't send
4070       * media-col-database unless specifically requested by the client.
4071       */
4072 
4073       if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
4074           !ra &&
4075 	  (to->request.status.version[0] == 1 ||
4076 	   !strcmp(fromattr->name, "media-col-database")))
4077 	continue;
4078 
4079       ippCopyAttribute(to, fromattr, quickcopy);
4080     }
4081   }
4082 }
4083 
4084 
4085 /*
4086  * 'copy_banner()' - Copy a banner file to the requests directory for the
4087  *                   specified job.
4088  */
4089 
4090 static int				/* O - Size of banner file in kbytes */
copy_banner(cupsd_client_t * con,cupsd_job_t * job,const char * name)4091 copy_banner(cupsd_client_t *con,	/* I - Client connection */
4092             cupsd_job_t    *job,	/* I - Job information */
4093             const char     *name)	/* I - Name of banner */
4094 {
4095   int		i;			/* Looping var */
4096   int		kbytes;			/* Size of banner file in kbytes */
4097   char		filename[1024];		/* Job filename */
4098   cupsd_banner_t *banner;		/* Pointer to banner */
4099   cups_file_t	*in;			/* Input file */
4100   cups_file_t	*out;			/* Output file */
4101   int		ch;			/* Character from file */
4102   char		attrname[255],		/* Name of attribute */
4103 		*s;			/* Pointer into name */
4104   ipp_attribute_t *attr;		/* Attribute */
4105 
4106 
4107   cupsdLogMessage(CUPSD_LOG_DEBUG2,
4108                   "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
4109                   (void *)con, con ? con->number : -1, (void *)job, job->id,
4110 		  name ? name : "(null)");
4111 
4112  /*
4113   * Find the banner; return if not found or "none"...
4114   */
4115 
4116   if (!name || !strcmp(name, "none") ||
4117       (banner = cupsdFindBanner(name)) == NULL)
4118     return (0);
4119 
4120  /*
4121   * Open the banner and job files...
4122   */
4123 
4124   if (add_file(con, job, banner->filetype, 0))
4125     return (-1);
4126 
4127   snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4128            job->num_files);
4129   if ((out = cupsFileOpen(filename, "w")) == NULL)
4130   {
4131     cupsdLogMessage(CUPSD_LOG_ERROR,
4132                     "Unable to create banner job file %s - %s",
4133                     filename, strerror(errno));
4134     job->num_files --;
4135     return (0);
4136   }
4137 
4138   fchmod(cupsFileNumber(out), 0640);
4139   fchown(cupsFileNumber(out), RunUser, Group);
4140 
4141  /*
4142   * Try the localized banner file under the subdirectory...
4143   */
4144 
4145   strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
4146           sizeof(attrname));
4147   if (strlen(attrname) > 2 && attrname[2] == '-')
4148   {
4149    /*
4150     * Convert ll-cc to ll_CC...
4151     */
4152 
4153     attrname[2] = '_';
4154     attrname[3] = (char)toupper(attrname[3] & 255);
4155     attrname[4] = (char)toupper(attrname[4] & 255);
4156   }
4157 
4158   snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4159            attrname, name);
4160 
4161   if (access(filename, 0) && strlen(attrname) > 2)
4162   {
4163    /*
4164     * Wasn't able to find "ll_CC" locale file; try the non-national
4165     * localization banner directory.
4166     */
4167 
4168     attrname[2] = '\0';
4169 
4170     snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
4171              attrname, name);
4172   }
4173 
4174   if (access(filename, 0))
4175   {
4176    /*
4177     * Use the non-localized banner file.
4178     */
4179 
4180     snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
4181   }
4182 
4183   if ((in = cupsFileOpen(filename, "r")) == NULL)
4184   {
4185     cupsFileClose(out);
4186     unlink(filename);
4187     cupsdLogMessage(CUPSD_LOG_ERROR,
4188                     "Unable to open banner template file %s - %s",
4189                     filename, strerror(errno));
4190     job->num_files --;
4191     return (0);
4192   }
4193 
4194  /*
4195   * Parse the file to the end...
4196   */
4197 
4198   while ((ch = cupsFileGetChar(in)) != EOF)
4199     if (ch == '{')
4200     {
4201      /*
4202       * Get an attribute name...
4203       */
4204 
4205       for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
4206         if (!isalpha(ch & 255) && ch != '-' && ch != '?')
4207           break;
4208 	else if (s < (attrname + sizeof(attrname) - 1))
4209           *s++ = (char)ch;
4210 	else
4211 	  break;
4212 
4213       *s = '\0';
4214 
4215       if (ch != '}')
4216       {
4217        /*
4218         * Ignore { followed by stuff that is not an attribute name...
4219 	*/
4220 
4221         cupsFilePrintf(out, "{%s%c", attrname, ch);
4222 	continue;
4223       }
4224 
4225      /*
4226       * See if it is defined...
4227       */
4228 
4229       if (attrname[0] == '?')
4230         s = attrname + 1;
4231       else
4232         s = attrname;
4233 
4234       if (!strcmp(s, "printer-name"))
4235       {
4236         cupsFilePuts(out, job->dest);
4237 	continue;
4238       }
4239       else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
4240       {
4241        /*
4242         * See if we have a leading question mark...
4243 	*/
4244 
4245 	if (attrname[0] != '?')
4246 	{
4247 	 /*
4248           * Nope, write to file as-is; probably a PostScript procedure...
4249 	  */
4250 
4251 	  cupsFilePrintf(out, "{%s}", attrname);
4252         }
4253 
4254         continue;
4255       }
4256 
4257      /*
4258       * Output value(s)...
4259       */
4260 
4261       for (i = 0; i < attr->num_values; i ++)
4262       {
4263 	if (i)
4264 	  cupsFilePutChar(out, ',');
4265 
4266 	switch (attr->value_tag)
4267 	{
4268 	  case IPP_TAG_INTEGER :
4269 	  case IPP_TAG_ENUM :
4270 	      if (!strncmp(s, "time-at-", 8))
4271 	      {
4272 	        struct timeval tv;	/* Time value */
4273 
4274 		tv.tv_sec  = attr->values[i].integer;
4275 		tv.tv_usec = 0;
4276 
4277 	        cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
4278 	      }
4279 	      else
4280 	        cupsFilePrintf(out, "%d", attr->values[i].integer);
4281 	      break;
4282 
4283 	  case IPP_TAG_BOOLEAN :
4284 	      cupsFilePrintf(out, "%d", attr->values[i].boolean);
4285 	      break;
4286 
4287 	  case IPP_TAG_NOVALUE :
4288 	      cupsFilePuts(out, "novalue");
4289 	      break;
4290 
4291 	  case IPP_TAG_RANGE :
4292 	      cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
4293 		      attr->values[i].range.upper);
4294 	      break;
4295 
4296 	  case IPP_TAG_RESOLUTION :
4297 	      cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
4298 		      attr->values[i].resolution.yres,
4299 		      attr->values[i].resolution.units == IPP_RES_PER_INCH ?
4300 			  "dpi" : "dpcm");
4301 	      break;
4302 
4303 	  case IPP_TAG_URI :
4304           case IPP_TAG_STRING :
4305 	  case IPP_TAG_TEXT :
4306 	  case IPP_TAG_NAME :
4307 	  case IPP_TAG_KEYWORD :
4308 	  case IPP_TAG_CHARSET :
4309 	  case IPP_TAG_LANGUAGE :
4310 	      if (!_cups_strcasecmp(banner->filetype->type, "postscript"))
4311 	      {
4312 	       /*
4313 	        * Need to quote strings for PS banners...
4314 		*/
4315 
4316 	        const char *p;
4317 
4318 		for (p = attr->values[i].string.text; *p; p ++)
4319 		{
4320 		  if (*p == '(' || *p == ')' || *p == '\\')
4321 		  {
4322 		    cupsFilePutChar(out, '\\');
4323 		    cupsFilePutChar(out, *p);
4324 		  }
4325 		  else if (*p < 32 || *p > 126)
4326 		    cupsFilePrintf(out, "\\%03o", *p & 255);
4327 		  else
4328 		    cupsFilePutChar(out, *p);
4329 		}
4330 	      }
4331 	      else
4332 		cupsFilePuts(out, attr->values[i].string.text);
4333 	      break;
4334 
4335           default :
4336 	      break; /* anti-compiler-warning-code */
4337 	}
4338       }
4339     }
4340     else if (ch == '\\')	/* Quoted char */
4341     {
4342       ch = cupsFileGetChar(in);
4343 
4344       if (ch != '{')		/* Only do special handling for \{ */
4345         cupsFilePutChar(out, '\\');
4346 
4347       cupsFilePutChar(out, ch);
4348     }
4349     else
4350       cupsFilePutChar(out, ch);
4351 
4352   cupsFileClose(in);
4353 
4354   kbytes = (cupsFileTell(out) + 1023) / 1024;
4355 
4356   job->koctets += kbytes;
4357 
4358   if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
4359     attr->values[0].integer += kbytes;
4360 
4361   cupsFileClose(out);
4362 
4363   return (kbytes);
4364 }
4365 
4366 
4367 /*
4368  * 'copy_file()' - Copy a PPD file...
4369  */
4370 
4371 static int				/* O - 0 = success, -1 = error */
copy_file(const char * from,const char * to,mode_t mode)4372 copy_file(const char *from,		/* I - Source file */
4373           const char *to,		/* I - Destination file */
4374 	  mode_t     mode)		/* I - Permissions */
4375 {
4376   cups_file_t	*src,			/* Source file */
4377 		*dst;			/* Destination file */
4378   int		bytes;			/* Bytes to read/write */
4379   char		buffer[2048];		/* Copy buffer */
4380 
4381 
4382   cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
4383 
4384  /*
4385   * Open the source and destination file for a copy...
4386   */
4387 
4388   if ((src = cupsFileOpen(from, "rb")) == NULL)
4389     return (-1);
4390 
4391   if ((dst = cupsdCreateConfFile(to, mode)) == NULL)
4392   {
4393     cupsFileClose(src);
4394     return (-1);
4395   }
4396 
4397  /*
4398   * Copy the source file to the destination...
4399   */
4400 
4401   while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
4402     if (cupsFileWrite(dst, buffer, (size_t)bytes) < bytes)
4403     {
4404       cupsFileClose(src);
4405       cupsFileClose(dst);
4406       return (-1);
4407     }
4408 
4409  /*
4410   * Close both files and return...
4411   */
4412 
4413   cupsFileClose(src);
4414 
4415   return (cupsdCloseCreatedConfFile(dst, to));
4416 }
4417 
4418 
4419 /*
4420  * 'copy_model()' - Copy a PPD model file, substituting default values
4421  *                  as needed...
4422  */
4423 
4424 static int				/* O - 0 = success, -1 = error */
copy_model(cupsd_client_t * con,const char * from,const char * to)4425 copy_model(cupsd_client_t *con,		/* I - Client connection */
4426            const char     *from,	/* I - Source file */
4427            const char     *to)		/* I - Destination file */
4428 {
4429   fd_set	input;			/* select() input set */
4430   struct timeval timeout;		/* select() timeout */
4431   int		maxfd;			/* Max file descriptor for select() */
4432   char		tempfile[1024];		/* Temporary PPD file */
4433   int		tempfd;			/* Temporary PPD file descriptor */
4434   int		temppid;		/* Process ID of cups-driverd */
4435   int		temppipe[2];		/* Temporary pipes */
4436   char		*argv[4],		/* Command-line arguments */
4437 		*envp[MAX_ENV];		/* Environment */
4438   cups_file_t	*src,			/* Source file */
4439 		*dst;			/* Destination file */
4440   ppd_file_t	*ppd;			/* PPD file */
4441   int		bytes,			/* Bytes from pipe */
4442 		total;			/* Total bytes from pipe */
4443   char		buffer[2048];		/* Copy buffer */
4444   int		i;			/* Looping var */
4445   char		option[PPD_MAX_NAME],	/* Option name */
4446 		choice[PPD_MAX_NAME];	/* Choice name */
4447   ppd_size_t	*size;			/* Default size */
4448   int		num_defaults;		/* Number of default options */
4449   cups_option_t	*defaults;		/* Default options */
4450   char		cups_protocol[PPD_MAX_LINE];
4451 					/* cupsProtocol attribute */
4452 
4453 
4454   cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_model(con=%p, from=\"%s\", to=\"%s\")", (void *)con, from, to);
4455 
4456  /*
4457   * Run cups-driverd to get the PPD file...
4458   */
4459 
4460   argv[0] = "cups-driverd";
4461   argv[1] = "cat";
4462   argv[2] = (char *)from;
4463   argv[3] = NULL;
4464 
4465   cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4466 
4467   snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
4468   snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->number);
4469   if ((tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
4470     return (-1);
4471   if (cupsdOpenPipe(temppipe))
4472   {
4473     close(tempfd);
4474     unlink(tempfile);
4475 
4476     return (-1);
4477   }
4478 
4479   cupsdLogMessage(CUPSD_LOG_DEBUG,
4480                   "copy_model: Running \"cups-driverd cat %s\"...", from);
4481 
4482   if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
4483                          -1, -1, 0, DefaultProfile, NULL, &temppid))
4484   {
4485     send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to run cups-driverd: %s"), strerror(errno));
4486     close(tempfd);
4487     unlink(tempfile);
4488 
4489     return (-1);
4490   }
4491 
4492   close(temppipe[1]);
4493 
4494  /*
4495   * Wait up to 30 seconds for the PPD file to be copied...
4496   */
4497 
4498   total = 0;
4499 
4500   if (temppipe[0] > CGIPipes[0])
4501     maxfd = temppipe[0] + 1;
4502   else
4503     maxfd = CGIPipes[0] + 1;
4504 
4505   for (;;)
4506   {
4507    /*
4508     * See if we have data ready...
4509     */
4510 
4511     FD_ZERO(&input);
4512     FD_SET(temppipe[0], &input);
4513     FD_SET(CGIPipes[0], &input);
4514 
4515     timeout.tv_sec  = 30;
4516     timeout.tv_usec = 0;
4517 
4518     if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
4519     {
4520       if (errno == EINTR)
4521         continue;
4522       else
4523         break;
4524     }
4525     else if (i == 0)
4526     {
4527      /*
4528       * We have timed out...
4529       */
4530 
4531       break;
4532     }
4533 
4534     if (FD_ISSET(temppipe[0], &input))
4535     {
4536      /*
4537       * Read the PPD file from the pipe, and write it to the PPD file.
4538       */
4539 
4540       if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
4541       {
4542 	if (write(tempfd, buffer, (size_t)bytes) < bytes)
4543           break;
4544 
4545 	total += bytes;
4546       }
4547       else
4548 	break;
4549     }
4550 
4551     if (FD_ISSET(CGIPipes[0], &input))
4552       cupsdUpdateCGI();
4553   }
4554 
4555   close(temppipe[0]);
4556   close(tempfd);
4557 
4558   if (!total)
4559   {
4560    /*
4561     * No data from cups-deviced...
4562     */
4563 
4564     cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file");
4565     send_ipp_status(con, IPP_INTERNAL_ERROR, _("cups-driverd failed to get PPD file - see error_log for details."));
4566     unlink(tempfile);
4567     return (-1);
4568   }
4569 
4570  /*
4571   * Open the source file for a copy...
4572   */
4573 
4574   if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
4575   {
4576     unlink(tempfile);
4577     return (-1);
4578   }
4579 
4580  /*
4581   * Read the source file and see what page sizes are supported...
4582   */
4583 
4584   if ((ppd = _ppdOpen(src, _PPD_LOCALIZATION_NONE)) == NULL)
4585   {
4586     cupsFileClose(src);
4587     unlink(tempfile);
4588     return (-1);
4589   }
4590 
4591  /*
4592   * Open the destination (if possible) and set the default options...
4593   */
4594 
4595   num_defaults     = 0;
4596   defaults         = NULL;
4597   cups_protocol[0] = '\0';
4598 
4599   if ((dst = cupsFileOpen(to, "rb")) != NULL)
4600   {
4601    /*
4602     * Read all of the default lines from the old PPD...
4603     */
4604 
4605     while (cupsFileGets(dst, buffer, sizeof(buffer)))
4606       if (!strncmp(buffer, "*Default", 8))
4607       {
4608        /*
4609 	* Add the default option...
4610 	*/
4611 
4612         if (!ppd_parse_line(buffer, option, sizeof(option),
4613 	                    choice, sizeof(choice)))
4614         {
4615 	  ppd_option_t	*ppdo;		/* PPD option */
4616 
4617 
4618          /*
4619 	  * Only add the default if the default hasn't already been
4620 	  * set and the choice exists in the new PPD...
4621 	  */
4622 
4623 	  if (!cupsGetOption(option, num_defaults, defaults) &&
4624 	      (ppdo = ppdFindOption(ppd, option)) != NULL &&
4625 	      ppdFindChoice(ppdo, choice))
4626             num_defaults = cupsAddOption(option, choice, num_defaults,
4627 	                                 &defaults);
4628         }
4629       }
4630       else if (!strncmp(buffer, "*cupsProtocol:", 14))
4631         strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
4632 
4633     cupsFileClose(dst);
4634   }
4635   else if ((size = ppdPageSize(ppd, DefaultPaperSize)) != NULL)
4636   {
4637    /*
4638     * Add the default media sizes...
4639     */
4640 
4641     num_defaults = cupsAddOption("PageSize", size->name,
4642                                  num_defaults, &defaults);
4643     num_defaults = cupsAddOption("PageRegion", size->name,
4644                                  num_defaults, &defaults);
4645     num_defaults = cupsAddOption("PaperDimension", size->name,
4646                                  num_defaults, &defaults);
4647     num_defaults = cupsAddOption("ImageableArea", size->name,
4648                                  num_defaults, &defaults);
4649   }
4650 
4651   ppdClose(ppd);
4652 
4653  /*
4654   * Open the destination file for a copy...
4655   */
4656 
4657   if ((dst = cupsdCreateConfFile(to, ConfigFilePerm)) == NULL)
4658   {
4659     send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to save PPD file: %s"), strerror(errno));
4660     cupsFreeOptions(num_defaults, defaults);
4661     cupsFileClose(src);
4662     unlink(tempfile);
4663     return (-1);
4664   }
4665 
4666  /*
4667   * Copy the source file to the destination...
4668   */
4669 
4670   cupsFileRewind(src);
4671 
4672   while (cupsFileGets(src, buffer, sizeof(buffer)))
4673   {
4674     if (!strncmp(buffer, "*Default", 8))
4675     {
4676      /*
4677       * Check for an previous default option choice...
4678       */
4679 
4680       if (!ppd_parse_line(buffer, option, sizeof(option),
4681 	                  choice, sizeof(choice)))
4682       {
4683         const char	*val;		/* Default option value */
4684 
4685 
4686         if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
4687 	{
4688 	 /*
4689 	  * Substitute the previous choice...
4690 	  */
4691 
4692 	  snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
4693 	}
4694       }
4695     }
4696 
4697     cupsFilePrintf(dst, "%s\n", buffer);
4698   }
4699 
4700   if (cups_protocol[0])
4701     cupsFilePrintf(dst, "%s\n", cups_protocol);
4702 
4703   cupsFreeOptions(num_defaults, defaults);
4704 
4705  /*
4706   * Close both files and return...
4707   */
4708 
4709   cupsFileClose(src);
4710 
4711   unlink(tempfile);
4712 
4713   if (cupsdCloseCreatedConfFile(dst, to))
4714   {
4715     send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to commit PPD file: %s"), strerror(errno));
4716     return (-1);
4717   }
4718   else
4719   {
4720     return (0);
4721   }
4722 }
4723 
4724 
4725 /*
4726  * 'copy_job_attrs()' - Copy job attributes.
4727  */
4728 
4729 static void
copy_job_attrs(cupsd_client_t * con,cupsd_job_t * job,cups_array_t * ra,cups_array_t * exclude)4730 copy_job_attrs(cupsd_client_t *con,	/* I - Client connection */
4731 	       cupsd_job_t    *job,	/* I - Job */
4732 	       cups_array_t   *ra,	/* I - Requested attributes array */
4733 	       cups_array_t   *exclude)	/* I - Private attributes array */
4734 {
4735   char	job_uri[HTTP_MAX_URI];		/* Job URI */
4736 
4737 
4738  /*
4739   * Send the requested attributes for each job...
4740   */
4741 
4742   if (!cupsArrayFind(exclude, "all"))
4743   {
4744     if ((!exclude || !cupsArrayFind(exclude, "number-of-documents")) &&
4745         (!ra || cupsArrayFind(ra, "number-of-documents")))
4746       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4747 		    "number-of-documents", job->num_files);
4748 
4749     if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
4750         (!ra || cupsArrayFind(ra, "job-media-progress")))
4751       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4752 		    "job-media-progress", job->progress);
4753 
4754     if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
4755         (!ra || cupsArrayFind(ra, "job-more-info")))
4756     {
4757       httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "http",
4758                        NULL, con->clientname, con->clientport, "/jobs/%d",
4759 		       job->id);
4760       ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4761 		   "job-more-info", NULL, job_uri);
4762     }
4763 
4764     if (job->state_value > IPP_JOB_PROCESSING &&
4765 	(!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
4766         (!ra || cupsArrayFind(ra, "job-preserved")))
4767       ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
4768 		    job->num_files > 0);
4769 
4770     if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
4771         (!ra || cupsArrayFind(ra, "job-printer-up-time")))
4772       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
4773 		    "job-printer-up-time", time(NULL));
4774   }
4775 
4776   if (!ra || cupsArrayFind(ra, "job-printer-uri"))
4777   {
4778     httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
4779 		     con->clientname, con->clientport,
4780 		     (job->dtype & CUPS_PRINTER_CLASS) ? "/classes/%s" :
4781 		                                         "/printers/%s",
4782 		     job->dest);
4783     ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4784         	 "job-printer-uri", NULL, job_uri);
4785   }
4786 
4787   if (!ra || cupsArrayFind(ra, "job-uri"))
4788   {
4789     httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
4790 		     con->clientname, con->clientport, "/jobs/%d",
4791 		     job->id);
4792     ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
4793         	 "job-uri", NULL, job_uri);
4794   }
4795 
4796   if (job->attrs)
4797   {
4798     copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
4799   }
4800   else
4801   {
4802    /*
4803     * Generate attributes from the job structure...
4804     */
4805 
4806     if (job->completed_time && (!ra || cupsArrayFind(ra, "date-time-at-completed")))
4807       ippAddDate(con->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed_time));
4808 
4809     if (job->creation_time && (!ra || cupsArrayFind(ra, "date-time-at-creation")))
4810       ippAddDate(con->response, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(job->creation_time));
4811 
4812     if (!ra || cupsArrayFind(ra, "job-id"))
4813       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4814 
4815     if (!ra || cupsArrayFind(ra, "job-k-octets"))
4816       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", job->koctets);
4817 
4818     if (job->name && (!ra || cupsArrayFind(ra, "job-name")))
4819       ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL, job->name);
4820 
4821     if (job->username && (!ra || cupsArrayFind(ra, "job-originating-user-name")))
4822       ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
4823 
4824     if (!ra || cupsArrayFind(ra, "job-state"))
4825       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
4826 
4827     if (!ra || cupsArrayFind(ra, "job-state-reasons"))
4828     {
4829       switch (job->state_value)
4830       {
4831         default : /* Should never get here for processing, pending, held, or stopped jobs since they don't get unloaded... */
4832 	    break;
4833         case IPP_JSTATE_ABORTED :
4834 	    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-aborted-by-system");
4835 	    break;
4836         case IPP_JSTATE_CANCELED :
4837 	    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user");
4838 	    break;
4839         case IPP_JSTATE_COMPLETED :
4840 	    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-completed-successfully");
4841 	    break;
4842       }
4843     }
4844 
4845     if (job->completed_time && (!ra || cupsArrayFind(ra, "time-at-completed")))
4846       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-completed", (int)job->completed_time);
4847 
4848     if (job->creation_time && (!ra || cupsArrayFind(ra, "time-at-creation")))
4849       ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)job->creation_time);
4850   }
4851 }
4852 
4853 
4854 /*
4855  * 'copy_printer_attrs()' - Copy printer attributes.
4856  */
4857 
4858 static void
copy_printer_attrs(cupsd_client_t * con,cupsd_printer_t * printer,cups_array_t * ra)4859 copy_printer_attrs(
4860     cupsd_client_t  *con,		/* I - Client connection */
4861     cupsd_printer_t *printer,		/* I - Printer */
4862     cups_array_t    *ra)		/* I - Requested attributes array */
4863 {
4864   char		uri[HTTP_MAX_URI];	/* URI value */
4865   time_t	curtime;		/* Current time */
4866   int		i;			/* Looping var */
4867   int		is_encrypted = httpIsEncrypted(con->http);
4868 					/* Is the connection encrypted? */
4869 
4870 
4871  /*
4872   * Copy the printer attributes to the response using requested-attributes
4873   * and document-format attributes that may be provided by the client.
4874   */
4875 
4876   _cupsRWLockRead(&printer->lock);
4877 
4878   curtime = time(NULL);
4879 
4880   if (!ra || cupsArrayFind(ra, "marker-change-time"))
4881     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "marker-change-time", printer->marker_time);
4882 
4883   if (printer->num_printers > 0 && (!ra || cupsArrayFind(ra, "member-uris")))
4884   {
4885     ipp_attribute_t	*member_uris;	/* member-uris attribute */
4886     cupsd_printer_t	*p2;		/* Printer in class */
4887     ipp_attribute_t	*p2_uri;	/* printer-uri-supported for class printer */
4888 
4889 
4890     if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris", printer->num_printers, NULL, NULL)) != NULL)
4891     {
4892       for (i = 0; i < printer->num_printers; i ++)
4893       {
4894         p2 = printer->printers[i];
4895 
4896         if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported", IPP_TAG_URI)) != NULL)
4897         {
4898           member_uris->values[i].string.text = _cupsStrAlloc(p2_uri->values[0].string.text);
4899         }
4900         else
4901 	{
4902 	  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), is_encrypted ? "ipps" : "ipp", NULL, con->clientname, con->clientport, (p2->type & CUPS_PRINTER_CLASS) ? "/classes/%s" : "/printers/%s", p2->name);
4903 	  member_uris->values[i].string.text = _cupsStrAlloc(uri);
4904         }
4905       }
4906     }
4907   }
4908 
4909   if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert")))
4910     ippAddOctetString(con->response, IPP_TAG_PRINTER, "printer-alert", printer->alert, (int)strlen(printer->alert));
4911 
4912   if (printer->alert_description && (!ra || cupsArrayFind(ra, "printer-alert-description")))
4913     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-alert-description", NULL, printer->alert_description);
4914 
4915   if (!ra || cupsArrayFind(ra, "printer-config-change-date-time"))
4916     ippAddDate(con->response, IPP_TAG_PRINTER, "printer-config-change-date-time", ippTimeToDate(printer->config_time));
4917 
4918   if (!ra || cupsArrayFind(ra, "printer-config-change-time"))
4919     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-config-change-time", printer->config_time);
4920 
4921   if (!ra || cupsArrayFind(ra, "printer-current-time"))
4922     ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(curtime));
4923 
4924 #ifdef HAVE_DNSSD
4925   if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
4926   {
4927     if (printer->reg_name)
4928       ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-dns-sd-name", NULL, printer->reg_name);
4929     else
4930       ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE, "printer-dns-sd-name", 0);
4931   }
4932 #endif /* HAVE_DNSSD */
4933 
4934   if (!ra || cupsArrayFind(ra, "printer-error-policy"))
4935     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-error-policy", NULL, printer->error_policy);
4936 
4937   if (!ra || cupsArrayFind(ra, "printer-error-policy-supported"))
4938   {
4939     static const char * const errors[] =/* printer-error-policy-supported values */
4940     {
4941       "abort-job",
4942       "retry-current-job",
4943       "retry-job",
4944       "stop-printer"
4945     };
4946 
4947     if (printer->type & CUPS_PRINTER_CLASS)
4948       ippAddString(con->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "printer-error-policy-supported", NULL, "retry-current-job");
4949     else
4950       ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "printer-error-policy-supported", sizeof(errors) / sizeof(errors[0]), NULL, errors);
4951   }
4952 
4953   if (!ra || cupsArrayFind(ra, "printer-icons"))
4954   {
4955     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), is_encrypted ? "https" : "http", NULL, con->clientname, con->clientport, "/icons/%s.png", printer->name);
4956     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons", NULL, uri);
4957     cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-icons=\"%s\"", uri);
4958   }
4959 
4960   if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
4961     ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
4962 
4963   if (!ra || cupsArrayFind(ra, "printer-is-shared"))
4964     ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared", (char)printer->shared);
4965 
4966   if (!ra || cupsArrayFind(ra, "printer-is-temporary"))
4967     ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-temporary", (char)printer->temporary);
4968 
4969   if (!ra || cupsArrayFind(ra, "printer-more-info"))
4970   {
4971     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), is_encrypted ? "https" : "http", NULL, con->clientname, con->clientport, (printer->type & CUPS_PRINTER_CLASS) ? "/classes/%s" : "/printers/%s", printer->name);
4972     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-more-info", NULL, uri);
4973   }
4974 
4975   if (!ra || cupsArrayFind(ra, "printer-op-policy"))
4976     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-op-policy", NULL, printer->op_policy);
4977 
4978   if (!ra || cupsArrayFind(ra, "printer-state"))
4979     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
4980 
4981   if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
4982     ippAddDate(con->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
4983 
4984   if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
4985     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-state-change-time", printer->state_time);
4986 
4987   if (!ra || cupsArrayFind(ra, "printer-state-message"))
4988     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-state-message", NULL, printer->state_message);
4989 
4990   if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
4991     add_printer_state_reasons(con, printer);
4992 
4993   if (!ra || cupsArrayFind(ra, "printer-strings-uri"))
4994   {
4995     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), is_encrypted ? "https" : "http", NULL, con->clientname, con->clientport, "/strings/%s.strings", printer->name);
4996     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-strings-uri", NULL, uri);
4997     cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-strings-uri=\"%s\"", uri);
4998   }
4999 
5000   if (!ra || cupsArrayFind(ra, "printer-type"))
5001   {
5002     cups_ptype_t type;			/* printer-type value */
5003 
5004    /*
5005     * Add the CUPS-specific printer-type attribute...
5006     */
5007 
5008     type = printer->type;
5009 
5010     if (printer == DefaultPrinter)
5011       type |= CUPS_PRINTER_DEFAULT;
5012 
5013     if (!printer->accepting)
5014       type |= CUPS_PRINTER_REJECTING;
5015 
5016     if (!printer->shared)
5017       type |= CUPS_PRINTER_NOT_SHARED;
5018 
5019     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type", (int)type);
5020   }
5021 
5022   if (!ra || cupsArrayFind(ra, "printer-up-time"))
5023     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "printer-up-time", curtime);
5024 
5025   if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
5026   {
5027     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), is_encrypted ? "ipps" : "ipp", NULL, con->clientname, con->clientport, (printer->type & CUPS_PRINTER_CLASS) ? "/classes/%s" : "/printers/%s", printer->name);
5028     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
5029     cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"", uri);
5030   }
5031 
5032   if (!ra || cupsArrayFind(ra, "queued-job-count"))
5033     add_queued_job_count(con, printer);
5034 
5035   if (!ra || cupsArrayFind(ra, "uri-security-supported"))
5036     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "uri-security-supported", NULL, is_encrypted ? "tls" : "none");
5037 
5038   copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0, NULL);
5039   if (printer->ppd_attrs)
5040     copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0, NULL);
5041   copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY, NULL);
5042 
5043   _cupsRWUnlock(&printer->lock);
5044 }
5045 
5046 
5047 /*
5048  * 'copy_subscription_attrs()' - Copy subscription attributes.
5049  */
5050 
5051 static void
copy_subscription_attrs(cupsd_client_t * con,cupsd_subscription_t * sub,cups_array_t * ra,cups_array_t * exclude)5052 copy_subscription_attrs(
5053     cupsd_client_t       *con,		/* I - Client connection */
5054     cupsd_subscription_t *sub,		/* I - Subscription */
5055     cups_array_t         *ra,		/* I - Requested attributes array */
5056     cups_array_t         *exclude)	/* I - Private attributes array */
5057 {
5058   ipp_attribute_t	*attr;		/* Current attribute */
5059   char			printer_uri[HTTP_MAX_URI];
5060 					/* Printer URI */
5061   int			count;		/* Number of events */
5062   unsigned		mask;		/* Current event mask */
5063   const char		*name;		/* Current event name */
5064 
5065 
5066   cupsdLogMessage(CUPSD_LOG_DEBUG2,
5067                   "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
5068 		  (void *)con, (void *)sub, (void *)ra, (void *)exclude);
5069 
5070  /*
5071   * Copy the subscription attributes to the response using the
5072   * requested-attributes attribute that may be provided by the client.
5073   */
5074 
5075   if (!exclude || !cupsArrayFind(exclude, "all"))
5076   {
5077     if ((!exclude || !cupsArrayFind(exclude, "notify-events")) &&
5078         (!ra || cupsArrayFind(ra, "notify-events")))
5079     {
5080       cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_subscription_attrs: notify-events");
5081 
5082       if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
5083       {
5084        /*
5085 	* Simple event list...
5086 	*/
5087 
5088 	ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events", NULL, name);
5089       }
5090       else
5091       {
5092        /*
5093 	* Complex event list...
5094 	*/
5095 
5096 	for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5097 	  if (sub->mask & mask)
5098 	    count ++;
5099 
5100 	attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events", count, NULL, NULL);
5101 
5102 	for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
5103 	  if (sub->mask & mask)
5104 	  {
5105 	    attr->values[count].string.text = (char *)cupsdEventName((cupsd_eventmask_t)mask);
5106 
5107 	    count ++;
5108 	  }
5109       }
5110     }
5111 
5112     if ((!exclude || !cupsArrayFind(exclude, "notify-lease-duration")) &&
5113         (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration"))))
5114       ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5115 		    "notify-lease-duration", sub->lease);
5116 
5117     if ((!exclude || !cupsArrayFind(exclude, "notify-recipient-uri")) &&
5118         (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri"))))
5119       ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5120 		   "notify-recipient-uri", NULL, sub->recipient);
5121     else if ((!exclude || !cupsArrayFind(exclude, "notify-pull-method")) &&
5122              (!ra || cupsArrayFind(ra, "notify-pull-method")))
5123       ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
5124 		   "notify-pull-method", NULL, "ippget");
5125 
5126     if ((!exclude || !cupsArrayFind(exclude, "notify-subscriber-user-name")) &&
5127         (!ra || cupsArrayFind(ra, "notify-subscriber-user-name")))
5128       ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
5129 		   "notify-subscriber-user-name", NULL, sub->owner);
5130 
5131     if ((!exclude || !cupsArrayFind(exclude, "notify-time-interval")) &&
5132         (!ra || cupsArrayFind(ra, "notify-time-interval")))
5133       ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5134 		    "notify-time-interval", sub->interval);
5135 
5136     if (sub->user_data_len > 0 &&
5137 	(!exclude || !cupsArrayFind(exclude, "notify-user-data")) &&
5138         (!ra || cupsArrayFind(ra, "notify-user-data")))
5139       ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
5140 			sub->user_data, sub->user_data_len);
5141   }
5142 
5143   if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
5144     ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5145                   "notify-job-id", sub->job->id);
5146 
5147   if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
5148   {
5149     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
5150                      "ipp", NULL, con->clientname, con->clientport,
5151 		     "/printers/%s", sub->dest->name);
5152     ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5153         	 "notify-printer-uri", NULL, printer_uri);
5154   }
5155 
5156   if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
5157     ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5158                   "notify-subscription-id", sub->id);
5159 }
5160 
5161 
5162 /*
5163  * 'create_job()' - Print a file to a printer or class.
5164  */
5165 
5166 static void
create_job(cupsd_client_t * con,ipp_attribute_t * uri)5167 create_job(cupsd_client_t  *con,	/* I - Client connection */
5168 	   ipp_attribute_t *uri)	/* I - Printer URI */
5169 {
5170   int			i;		/* Looping var */
5171   cupsd_printer_t	*printer;	/* Printer */
5172   cupsd_job_t		*job;		/* New job */
5173   static const char * const forbidden_attrs[] =
5174   {					/* List of forbidden attributes */
5175     "compression",
5176     "document-format",
5177     "document-name",
5178     "document-natural-language"
5179   };
5180 
5181 
5182   cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", (void *)con,
5183                   con->number, uri->values[0].string.text);
5184 
5185  /*
5186   * Is the destination valid?
5187   */
5188 
5189   if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
5190   {
5191    /*
5192     * Bad URI...
5193     */
5194 
5195     send_ipp_status(con, IPP_NOT_FOUND,
5196                     _("The printer or class does not exist."));
5197     return;
5198   }
5199 
5200  /*
5201   * Check for invalid Create-Job attributes and log a warning or error depending
5202   * on whether cupsd is running in "strict conformance" mode...
5203   */
5204 
5205   for (i = 0;
5206        i < (int)(sizeof(forbidden_attrs) / sizeof(forbidden_attrs[0]));
5207        i ++)
5208     if (ippFindAttribute(con->request, forbidden_attrs[i], IPP_TAG_ZERO))
5209     {
5210       if (StrictConformance)
5211       {
5212 	send_ipp_status(con, IPP_BAD_REQUEST,
5213 			_("The '%s' operation attribute cannot be supplied in a "
5214 			  "Create-Job request."), forbidden_attrs[i]);
5215 	return;
5216       }
5217 
5218       cupsdLogMessage(CUPSD_LOG_WARN,
5219                       "Unexpected '%s' operation attribute in a Create-Job "
5220                       "request.", forbidden_attrs[i]);
5221     }
5222 
5223  /*
5224   * Create the job object...
5225   */
5226 
5227   if ((job = add_job(con, printer, NULL)) == NULL)
5228     return;
5229 
5230   job->pending_timeout = 1;
5231 
5232  /*
5233   * Save and log the job...
5234   */
5235 
5236   cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
5237 	      job->dest, job->username);
5238 }
5239 
5240 
5241 /*
5242  * 'create_local_bg_thread()' - Background thread for creating a local print queue.
5243  */
5244 
5245 static void *				/* O - Exit status */
create_local_bg_thread(cupsd_client_t * con)5246 create_local_bg_thread(
5247     cupsd_client_t *con)		/* I - Client */
5248 {
5249   cupsd_printer_t *printer = con->bg_printer;
5250 					/* Printer */
5251   cups_file_t	*from,			/* Source file */
5252 		*to;			/* Destination file */
5253   char		device_uri[1024],	/* Device URI */
5254 		fromppd[1024],		/* Source PPD */
5255 		toppd[1024],		/* Destination PPD */
5256 		scheme[32],		/* URI scheme */
5257 		userpass[256],		/* User:pass */
5258 		host[256],		/* Hostname */
5259 		resource[1024],		/* Resource path */
5260 		uri[1024],		/* Resolved URI, if needed */
5261 		line[1024];		/* Line from PPD */
5262   int		port;			/* Port number */
5263   http_encryption_t encryption;		/* Type of encryption to use */
5264   http_t	*http;			/* Connection to printer */
5265   ipp_t		*request,		/* Request to printer */
5266 		*response = NULL;	/* Response from printer */
5267   ipp_attribute_t *attr;		/* Attribute in response */
5268   ipp_status_t	status;			/* Status code */
5269   static const char * const pattrs[] =	/* Printer attributes we need */
5270   {
5271     "all",
5272     "media-col-database"
5273   };
5274 
5275 
5276  /*
5277   * Try connecting to the printer...
5278   */
5279 
5280   _cupsRWLockRead(&printer->lock);
5281   strlcpy(device_uri, printer->device_uri, sizeof(device_uri));
5282   _cupsRWUnlock(&printer->lock);
5283 
5284   cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Generating PPD file from \"%s\"...", printer->name, device_uri);
5285 
5286   if (strstr(device_uri, "._tcp"))
5287   {
5288     cupsdLogMessage(CUPSD_LOG_DEBUG2, "%s: Resolving mDNS URI \"%s\".", printer->name, device_uri);
5289 
5290     if (!_httpResolveURI(device_uri, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL))
5291     {
5292       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Couldn't resolve mDNS URI \"%s\".", printer->name, device_uri);
5293 
5294       /* Force printer to timeout and be deleted */
5295       _cupsRWLockWrite(&printer->lock);
5296       printer->state_time = 0;
5297       printer->temporary = 1;
5298       _cupsRWUnlock(&printer->lock);
5299 
5300       send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Couldn't resolve mDNS URI \"%s\"."), printer->device_uri);
5301       goto finish_response;
5302     }
5303 
5304     _cupsRWLockWrite(&printer->lock);
5305     cupsdSetString(&printer->device_uri, uri);
5306     _cupsRWUnlock(&printer->lock);
5307 
5308     strlcpy(device_uri, uri, sizeof(device_uri));
5309   }
5310 
5311   if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
5312   {
5313     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Bad device URI \"%s\".", printer->name, device_uri);
5314 
5315     /* Force printer to timeout and be deleted */
5316     _cupsRWLockWrite(&printer->lock);
5317     printer->state_time = 0;
5318     printer->temporary = 1;
5319     _cupsRWUnlock(&printer->lock);
5320 
5321     send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Bad device URI \"%s\"."), device_uri);
5322     goto finish_response;
5323   }
5324 
5325   if (!strcmp(scheme, "ipps") || port == 443)
5326     encryption = HTTP_ENCRYPTION_ALWAYS;
5327   else
5328     encryption = HTTP_ENCRYPTION_IF_REQUESTED;
5329 
5330   if ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
5331   {
5332     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to connect to %s:%d: %s", printer->name, host, port, cupsLastErrorString());
5333 
5334     /* Force printer to timeout and be deleted */
5335     _cupsRWLockWrite(&printer->lock);
5336     printer->state_time = 0;
5337     printer->temporary = 1;
5338     _cupsRWUnlock(&printer->lock);
5339 
5340     send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to connect to %s:%d: %s"), host, port, cupsLastErrorString());
5341     goto finish_response;
5342   }
5343 
5344  /*
5345   * Query the printer for its capabilities...
5346   */
5347 
5348   cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Connected to %s:%d, sending Get-Printer-Attributes request...", printer->name, host, port);
5349 
5350   request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
5351   ippSetVersion(request, 2, 0);
5352   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri);
5353   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
5354 
5355   response = cupsDoRequest(http, request, resource);
5356   status   = cupsLastError();
5357 
5358   cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Get-Printer-Attributes returned %s (%s)", printer->name, ippErrorString(cupsLastError()), cupsLastErrorString());
5359 
5360   if (status == IPP_STATUS_ERROR_BAD_REQUEST || status == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
5361   {
5362    /*
5363     * Try request using IPP/1.1, in case we are talking to an old CUPS server or
5364     * printer...
5365     */
5366 
5367     ippDelete(response);
5368 
5369     cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Re-sending Get-Printer-Attributes request using IPP/1.1...", printer->name);
5370 
5371     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
5372     ippSetVersion(request, 1, 1);
5373     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, device_uri);
5374     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "all");
5375 
5376     response = cupsDoRequest(http, request, resource);
5377 
5378     cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: IPP/1.1 Get-Printer-Attributes returned %s (%s)", printer->name, ippErrorString(cupsLastError()), cupsLastErrorString());
5379   }
5380 
5381  /*
5382   * If we did not succeed to obtain the "media-col-database" attribute
5383   * try to get it separately
5384   */
5385 
5386   if (ippFindAttribute(response, "media-col-database", IPP_TAG_ZERO) ==
5387       NULL)
5388   {
5389     ipp_t *response2;
5390 
5391     cupsdLogMessage(CUPSD_LOG_DEBUG,
5392 		    "Polling \"media-col-database\" attribute separately.");
5393     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
5394     ippSetVersion(request, 2, 0);
5395     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
5396 		 "printer-uri", NULL, device_uri);
5397     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
5398 		 "requested-attributes", NULL, "media-col-database");
5399     response2 = cupsDoRequest(http, request, resource);
5400     //ipp_status = cupsLastError();
5401     if (response2)
5402     {
5403       if ((attr = ippFindAttribute(response2, "media-col-database",
5404 				   IPP_TAG_ZERO)) != NULL)
5405       {
5406 	cupsdLogMessage(CUPSD_LOG_WARN, "The printer %s does not support requests"
5407 			" with attribute set \"all,media-col-database\", which breaks IPP"
5408 			" conformance (RFC 8011, 4.2.5.1 \"requested-attributes\")"
5409 			" - report the issue to your printer manufacturer", printer->name);
5410        /*
5411 	* Copy "media-col-database" attribute into the original
5412 	* IPP response
5413 	*/
5414 
5415 	cupsdLogMessage(CUPSD_LOG_DEBUG,
5416 			"\"media-col-database\" attribute found.");
5417 	ippCopyAttribute(response, attr, 0);
5418       }
5419       ippDelete(response2);
5420     }
5421   }
5422 
5423   // Validate response from printer...
5424   if (!ippValidateAttributes(response))
5425   {
5426     /* Force printer to timeout and be deleted */
5427     _cupsRWLockWrite(&printer->lock);
5428     printer->state_time = 0;
5429     printer->temporary = 1;
5430     _cupsRWUnlock(&printer->lock);
5431 
5432     send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Printer returned invalid data: %s"), cupsLastErrorString());
5433     goto finish_response;
5434   }
5435 
5436   // TODO: Grab printer icon file...
5437   httpClose(http);
5438 
5439  /*
5440   * Write the PPD for the queue...
5441   */
5442 
5443   if (_ppdCreateFromIPP(fromppd, sizeof(fromppd), response))
5444   {
5445     _cupsRWLockWrite(&printer->lock);
5446 
5447     if ((!printer->info || !*(printer->info)) && (attr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL)
5448       cupsdSetString(&printer->info, ippGetString(attr, 0, NULL));
5449 
5450     if ((!printer->location || !*(printer->location)) && (attr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL)
5451       cupsdSetString(&printer->location, ippGetString(attr, 0, NULL));
5452 
5453     if ((!printer->geo_location || !*(printer->geo_location)) && (attr = ippFindAttribute(response, "printer-geo-location", IPP_TAG_URI)) != NULL)
5454       cupsdSetString(&printer->geo_location, ippGetString(attr, 0, NULL));
5455 
5456     _cupsRWUnlock(&printer->lock);
5457 
5458     if ((from = cupsFileOpen(fromppd, "r")) == NULL)
5459     {
5460       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to read generated PPD: %s", printer->name, strerror(errno));
5461 
5462       /* Force printer to timeout and be deleted */
5463       _cupsRWLockWrite(&printer->lock);
5464       printer->state_time = 0;
5465       printer->temporary = 1;
5466       _cupsRWUnlock(&printer->lock);
5467 
5468       send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to read generated PPD: %s"), strerror(errno));
5469       goto finish_response;
5470     }
5471 
5472     snprintf(toppd, sizeof(toppd), "%s/ppd/%s.ppd", ServerRoot, printer->name);
5473     if ((to = cupsdCreateConfFile(toppd, ConfigFilePerm)) == NULL)
5474     {
5475       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to create PPD for printer: %s", printer->name, strerror(errno));
5476       cupsFileClose(from);
5477 
5478       /* Force printer to timeout and be deleted */
5479       _cupsRWLockWrite(&printer->lock);
5480       printer->state_time = 0;
5481       printer->temporary = 1;
5482       _cupsRWUnlock(&printer->lock);
5483 
5484       send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to create PPD for printer: %s"), strerror(errno));
5485       goto finish_response;
5486     }
5487 
5488     while (cupsFileGets(from, line, sizeof(line)))
5489       cupsFilePrintf(to, "%s\n", line);
5490 
5491     cupsFileClose(from);
5492     if (!cupsdCloseCreatedConfFile(to, toppd))
5493     {
5494       _cupsRWLockWrite(&printer->lock);
5495 
5496       printer->config_time = time(NULL);
5497       printer->state       = IPP_PSTATE_IDLE;
5498       printer->accepting   = 1;
5499 
5500       _cupsRWUnlock(&printer->lock);
5501 
5502       cupsdSetPrinterAttrs(printer);
5503 
5504       cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL, "Printer \"%s\" is now available.", printer->name);
5505       cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" is now available.", printer->name);
5506     }
5507   }
5508   else
5509   {
5510     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: PPD creation failed: %s", printer->name, cupsLastErrorString());
5511 
5512     /* Force printer to timeout and be deleted */
5513     _cupsRWLockWrite(&printer->lock);
5514     printer->state_time = 0;
5515     printer->temporary = 1;
5516     _cupsRWUnlock(&printer->lock);
5517 
5518     send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to create PPD: %s"), cupsLastErrorString());
5519     goto finish_response;
5520   }
5521 
5522  /*
5523   * Respond to the client...
5524   */
5525 
5526   send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
5527 
5528   ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
5529   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
5530   add_printer_state_reasons(con, printer);
5531 
5532   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
5533   ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
5534 
5535   finish_response:
5536 
5537   ippDelete(response);
5538 
5539   send_response(con);
5540 
5541   con->bg_pending = 0;
5542 
5543   return (NULL);
5544 }
5545 
5546 
5547 /*
5548  * 'create_local_printer()' - Create a local (temporary) print queue.
5549  */
5550 
5551 static void
create_local_printer(cupsd_client_t * con)5552 create_local_printer(
5553     cupsd_client_t *con)		/* I - Client connection */
5554 {
5555   ipp_attribute_t *device_uri,		/* device-uri attribute */
5556 		*printer_geo_location,	/* printer-geo-location attribute */
5557 		*printer_info,		/* printer-info attribute */
5558 		*printer_location,	/* printer-location attribute */
5559 		*printer_name;		/* printer-name attribute */
5560   cupsd_printer_t *printer;		/* New printer */
5561   http_status_t	status;			/* Policy status */
5562   char		name[128],		/* Sanitized printer name */
5563 		*nameptr,		/* Pointer into name */
5564 		uri[1024];		/* printer-uri-supported value */
5565   const char	*ptr;			/* Pointer into attribute value */
5566   char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
5567 		userpass[HTTP_MAX_URI],	/* Username portion of URI */
5568 		host[HTTP_MAX_URI],	/* Host portion of URI */
5569 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
5570   int		port;			/* Port portion of URI */
5571 
5572 
5573  /*
5574   * Require local access to create a local printer...
5575   */
5576 
5577   if (!httpAddrLocalhost(httpGetAddress(con->http)))
5578   {
5579     send_ipp_status(con, IPP_STATUS_ERROR_FORBIDDEN, _("Only local users can create a local printer."));
5580     return;
5581   }
5582 
5583  /*
5584   * Check any other policy limits...
5585   */
5586 
5587   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5588   {
5589     send_http_error(con, status, NULL);
5590     return;
5591   }
5592 
5593  /*
5594   * Grab needed attributes...
5595   */
5596 
5597   if ((printer_name = ippFindAttribute(con->request, "printer-name", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(printer_name) != IPP_TAG_PRINTER || ippGetValueTag(printer_name) != IPP_TAG_NAME)
5598   {
5599     if (!printer_name)
5600       send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Missing required attribute \"%s\"."), "printer-name");
5601     else if (ippGetGroupTag(printer_name) != IPP_TAG_PRINTER)
5602       send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is in the wrong group."), "printer-name");
5603     else
5604       send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is the wrong value type."), "printer-name");
5605 
5606     return;
5607   }
5608 
5609   for (nameptr = name, ptr = ippGetString(printer_name, 0, NULL); *ptr && nameptr < (name + sizeof(name) - 1); ptr ++)
5610   {
5611    /*
5612     * Sanitize the printer name...
5613     */
5614 
5615     if (_cups_isalnum(*ptr))
5616       *nameptr++ = *ptr;
5617     else if (nameptr == name || nameptr[-1] != '_')
5618       *nameptr++ = '_';
5619   }
5620 
5621   *nameptr = '\0';
5622 
5623   if ((device_uri = ippFindAttribute(con->request, "device-uri", IPP_TAG_ZERO)) == NULL || ippGetGroupTag(device_uri) != IPP_TAG_PRINTER || ippGetValueTag(device_uri) != IPP_TAG_URI)
5624   {
5625     if (!device_uri)
5626       send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Missing required attribute \"%s\"."), "device-uri");
5627     else if (ippGetGroupTag(device_uri) != IPP_TAG_PRINTER)
5628       send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is in the wrong group."), "device-uri");
5629     else
5630       send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" is the wrong value type."), "device-uri");
5631 
5632     return;
5633   }
5634 
5635   ptr = ippGetString(device_uri, 0, NULL);
5636 
5637   if (!ptr || !ptr[0])
5638   {
5639     send_ipp_status(con, IPP_STATUS_ERROR_BAD_REQUEST, _("Attribute \"%s\" has empty value."), "device-uri");
5640 
5641     return;
5642   }
5643 
5644   printer_geo_location = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI);
5645   printer_info         = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT);
5646   printer_location     = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT);
5647 
5648  /*
5649   * See if the printer already exists...
5650   */
5651 
5652   if ((printer = cupsdFindDest(name)) != NULL)
5653   {
5654     printer->state_time = time(NULL);
5655     send_ipp_status(con, IPP_STATUS_OK, _("Printer \"%s\" already exists."), name);
5656     goto add_printer_attributes;
5657   }
5658 
5659   for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers); printer; printer = (cupsd_printer_t *)cupsArrayNext(Printers))
5660   {
5661     if (printer->device_uri && !strcmp(ptr, printer->device_uri))
5662     {
5663       printer->state_time = time(NULL);
5664       send_ipp_status(con, IPP_STATUS_OK, _("Printer \"%s\" already exists."), printer->name);
5665       goto add_printer_attributes;
5666     }
5667   }
5668 
5669  /*
5670   * Create the printer...
5671   */
5672 
5673   if ((printer = cupsdAddPrinter(name)) == NULL)
5674   {
5675     send_ipp_status(con, IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer."));
5676     return;
5677   }
5678 
5679   printer->shared    = 0;
5680   printer->temporary = 1;
5681 
5682  /*
5683   * Check device URI if it has the same hostname as we have, if so, replace
5684   * the hostname by localhost. This way we assure that local-only services
5685   * like ipp-usb or Printer Applications always work.
5686   *
5687   * When comparing our hostname with the one in the device URI,
5688   * consider names with or without trailing dot ('.') the same. Also
5689   * compare case-insensitively.
5690   */
5691 
5692 #ifdef HAVE_DNSSD
5693   if (DNSSDHostName)
5694     nameptr = DNSSDHostName;
5695   else
5696 #endif
5697   if (ServerName)
5698     nameptr = ServerName;
5699   else
5700     nameptr = NULL;
5701 
5702   if (nameptr)
5703   {
5704     size_t host_len,
5705         server_name_len;
5706 
5707     /* Get host name of device URI */
5708     httpSeparateURI(HTTP_URI_CODING_ALL, ptr,
5709 		    scheme, sizeof(scheme), userpass, sizeof(userpass), host,
5710 		    sizeof(host), &port, resource, sizeof(resource));
5711 
5712     /* Take trailing dot out of comparison */
5713     host_len = strlen(host);
5714     if (host_len > 1 && host[host_len - 1] == '.')
5715       host_len --;
5716 
5717     server_name_len = strlen(nameptr);
5718     if (server_name_len > 1 && nameptr[server_name_len - 1] == '.')
5719       server_name_len --;
5720 
5721    /*
5722     * If we have no DNSSDHostName but only a ServerName (if we are not
5723     * sharing printers, Browsing = Off) the ServerName has no ".local"
5724     * but the requested device URI has. Take this into account.
5725     */
5726 
5727     if (nameptr == ServerName && host_len >= 6 && (server_name_len < 6 || strcmp(nameptr + server_name_len - 6, ".local") != 0) && strcmp(host + host_len - 6, ".local") == 0)
5728       host_len -= 6;
5729 
5730     if (host_len == server_name_len && strncasecmp(host, nameptr, host_len) == 0)
5731       ptr = "localhost";
5732     else
5733       ptr = host;
5734 
5735     httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, userpass,
5736 		    ptr, port, resource);
5737     cupsdSetDeviceURI(printer, uri);
5738   }
5739   else
5740     cupsdSetDeviceURI(printer, ptr);
5741 
5742   if (printer_geo_location)
5743     cupsdSetString(&printer->geo_location, ippGetString(printer_geo_location, 0, NULL));
5744   if (printer_info)
5745     cupsdSetString(&printer->info, ippGetString(printer_info, 0, NULL));
5746   if (printer_location)
5747     cupsdSetString(&printer->location, ippGetString(printer_location, 0, NULL));
5748 
5749   cupsdSetPrinterAttrs(printer);
5750 
5751  /*
5752   * Run a background thread to create the PPD...
5753   */
5754 
5755   con->bg_pending = 1;
5756   con->bg_printer = printer;
5757 
5758   _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, con);
5759 
5760   return;
5761 
5762  /*
5763   * Return printer attributes...
5764   */
5765 
5766   add_printer_attributes:
5767 
5768   ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
5769   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
5770   add_printer_state_reasons(con, printer);
5771 
5772   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
5773   ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
5774 }
5775 
5776 
5777 /*
5778  * 'create_requested_array()' - Create an array for the requested-attributes.
5779  */
5780 
5781 static cups_array_t *			/* O - Array of attributes or NULL */
create_requested_array(ipp_t * request)5782 create_requested_array(ipp_t *request)	/* I - IPP request */
5783 {
5784   cups_array_t		*ra;		/* Requested attributes array */
5785 
5786 
5787  /*
5788   * Create the array for standard attributes...
5789   */
5790 
5791   ra = ippCreateRequestedArray(request);
5792 
5793  /*
5794   * Add CUPS defaults as needed...
5795   */
5796 
5797   if (cupsArrayFind(ra, "printer-defaults"))
5798   {
5799    /*
5800     * Include user-set defaults...
5801     */
5802 
5803     char	*name;			/* Option name */
5804 
5805     cupsArrayRemove(ra, "printer-defaults");
5806 
5807     for (name = (char *)cupsArrayFirst(CommonDefaults);
5808 	 name;
5809 	 name = (char *)cupsArrayNext(CommonDefaults))
5810       if (!cupsArrayFind(ra, name))
5811         cupsArrayAdd(ra, name);
5812   }
5813 
5814   return (ra);
5815 }
5816 
5817 
5818 /*
5819  * 'create_subscriptions()' - Create one or more notification subscriptions.
5820  */
5821 
5822 static void
create_subscriptions(cupsd_client_t * con,ipp_attribute_t * uri)5823 create_subscriptions(
5824     cupsd_client_t  *con,		/* I - Client connection */
5825     ipp_attribute_t *uri)		/* I - Printer URI */
5826 {
5827   http_status_t	status;			/* Policy status */
5828   int			i;		/* Looping var */
5829   ipp_attribute_t	*attr;		/* Current attribute */
5830   cups_ptype_t		dtype;		/* Destination type (printer/class) */
5831   char			scheme[HTTP_MAX_URI],
5832 					/* Scheme portion of URI */
5833 			userpass[HTTP_MAX_URI],
5834 					/* Username portion of URI */
5835 			host[HTTP_MAX_URI],
5836 					/* Host portion of URI */
5837 			resource[HTTP_MAX_URI];
5838 					/* Resource portion of URI */
5839   int			port;		/* Port portion of URI */
5840   cupsd_printer_t	*printer;	/* Printer/class */
5841   cupsd_job_t		*job;		/* Job */
5842   int			jobid;		/* Job ID */
5843   cupsd_subscription_t	*sub;		/* Subscription object */
5844   const char		*username,	/* requesting-user-name or
5845 					   authenticated username */
5846 			*recipient,	/* notify-recipient-uri */
5847 			*pullmethod;	/* notify-pull-method */
5848   ipp_attribute_t	*user_data;	/* notify-user-data */
5849   int			interval,	/* notify-time-interval */
5850 			lease;		/* notify-lease-duration */
5851   unsigned		mask;		/* notify-events */
5852   ipp_attribute_t	*notify_events,/* notify-events(-default) */
5853 			*notify_lease;	/* notify-lease-duration(-default) */
5854 
5855 
5856 #ifdef DEBUG
5857   for (attr = con->request->attrs; attr; attr = attr->next)
5858   {
5859     if (attr->group_tag != IPP_TAG_ZERO)
5860       cupsdLogMessage(CUPSD_LOG_DEBUG2, "g%04x v%04x %s", attr->group_tag,
5861                       attr->value_tag, attr->name);
5862     else
5863       cupsdLogMessage(CUPSD_LOG_DEBUG2, "----SEP----");
5864   }
5865 #endif /* DEBUG */
5866 
5867  /*
5868   * Is the destination valid?
5869   */
5870 
5871   cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", (void *)con, con->number, uri->values[0].string.text);
5872 
5873   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
5874                   sizeof(scheme), userpass, sizeof(userpass), host,
5875 		  sizeof(host), &port, resource, sizeof(resource));
5876 
5877   if (!strcmp(resource, "/"))
5878   {
5879     dtype   = (cups_ptype_t)0;
5880     printer = NULL;
5881   }
5882   else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
5883   {
5884     dtype   = (cups_ptype_t)0;
5885     printer = NULL;
5886   }
5887   else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
5888   {
5889     dtype   = CUPS_PRINTER_CLASS;
5890     printer = NULL;
5891   }
5892   else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
5893   {
5894    /*
5895     * Bad URI...
5896     */
5897 
5898     send_ipp_status(con, IPP_NOT_FOUND,
5899                     _("The printer or class does not exist."));
5900     return;
5901   }
5902 
5903  /*
5904   * Check policy...
5905   */
5906 
5907   if (printer)
5908   {
5909     if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
5910                                    NULL)) != HTTP_OK)
5911     {
5912       send_http_error(con, status, printer);
5913       return;
5914     }
5915   }
5916   else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
5917   {
5918     send_http_error(con, status, NULL);
5919     return;
5920   }
5921 
5922  /*
5923   * Get the user that is requesting the subscription...
5924   */
5925 
5926   username = get_username(con);
5927 
5928  /*
5929   * Find the first subscription group attribute; return if we have
5930   * none...
5931   */
5932 
5933   for (attr = con->request->attrs; attr; attr = attr->next)
5934     if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
5935       break;
5936 
5937   if (!attr)
5938   {
5939     send_ipp_status(con, IPP_BAD_REQUEST,
5940                     _("No subscription attributes in request."));
5941     return;
5942   }
5943 
5944  /*
5945   * Process the subscription attributes in the request...
5946   */
5947 
5948   con->response->request.status.status_code = IPP_BAD_REQUEST;
5949 
5950   while (attr)
5951   {
5952     recipient = NULL;
5953     pullmethod = NULL;
5954     user_data  = NULL;
5955     interval   = 0;
5956     lease      = DefaultLeaseDuration;
5957     jobid      = 0;
5958     mask       = CUPSD_EVENT_NONE;
5959 
5960     if (printer)
5961     {
5962       notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
5963                                        IPP_TAG_KEYWORD);
5964       notify_lease  = ippFindAttribute(printer->attrs,
5965                                        "notify-lease-duration-default",
5966                                        IPP_TAG_INTEGER);
5967 
5968       if (notify_lease)
5969         lease = notify_lease->values[0].integer;
5970     }
5971     else
5972     {
5973       notify_events = NULL;
5974       notify_lease  = NULL;
5975     }
5976 
5977     while (attr && attr->group_tag != IPP_TAG_ZERO)
5978     {
5979       if (!strcmp(attr->name, "notify-recipient-uri") &&
5980           attr->value_tag == IPP_TAG_URI)
5981       {
5982        /*
5983         * Validate the recipient scheme against the ServerBin/notifier
5984 	* directory...
5985 	*/
5986 
5987 	char	notifier[1024];		/* Notifier filename */
5988 
5989 
5990         recipient = attr->values[0].string.text;
5991 
5992 	if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
5993 	                    scheme, sizeof(scheme), userpass, sizeof(userpass),
5994 			    host, sizeof(host), &port,
5995 			    resource, sizeof(resource)) < HTTP_URI_OK)
5996         {
5997           send_ipp_status(con, IPP_NOT_POSSIBLE,
5998 	                  _("Bad notify-recipient-uri \"%s\"."), recipient);
5999 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6000 	                "notify-status-code", IPP_URI_SCHEME);
6001 	  return;
6002 	}
6003 
6004         snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
6005 	         scheme);
6006         if (access(notifier, X_OK) || !strcmp(scheme, ".") || !strcmp(scheme, ".."))
6007 	{
6008           send_ipp_status(con, IPP_NOT_POSSIBLE,
6009 	                  _("notify-recipient-uri URI \"%s\" uses unknown "
6010 			    "scheme."), recipient);
6011 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6012 	                "notify-status-code", IPP_URI_SCHEME);
6013 	  return;
6014 	}
6015 
6016         if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
6017 	{
6018           send_ipp_status(con, IPP_NOT_POSSIBLE,
6019 	                  _("notify-recipient-uri URI \"%s\" is already used."),
6020 			  recipient);
6021 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6022 	                "notify-status-code", IPP_ATTRIBUTES);
6023 	  return;
6024 	}
6025       }
6026       else if (!strcmp(attr->name, "notify-pull-method") &&
6027                attr->value_tag == IPP_TAG_KEYWORD)
6028       {
6029         pullmethod = attr->values[0].string.text;
6030 
6031         if (strcmp(pullmethod, "ippget"))
6032 	{
6033           send_ipp_status(con, IPP_NOT_POSSIBLE,
6034 	                  _("Bad notify-pull-method \"%s\"."), pullmethod);
6035 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
6036 	                "notify-status-code", IPP_ATTRIBUTES);
6037 	  return;
6038 	}
6039       }
6040       else if (!strcmp(attr->name, "notify-charset") &&
6041                attr->value_tag == IPP_TAG_CHARSET &&
6042 	       strcmp(attr->values[0].string.text, "us-ascii") &&
6043 	       strcmp(attr->values[0].string.text, "utf-8"))
6044       {
6045         send_ipp_status(con, IPP_CHARSET,
6046 	                _("Character set \"%s\" not supported."),
6047 			attr->values[0].string.text);
6048 	return;
6049       }
6050       else if (!strcmp(attr->name, "notify-natural-language") &&
6051                (attr->value_tag != IPP_TAG_LANGUAGE ||
6052 	        strcmp(attr->values[0].string.text, DefaultLanguage)))
6053       {
6054         send_ipp_status(con, IPP_CHARSET,
6055 	                _("Language \"%s\" not supported."),
6056 			attr->values[0].string.text);
6057 	return;
6058       }
6059       else if (!strcmp(attr->name, "notify-user-data") &&
6060                attr->value_tag == IPP_TAG_STRING)
6061       {
6062         if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
6063 	{
6064           send_ipp_status(con, IPP_REQUEST_VALUE,
6065 	                  _("The notify-user-data value is too large "
6066 			    "(%d > 63 octets)."),
6067 			  attr->values[0].unknown.length);
6068 	  return;
6069 	}
6070 
6071         user_data = attr;
6072       }
6073       else if (!strcmp(attr->name, "notify-events") &&
6074                attr->value_tag == IPP_TAG_KEYWORD)
6075         notify_events = attr;
6076       else if (!strcmp(attr->name, "notify-lease-duration") &&
6077                attr->value_tag == IPP_TAG_INTEGER)
6078         lease = attr->values[0].integer;
6079       else if (!strcmp(attr->name, "notify-time-interval") &&
6080                attr->value_tag == IPP_TAG_INTEGER)
6081         interval = attr->values[0].integer;
6082       else if (!strcmp(attr->name, "notify-job-id") &&
6083                attr->value_tag == IPP_TAG_INTEGER)
6084         jobid = attr->values[0].integer;
6085 
6086       attr = attr->next;
6087     }
6088 
6089     if (notify_events)
6090     {
6091       for (i = 0; i < notify_events->num_values; i ++)
6092 	mask |= cupsdEventValue(notify_events->values[i].string.text);
6093     }
6094 
6095     if (recipient)
6096     {
6097       cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
6098 
6099 
6100       if (!strncmp(recipient, "mailto:", 7) && user_data)
6101       {
6102         char	temp[64];		/* Temporary string */
6103 
6104 	memcpy(temp, user_data->values[0].unknown.data, (size_t)user_data->values[0].unknown.length);
6105 	temp[user_data->values[0].unknown.length] = '\0';
6106 
6107 	if (httpSeparateURI(HTTP_URI_CODING_ALL, temp, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_OK)
6108 	{
6109 	  send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad notify-user-data \"%s\"."), temp);
6110 	  ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES);
6111 	  return;
6112 	}
6113       }
6114     }
6115 
6116     if (pullmethod)
6117       cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
6118     cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
6119     cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
6120 
6121     if (!recipient && !pullmethod)
6122       break;
6123 
6124     if (mask == CUPSD_EVENT_NONE)
6125     {
6126       if (jobid)
6127         mask = CUPSD_EVENT_JOB_COMPLETED;
6128       else if (printer)
6129         mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
6130       else
6131       {
6132         send_ipp_status(con, IPP_BAD_REQUEST,
6133 	                _("notify-events not specified."));
6134 	return;
6135       }
6136     }
6137 
6138     if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
6139     {
6140       cupsdLogMessage(CUPSD_LOG_INFO,
6141                       "create_subscriptions: Limiting notify-lease-duration to "
6142 		      "%d seconds.",
6143 		      MaxLeaseDuration);
6144       lease = MaxLeaseDuration;
6145     }
6146 
6147     if (jobid)
6148     {
6149       if ((job = cupsdFindJob(jobid)) == NULL)
6150       {
6151 	send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
6152 	                jobid);
6153 	return;
6154       }
6155     }
6156     else
6157       job = NULL;
6158 
6159     if ((sub = cupsdAddSubscription(mask, printer, job, recipient, 0)) == NULL)
6160     {
6161       send_ipp_status(con, IPP_TOO_MANY_SUBSCRIPTIONS,
6162 		      _("There are too many subscriptions."));
6163       return;
6164     }
6165 
6166     if (job)
6167       cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
6168 		      sub->id, job->id);
6169     else if (printer)
6170       cupsdLogMessage(CUPSD_LOG_DEBUG,
6171                       "Added subscription #%d for printer \"%s\".",
6172 		      sub->id, printer->name);
6173     else
6174       cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
6175 		      sub->id);
6176 
6177     sub->interval = interval;
6178     sub->lease    = lease;
6179     sub->expire   = lease ? time(NULL) + lease : 0;
6180 
6181     cupsdSetString(&sub->owner, username);
6182 
6183     if (user_data)
6184     {
6185       sub->user_data_len = user_data->values[0].unknown.length;
6186       memcpy(sub->user_data, user_data->values[0].unknown.data,
6187              (size_t)sub->user_data_len);
6188     }
6189 
6190     ippAddSeparator(con->response);
6191     copy_subscription_attrs(con, sub, /*ra*/NULL, /*exclude*/NULL);
6192 
6193     con->response->request.status.status_code = IPP_OK;
6194 
6195     if (attr)
6196       attr = attr->next;
6197   }
6198 
6199   cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
6200 }
6201 
6202 
6203 /*
6204  * 'delete_printer()' - Remove a printer or class from the system.
6205  */
6206 
6207 static void
delete_printer(cupsd_client_t * con,ipp_attribute_t * uri)6208 delete_printer(cupsd_client_t  *con,	/* I - Client connection */
6209                ipp_attribute_t *uri)	/* I - URI of printer or class */
6210 {
6211   http_status_t	status;			/* Policy status */
6212   cups_ptype_t	dtype;			/* Destination type (printer/class) */
6213   cupsd_printer_t *printer;		/* Printer/class */
6214   char		filename[1024];		/* Script/PPD filename */
6215   int		temporary;		/* Temporary queue? */
6216 
6217 
6218   cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", (void *)con,
6219                   con->number, uri->values[0].string.text);
6220 
6221  /*
6222   * Do we have a valid URI?
6223   */
6224 
6225   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
6226   {
6227    /*
6228     * Bad URI...
6229     */
6230 
6231     send_ipp_status(con, IPP_NOT_FOUND,
6232                     _("The printer or class does not exist."));
6233     return;
6234   }
6235 
6236  /*
6237   * Check policy...
6238   */
6239 
6240   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6241   {
6242     send_http_error(con, status, NULL);
6243     return;
6244   }
6245 
6246  /*
6247   * Remove old jobs...
6248   */
6249 
6250   cupsdCancelJobs(printer->name, NULL, 1);
6251 
6252  /*
6253   * Remove old subscriptions and send a "deleted printer" event...
6254   */
6255 
6256   cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
6257                 "%s \"%s\" deleted by \"%s\".",
6258 		(dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
6259 		printer->name, get_username(con));
6260 
6261   cupsdExpireSubscriptions(printer, NULL);
6262 
6263  /*
6264   * Remove any old PPD or script files...
6265   */
6266 
6267   snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
6268            printer->name);
6269   unlink(filename);
6270   snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd.O", ServerRoot,
6271            printer->name);
6272   unlink(filename);
6273 
6274   snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
6275   unlink(filename);
6276 
6277   snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
6278   unlink(filename);
6279 
6280  /*
6281   * Unregister color profiles...
6282   */
6283 
6284   cupsdUnregisterColor(printer);
6285 
6286   temporary = printer->temporary;
6287 
6288   if (dtype & CUPS_PRINTER_CLASS)
6289   {
6290     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
6291                     printer->name, get_username(con));
6292 
6293     cupsdDeletePrinter(printer, 0);
6294     if (!temporary)
6295       cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
6296   }
6297   else
6298   {
6299     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
6300                     printer->name, get_username(con));
6301 
6302     if (cupsdDeletePrinter(printer, 0) && !temporary)
6303       cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
6304 
6305     if (!temporary)
6306       cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
6307   }
6308 
6309   if (!temporary)
6310     cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
6311 
6312  /*
6313   * Return with no errors...
6314   */
6315 
6316   con->response->request.status.status_code = IPP_OK;
6317 }
6318 
6319 
6320 /*
6321  * 'get_default()' - Get the default destination.
6322  */
6323 
6324 static void
get_default(cupsd_client_t * con)6325 get_default(cupsd_client_t *con)	/* I - Client connection */
6326 {
6327   http_status_t	status;			/* Policy status */
6328   cups_array_t	*ra;			/* Requested attributes array */
6329 
6330 
6331   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", (void *)con, con->number);
6332 
6333  /*
6334   * Check policy...
6335   */
6336 
6337   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6338   {
6339     send_http_error(con, status, NULL);
6340     return;
6341   }
6342 
6343   if (DefaultPrinter)
6344   {
6345     ra = create_requested_array(con->request);
6346 
6347     copy_printer_attrs(con, DefaultPrinter, ra);
6348 
6349     cupsArrayDelete(ra);
6350 
6351     con->response->request.status.status_code = IPP_OK;
6352   }
6353   else
6354     send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
6355 }
6356 
6357 
6358 /*
6359  * 'get_devices()' - Get the list of available devices on the local system.
6360  */
6361 
6362 static void
get_devices(cupsd_client_t * con)6363 get_devices(cupsd_client_t *con)	/* I - Client connection */
6364 {
6365   http_status_t		status;		/* Policy status */
6366   ipp_attribute_t	*limit,		/* limit attribute */
6367 			*timeout,	/* timeout attribute */
6368 			*requested,	/* requested-attributes attribute */
6369 			*exclude,	/* exclude-schemes attribute */
6370 			*include;	/* include-schemes attribute */
6371   char			command[1024],	/* cups-deviced command */
6372 			options[2048],	/* Options to pass to command */
6373 			requested_str[256],
6374 					/* String for requested attributes */
6375 			exclude_str[512],
6376 					/* String for excluded schemes */
6377 			include_str[512];
6378 					/* String for included schemes */
6379 
6380 
6381   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", (void *)con, con->number);
6382 
6383  /*
6384   * Check policy...
6385   */
6386 
6387   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
6388   {
6389     send_http_error(con, status, NULL);
6390     return;
6391   }
6392 
6393  /*
6394   * Run cups-deviced command with the given options...
6395   */
6396 
6397   limit     = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
6398   timeout   = ippFindAttribute(con->request, "timeout", IPP_TAG_INTEGER);
6399   requested = ippFindAttribute(con->request, "requested-attributes",
6400                                IPP_TAG_KEYWORD);
6401   exclude   = ippFindAttribute(con->request, "exclude-schemes", IPP_TAG_NAME);
6402   include   = ippFindAttribute(con->request, "include-schemes", IPP_TAG_NAME);
6403 
6404   if (requested)
6405     url_encode_attr(requested, requested_str, sizeof(requested_str));
6406   else
6407     strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
6408 
6409   if (exclude)
6410     url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
6411   else
6412     exclude_str[0] = '\0';
6413 
6414   if (include)
6415     url_encode_attr(include, include_str, sizeof(include_str));
6416   else
6417     include_str[0] = '\0';
6418 
6419   snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
6420   snprintf(options, sizeof(options),
6421            "%d+%d+%d+%d+%s%s%s%s%s",
6422            con->request->request.op.request_id,
6423            limit ? limit->values[0].integer : 0,
6424 	   timeout ? timeout->values[0].integer : 15,
6425 	   (int)User,
6426 	   requested_str,
6427 	   exclude_str[0] ? "%20" : "", exclude_str,
6428 	   include_str[0] ? "%20" : "", include_str);
6429 
6430   if (cupsdSendCommand(con, command, options, 1))
6431   {
6432    /*
6433     * Command started successfully, don't send an IPP response here...
6434     */
6435 
6436     ippDelete(con->response);
6437     con->response = NULL;
6438   }
6439   else
6440   {
6441    /*
6442     * Command failed, return "internal error" so the user knows something
6443     * went wrong...
6444     */
6445 
6446     send_ipp_status(con, IPP_INTERNAL_ERROR,
6447                     _("cups-deviced failed to execute."));
6448   }
6449 }
6450 
6451 
6452 /*
6453  * 'get_document()' - Get a copy of a job file.
6454  */
6455 
6456 static void
get_document(cupsd_client_t * con,ipp_attribute_t * uri)6457 get_document(cupsd_client_t  *con,	/* I - Client connection */
6458              ipp_attribute_t *uri)	/* I - Job URI */
6459 {
6460   http_status_t	status;			/* Policy status */
6461   ipp_attribute_t *attr;		/* Current attribute */
6462   int		jobid;			/* Job ID */
6463   int		docnum;			/* Document number */
6464   cupsd_job_t	*job;			/* Current job */
6465   char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
6466 		username[HTTP_MAX_URI],	/* Username portion of URI */
6467 		host[HTTP_MAX_URI],	/* Host portion of URI */
6468 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
6469   int		port;			/* Port portion of URI */
6470   char		filename[1024],		/* Filename for document */
6471 		format[1024];		/* Format for document */
6472 
6473 
6474   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", (void *)con,
6475                   con->number, uri->values[0].string.text);
6476 
6477  /*
6478   * See if we have a job URI or a printer URI...
6479   */
6480 
6481   if (!strcmp(uri->name, "printer-uri"))
6482   {
6483    /*
6484     * Got a printer URI; see if we also have a job-id attribute...
6485     */
6486 
6487     if ((attr = ippFindAttribute(con->request, "job-id",
6488                                  IPP_TAG_INTEGER)) == NULL)
6489     {
6490       send_ipp_status(con, IPP_BAD_REQUEST,
6491                       _("Got a printer-uri attribute but no job-id."));
6492       return;
6493     }
6494 
6495     jobid = attr->values[0].integer;
6496   }
6497   else
6498   {
6499    /*
6500     * Got a job URI; parse it to get the job ID...
6501     */
6502 
6503     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6504                     sizeof(scheme), username, sizeof(username), host,
6505 		    sizeof(host), &port, resource, sizeof(resource));
6506 
6507     if (strncmp(resource, "/jobs/", 6))
6508     {
6509      /*
6510       * Not a valid URI!
6511       */
6512 
6513       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
6514                       uri->values[0].string.text);
6515       return;
6516     }
6517 
6518     jobid = atoi(resource + 6);
6519   }
6520 
6521  /*
6522   * See if the job exists...
6523   */
6524 
6525   if ((job = cupsdFindJob(jobid)) == NULL)
6526   {
6527    /*
6528     * Nope - return a "not found" error...
6529     */
6530 
6531     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
6532     return;
6533   }
6534 
6535  /*
6536   * Check policy...
6537   */
6538 
6539   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
6540                                  job->username)) != HTTP_OK)
6541   {
6542     send_http_error(con, status, NULL);
6543     return;
6544   }
6545 
6546  /*
6547   * Get the document number...
6548   */
6549 
6550   if ((attr = ippFindAttribute(con->request, "document-number",
6551                                IPP_TAG_INTEGER)) == NULL)
6552   {
6553     send_ipp_status(con, IPP_BAD_REQUEST,
6554                     _("Missing document-number attribute."));
6555     return;
6556   }
6557 
6558   if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
6559       attr->num_values > 1)
6560   {
6561     send_ipp_status(con, IPP_NOT_FOUND,
6562                     _("Document #%d does not exist in job #%d."), docnum,
6563 		    jobid);
6564     return;
6565   }
6566 
6567   snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
6568            docnum);
6569   if ((con->file = open(filename, O_RDONLY)) == -1)
6570   {
6571     cupsdLogMessage(CUPSD_LOG_ERROR,
6572                     "Unable to open document %d in job %d - %s", docnum, jobid,
6573 		    strerror(errno));
6574     send_ipp_status(con, IPP_NOT_FOUND,
6575                     _("Unable to open document #%d in job #%d."), docnum,
6576 		    jobid);
6577     return;
6578   }
6579 
6580   fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
6581 
6582   cupsdLoadJob(job);
6583 
6584   snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
6585            job->filetypes[docnum - 1]->type);
6586 
6587   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
6588                NULL, format);
6589   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
6590                 docnum);
6591   if ((attr = ippFindAttribute(job->attrs, "document-name",
6592                                IPP_TAG_NAME)) != NULL)
6593     ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
6594                  NULL, attr->values[0].string.text);
6595 }
6596 
6597 
6598 /*
6599  * 'get_job_attrs()' - Get job attributes.
6600  */
6601 
6602 static void
get_job_attrs(cupsd_client_t * con,ipp_attribute_t * uri)6603 get_job_attrs(cupsd_client_t  *con,	/* I - Client connection */
6604 	      ipp_attribute_t *uri)	/* I - Job URI */
6605 {
6606   http_status_t	status;			/* Policy status */
6607   ipp_attribute_t *attr;		/* Current attribute */
6608   int		jobid;			/* Job ID */
6609   cupsd_job_t	*job;			/* Current job */
6610   cupsd_printer_t *printer;		/* Current printer */
6611   cupsd_policy_t *policy;		/* Current security policy */
6612   char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
6613 		username[HTTP_MAX_URI],	/* Username portion of URI */
6614 		host[HTTP_MAX_URI],	/* Host portion of URI */
6615 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
6616   int		port;			/* Port portion of URI */
6617   cups_array_t	*ra,			/* Requested attributes array */
6618 		*exclude;		/* Private attributes array */
6619 
6620 
6621   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", (void *)con,
6622                   con->number, uri->values[0].string.text);
6623 
6624  /*
6625   * See if we have a job URI or a printer URI...
6626   */
6627 
6628   if (!strcmp(uri->name, "printer-uri"))
6629   {
6630    /*
6631     * Got a printer URI; see if we also have a job-id attribute...
6632     */
6633 
6634     if ((attr = ippFindAttribute(con->request, "job-id",
6635                                  IPP_TAG_INTEGER)) == NULL)
6636     {
6637       send_ipp_status(con, IPP_BAD_REQUEST,
6638                       _("Got a printer-uri attribute but no job-id."));
6639       return;
6640     }
6641 
6642     jobid = attr->values[0].integer;
6643   }
6644   else
6645   {
6646    /*
6647     * Got a job URI; parse it to get the job ID...
6648     */
6649 
6650     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6651                     sizeof(scheme), username, sizeof(username), host,
6652 		    sizeof(host), &port, resource, sizeof(resource));
6653 
6654     if (strncmp(resource, "/jobs/", 6))
6655     {
6656      /*
6657       * Not a valid URI!
6658       */
6659 
6660       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
6661                       uri->values[0].string.text);
6662       return;
6663     }
6664 
6665     jobid = atoi(resource + 6);
6666   }
6667 
6668  /*
6669   * See if the job exists...
6670   */
6671 
6672   if ((job = cupsdFindJob(jobid)) == NULL)
6673   {
6674    /*
6675     * Nope - return a "not found" error...
6676     */
6677 
6678     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
6679     return;
6680   }
6681 
6682  /*
6683   * Check policy...
6684   */
6685 
6686   if ((printer = job->printer) == NULL)
6687     printer = cupsdFindDest(job->dest);
6688 
6689   if (printer)
6690     policy = printer->op_policy_ptr;
6691   else
6692     policy = DefaultPolicyPtr;
6693 
6694   if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
6695   {
6696     send_http_error(con, status, NULL);
6697     return;
6698   }
6699 
6700   exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
6701 
6702  /*
6703   * Copy attributes...
6704   */
6705 
6706   cupsdLoadJob(job);
6707 
6708   ra = create_requested_array(con->request);
6709   copy_job_attrs(con, job, ra, exclude);
6710   cupsArrayDelete(ra);
6711 
6712   con->response->request.status.status_code = IPP_OK;
6713 }
6714 
6715 
6716 /*
6717  * 'get_jobs()' - Get a list of jobs for the specified printer.
6718  */
6719 
6720 static void
get_jobs(cupsd_client_t * con,ipp_attribute_t * uri)6721 get_jobs(cupsd_client_t  *con,		/* I - Client connection */
6722 	 ipp_attribute_t *uri)		/* I - Printer URI */
6723 {
6724   http_status_t	status;			/* Policy status */
6725   ipp_attribute_t *attr;		/* Current attribute */
6726   const char	*dest;			/* Destination */
6727   cups_ptype_t	dtype;			/* Destination type (printer/class) */
6728   cups_ptype_t	dmask;			/* Destination type mask */
6729   char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
6730 		username[HTTP_MAX_URI],	/* Username portion of URI */
6731 		host[HTTP_MAX_URI],	/* Host portion of URI */
6732 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
6733   int		port;			/* Port portion of URI */
6734   int		job_comparison;		/* Job comparison */
6735   ipp_jstate_t	job_state;		/* job-state value */
6736   int		first_job_id = 1,	/* First job ID */
6737 		first_index = 1,	/* First index */
6738 		limit = 0,		/* Maximum number of jobs to return */
6739 		count,			/* Number of jobs that match */
6740 		need_load_job = 0;	/* Do we need to load the job? */
6741   const char	*job_attr;		/* Job attribute requested */
6742   ipp_attribute_t *job_ids;		/* job-ids attribute */
6743   cupsd_job_t	*job;			/* Current job pointer */
6744   cupsd_printer_t *printer;		/* Printer */
6745   cups_array_t	*list;			/* Which job list... */
6746   int		delete_list = 0;	/* Delete the list afterwards? */
6747   cups_array_t	*ra,			/* Requested attributes array */
6748 		*exclude;		/* Private attributes array */
6749   cupsd_policy_t *policy;		/* Current policy */
6750 
6751 
6752   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", (void *)con, con->number,
6753                   uri->values[0].string.text);
6754 
6755  /*
6756   * Is the destination valid?
6757   */
6758 
6759   if (strcmp(uri->name, "printer-uri"))
6760   {
6761     send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
6762     return;
6763   }
6764 
6765   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
6766                   sizeof(scheme), username, sizeof(username), host,
6767 		  sizeof(host), &port, resource, sizeof(resource));
6768 
6769   if (!strcmp(resource, "/") || !strcmp(resource, "/jobs"))
6770   {
6771     dest    = NULL;
6772     dtype   = (cups_ptype_t)0;
6773     dmask   = (cups_ptype_t)0;
6774     printer = NULL;
6775   }
6776   else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
6777   {
6778     dest    = NULL;
6779     dtype   = (cups_ptype_t)0;
6780     dmask   = CUPS_PRINTER_CLASS;
6781     printer = NULL;
6782   }
6783   else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
6784   {
6785     dest    = NULL;
6786     dtype   = CUPS_PRINTER_CLASS;
6787     dmask   = CUPS_PRINTER_CLASS;
6788     printer = NULL;
6789   }
6790   else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
6791                                      &printer)) == NULL)
6792   {
6793    /*
6794     * Bad URI...
6795     */
6796 
6797     send_ipp_status(con, IPP_NOT_FOUND,
6798                     _("The printer or class does not exist."));
6799     return;
6800   }
6801   else
6802   {
6803     dtype &= CUPS_PRINTER_CLASS;
6804     dmask = CUPS_PRINTER_CLASS;
6805   }
6806 
6807  /*
6808   * Check policy...
6809   */
6810 
6811   if (printer)
6812     policy = printer->op_policy_ptr;
6813   else
6814     policy = DefaultPolicyPtr;
6815 
6816   if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
6817   {
6818     send_http_error(con, status, NULL);
6819     return;
6820   }
6821 
6822   job_ids = ippFindAttribute(con->request, "job-ids", IPP_TAG_INTEGER);
6823 
6824  /*
6825   * See if the "which-jobs" attribute have been specified...
6826   */
6827 
6828   if ((attr = ippFindAttribute(con->request, "which-jobs",
6829                                IPP_TAG_KEYWORD)) != NULL && job_ids)
6830   {
6831     send_ipp_status(con, IPP_CONFLICT,
6832                     _("The %s attribute cannot be provided with job-ids."),
6833                     "which-jobs");
6834     return;
6835   }
6836   else if (!attr || !strcmp(attr->values[0].string.text, "not-completed"))
6837   {
6838     job_comparison = -1;
6839     job_state      = IPP_JOB_STOPPED;
6840     list           = ActiveJobs;
6841   }
6842   else if (!strcmp(attr->values[0].string.text, "completed"))
6843   {
6844     job_comparison = 1;
6845     job_state      = IPP_JOB_CANCELED;
6846     list           = cupsdGetCompletedJobs(printer);
6847     delete_list    = 1;
6848   }
6849   else if (!strcmp(attr->values[0].string.text, "aborted"))
6850   {
6851     job_comparison = 0;
6852     job_state      = IPP_JOB_ABORTED;
6853     list           = cupsdGetCompletedJobs(printer);
6854     delete_list    = 1;
6855   }
6856   else if (!strcmp(attr->values[0].string.text, "all"))
6857   {
6858     job_comparison = 1;
6859     job_state      = IPP_JOB_PENDING;
6860     list           = Jobs;
6861   }
6862   else if (!strcmp(attr->values[0].string.text, "canceled"))
6863   {
6864     job_comparison = 0;
6865     job_state      = IPP_JOB_CANCELED;
6866     list           = cupsdGetCompletedJobs(printer);
6867     delete_list    = 1;
6868   }
6869   else if (!strcmp(attr->values[0].string.text, "pending"))
6870   {
6871     job_comparison = 0;
6872     job_state      = IPP_JOB_PENDING;
6873     list           = ActiveJobs;
6874   }
6875   else if (!strcmp(attr->values[0].string.text, "pending-held"))
6876   {
6877     job_comparison = 0;
6878     job_state      = IPP_JOB_HELD;
6879     list           = ActiveJobs;
6880   }
6881   else if (!strcmp(attr->values[0].string.text, "processing"))
6882   {
6883     job_comparison = 0;
6884     job_state      = IPP_JOB_PROCESSING;
6885     list           = PrintingJobs;
6886   }
6887   else if (!strcmp(attr->values[0].string.text, "processing-stopped"))
6888   {
6889     job_comparison = 0;
6890     job_state      = IPP_JOB_STOPPED;
6891     list           = ActiveJobs;
6892   }
6893   else
6894   {
6895     send_ipp_status(con, IPP_ATTRIBUTES,
6896                     _("The which-jobs value \"%s\" is not supported."),
6897 		    attr->values[0].string.text);
6898     ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
6899                  "which-jobs", NULL, attr->values[0].string.text);
6900     return;
6901   }
6902 
6903  /*
6904   * See if they want to limit the number of jobs reported...
6905   */
6906 
6907   if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
6908   {
6909     if (job_ids)
6910     {
6911       send_ipp_status(con, IPP_CONFLICT,
6912 		      _("The %s attribute cannot be provided with job-ids."),
6913 		      "limit");
6914       return;
6915     }
6916 
6917     limit = attr->values[0].integer;
6918   }
6919 
6920   if ((attr = ippFindAttribute(con->request, "first-index", IPP_TAG_INTEGER)) != NULL)
6921   {
6922     if (job_ids)
6923     {
6924       send_ipp_status(con, IPP_CONFLICT,
6925 		      _("The %s attribute cannot be provided with job-ids."),
6926 		      "first-index");
6927       return;
6928     }
6929 
6930     first_index = attr->values[0].integer;
6931   }
6932   else if ((attr = ippFindAttribute(con->request, "first-job-id", IPP_TAG_INTEGER)) != NULL)
6933   {
6934     if (job_ids)
6935     {
6936       send_ipp_status(con, IPP_CONFLICT,
6937 		      _("The %s attribute cannot be provided with job-ids."),
6938 		      "first-job-id");
6939       return;
6940     }
6941 
6942     first_job_id = attr->values[0].integer;
6943   }
6944 
6945  /*
6946   * See if we only want to see jobs for a specific user...
6947   */
6948 
6949   if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL && job_ids)
6950   {
6951     send_ipp_status(con, IPP_CONFLICT,
6952                     _("The %s attribute cannot be provided with job-ids."),
6953                     "my-jobs");
6954     return;
6955   }
6956   else if (attr && attr->values[0].boolean)
6957     strlcpy(username, get_username(con), sizeof(username));
6958   else
6959     username[0] = '\0';
6960 
6961   ra = create_requested_array(con->request);
6962   for (job_attr = (char *)cupsArrayFirst(ra); job_attr; job_attr = (char *)cupsArrayNext(ra))
6963     if (strcmp(job_attr, "job-id") &&
6964 	strcmp(job_attr, "job-k-octets") &&
6965 	strcmp(job_attr, "job-media-progress") &&
6966 	strcmp(job_attr, "job-more-info") &&
6967 	strcmp(job_attr, "job-name") &&
6968 	strcmp(job_attr, "job-originating-user-name") &&
6969 	strcmp(job_attr, "job-preserved") &&
6970 	strcmp(job_attr, "job-printer-up-time") &&
6971         strcmp(job_attr, "job-printer-uri") &&
6972 	strcmp(job_attr, "job-state") &&
6973 	strcmp(job_attr, "job-state-reasons") &&
6974 	strcmp(job_attr, "job-uri") &&
6975 	strcmp(job_attr, "time-at-completed") &&
6976 	strcmp(job_attr, "time-at-creation") &&
6977 	strcmp(job_attr, "number-of-documents"))
6978     {
6979       need_load_job = 1;
6980       break;
6981     }
6982 
6983   if (need_load_job && (limit == 0 || limit > 500) && (list == Jobs || delete_list))
6984   {
6985    /*
6986     * Limit expensive Get-Jobs for job history to 500 jobs...
6987     */
6988 
6989     ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "limit", 500);
6990 
6991     if (limit)
6992       ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER, "limit", limit);
6993 
6994     limit = 500;
6995 
6996     cupsdLogClient(con, CUPSD_LOG_INFO, "Limiting Get-Jobs response to %d jobs.", limit);
6997   }
6998 
6999  /*
7000   * OK, build a list of jobs for this printer...
7001   */
7002 
7003   if (job_ids)
7004   {
7005     int	i;				/* Looping var */
7006 
7007     for (i = 0; i < job_ids->num_values; i ++)
7008     {
7009       if (!cupsdFindJob(job_ids->values[i].integer))
7010         break;
7011     }
7012 
7013     if (i < job_ids->num_values)
7014     {
7015       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7016                       job_ids->values[i].integer);
7017       cupsArrayDelete(ra);
7018       return;
7019     }
7020 
7021     for (i = 0; i < job_ids->num_values; i ++)
7022     {
7023       job = cupsdFindJob(job_ids->values[i].integer);
7024 
7025       if (need_load_job && !job->attrs)
7026       {
7027         cupsdLoadJob(job);
7028 
7029 	if (!job->attrs)
7030 	{
7031 	  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
7032 	  continue;
7033 	}
7034       }
7035 
7036       if (i > 0)
7037 	ippAddSeparator(con->response);
7038 
7039       exclude = cupsdGetPrivateAttrs(job->printer ?
7040                                          job->printer->op_policy_ptr :
7041 					 policy, con, job->printer,
7042 					 job->username);
7043 
7044       copy_job_attrs(con, job, ra, exclude);
7045     }
7046   }
7047   else
7048   {
7049     if (first_index > 1)
7050       job = (cupsd_job_t *)cupsArrayIndex(list, first_index - 1);
7051     else
7052       job = (cupsd_job_t *)cupsArrayFirst(list);
7053 
7054     for (count = 0; (limit <= 0 || count < limit) && job; job = (cupsd_job_t *)cupsArrayNext(list))
7055     {
7056      /*
7057       * Filter out jobs that don't match...
7058       */
7059 
7060       cupsdLogMessage(CUPSD_LOG_DEBUG2,
7061 		      "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
7062 		      "state_value=%d, attrs=%p", job->id, job->dest,
7063 		      job->username, job->state_value, (void *)job->attrs);
7064 
7065       if (!job->dest || !job->username)
7066 	cupsdLoadJob(job);
7067 
7068       if (!job->dest || !job->username)
7069 	continue;
7070 
7071       if ((dest && strcmp(job->dest, dest)) &&
7072 	  (!job->printer || !dest || strcmp(job->printer->name, dest)))
7073 	continue;
7074       if ((job->dtype & dmask) != dtype &&
7075 	  (!job->printer || (job->printer->type & dmask) != dtype))
7076 	continue;
7077 
7078       if ((job_comparison < 0 && job->state_value > job_state) ||
7079           (job_comparison == 0 && job->state_value != job_state) ||
7080           (job_comparison > 0 && job->state_value < job_state))
7081 	continue;
7082 
7083       if (job->id < first_job_id)
7084 	continue;
7085 
7086       if (need_load_job && !job->attrs)
7087       {
7088         cupsdLoadJob(job);
7089 
7090 	if (!job->attrs)
7091 	{
7092 	  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: No attributes for job %d", job->id);
7093 	  continue;
7094 	}
7095       }
7096 
7097       if (username[0] && _cups_strcasecmp(username, job->username))
7098 	continue;
7099 
7100       if (count > 0)
7101 	ippAddSeparator(con->response);
7102 
7103       count ++;
7104 
7105       exclude = cupsdGetPrivateAttrs(job->printer ?
7106                                          job->printer->op_policy_ptr :
7107 					 policy, con, job->printer,
7108 					 job->username);
7109 
7110       copy_job_attrs(con, job, ra, exclude);
7111     }
7112 
7113     cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
7114   }
7115 
7116   cupsArrayDelete(ra);
7117 
7118   if (delete_list)
7119     cupsArrayDelete(list);
7120 
7121   con->response->request.status.status_code = IPP_OK;
7122 }
7123 
7124 
7125 /*
7126  * 'get_notifications()' - Get events for a subscription.
7127  */
7128 
7129 static void
get_notifications(cupsd_client_t * con)7130 get_notifications(cupsd_client_t *con)	/* I - Client connection */
7131 {
7132   int			i, j;		/* Looping vars */
7133   http_status_t		status;		/* Policy status */
7134   cupsd_subscription_t	*sub;		/* Subscription */
7135   ipp_attribute_t	*ids,		/* notify-subscription-ids */
7136 			*sequences;	/* notify-sequence-numbers */
7137   int			min_seq;	/* Minimum sequence number */
7138   int			interval;	/* Poll interval */
7139 
7140 
7141   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
7142                   (void *)con, con->number);
7143 
7144  /*
7145   * Get subscription attributes...
7146   */
7147 
7148   ids       = ippFindAttribute(con->request, "notify-subscription-ids",
7149                                IPP_TAG_INTEGER);
7150   sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
7151                                IPP_TAG_INTEGER);
7152 
7153   if (!ids)
7154   {
7155     send_ipp_status(con, IPP_BAD_REQUEST,
7156                     _("Missing notify-subscription-ids attribute."));
7157     return;
7158   }
7159 
7160  /*
7161   * Are the subscription IDs valid?
7162   */
7163 
7164   for (i = 0, interval = 60; i < ids->num_values; i ++)
7165   {
7166     if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
7167     {
7168      /*
7169       * Bad subscription ID...
7170       */
7171 
7172       send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7173 		      ids->values[i].integer);
7174       return;
7175     }
7176 
7177    /*
7178     * Check policy...
7179     */
7180 
7181     if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
7182                                                DefaultPolicyPtr,
7183                                    con, sub->owner)) != HTTP_OK)
7184     {
7185       send_http_error(con, status, sub->dest);
7186       return;
7187     }
7188 
7189    /*
7190     * Check the subscription type and update the interval accordingly.
7191     */
7192 
7193     if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
7194         interval > 10)
7195       interval = 10;
7196     else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
7197       interval = 0;
7198     else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
7199              interval > 30)
7200       interval = 30;
7201   }
7202 
7203  /*
7204   * Tell the client to poll again in N seconds...
7205   */
7206 
7207   if (interval > 0)
7208     ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7209                   "notify-get-interval", interval);
7210 
7211   ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
7212                 "printer-up-time", time(NULL));
7213 
7214  /*
7215   * Copy the subscription event attributes to the response.
7216   */
7217 
7218   con->response->request.status.status_code =
7219       interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
7220 
7221   for (i = 0; i < ids->num_values; i ++)
7222   {
7223    /*
7224     * Get the subscription and sequence number...
7225     */
7226 
7227     sub = cupsdFindSubscription(ids->values[i].integer);
7228 
7229     if (sequences && i < sequences->num_values)
7230       min_seq = sequences->values[i].integer;
7231     else
7232       min_seq = 1;
7233 
7234    /*
7235     * If we don't have any new events, nothing to do here...
7236     */
7237 
7238     if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
7239       continue;
7240 
7241    /*
7242     * Otherwise copy all of the new events...
7243     */
7244 
7245     if (sub->first_event_id > min_seq)
7246       j = 0;
7247     else
7248       j = min_seq - sub->first_event_id;
7249 
7250     for (; j < cupsArrayCount(sub->events); j ++)
7251     {
7252       ippAddSeparator(con->response);
7253 
7254       copy_attrs(con->response,
7255                  ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
7256         	 IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
7257     }
7258   }
7259 }
7260 
7261 
7262 /*
7263  * 'get_ppd()' - Get a named PPD from the local system.
7264  */
7265 
7266 static void
get_ppd(cupsd_client_t * con,ipp_attribute_t * uri)7267 get_ppd(cupsd_client_t  *con,		/* I - Client connection */
7268         ipp_attribute_t *uri)		/* I - Printer URI or PPD name */
7269 {
7270   http_status_t		status;		/* Policy status */
7271   cupsd_printer_t	*dest;		/* Destination */
7272   cups_ptype_t		dtype;		/* Destination type */
7273 
7274 
7275   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", (void *)con,
7276                   con->number, (void *)uri, uri->name, uri->values[0].string.text);
7277 
7278   if (!strcmp(ippGetName(uri), "ppd-name"))
7279   {
7280    /*
7281     * Return a PPD file from cups-driverd...
7282     */
7283 
7284     const char *ppd_name = ippGetString(uri, 0, NULL);
7285 					/* ppd-name value */
7286     char	command[1024],		/* cups-driverd command */
7287 		options[1024],		/* Options to pass to command */
7288 		oppd_name[1024];	/* Escaped ppd-name */
7289 
7290    /*
7291     * Check policy...
7292     */
7293 
7294     if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7295     {
7296       send_http_error(con, status, NULL);
7297       return;
7298     }
7299 
7300    /*
7301     * Check ppd-name value...
7302     */
7303 
7304     if (strstr(ppd_name, "../"))
7305     {
7306       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Invalid ppd-name value."));
7307       return;
7308     }
7309 
7310    /*
7311     * Run cups-driverd command with the given options...
7312     */
7313 
7314     snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7315     url_encode_string(ppd_name, oppd_name, sizeof(oppd_name));
7316     snprintf(options, sizeof(options), "get+%d+%s", ippGetRequestId(con->request), oppd_name);
7317 
7318     if (cupsdSendCommand(con, command, options, 0))
7319     {
7320      /*
7321       * Command started successfully, don't send an IPP response here...
7322       */
7323 
7324       ippDelete(con->response);
7325       con->response = NULL;
7326     }
7327     else
7328     {
7329      /*
7330       * Command failed, return "internal error" so the user knows something
7331       * went wrong...
7332       */
7333 
7334       send_ipp_status(con, IPP_INTERNAL_ERROR, _("cups-driverd failed to execute."));
7335     }
7336   }
7337   else if (!strcmp(ippGetName(uri), "printer-uri") && cupsdValidateDest(ippGetString(uri, 0, NULL), &dtype, &dest))
7338   {
7339     int 	i;			/* Looping var */
7340     char	filename[1024];		/* PPD filename */
7341 
7342    /*
7343     * Check policy...
7344     */
7345 
7346     if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
7347     {
7348       send_http_error(con, status, dest);
7349       return;
7350     }
7351 
7352    /*
7353     * See if we need the PPD for a class or remote printer...
7354     */
7355 
7356     snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest->name);
7357 
7358     if ((dtype & CUPS_PRINTER_REMOTE) && access(filename, 0))
7359     {
7360       send_ipp_status(con, IPP_STATUS_CUPS_SEE_OTHER, _("See remote printer."));
7361       ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, dest->uri);
7362       return;
7363     }
7364     else if (dtype & CUPS_PRINTER_CLASS)
7365     {
7366       for (i = 0; i < dest->num_printers; i ++)
7367         if (!(dest->printers[i]->type & CUPS_PRINTER_CLASS))
7368 	{
7369 	  snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest->printers[i]->name);
7370 
7371           if (!access(filename, 0))
7372 	    break;
7373         }
7374 
7375       if (i < dest->num_printers)
7376         dest = dest->printers[i];
7377       else
7378       {
7379 	send_ipp_status(con, IPP_STATUS_CUPS_SEE_OTHER, _("See remote printer."));
7380         ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, dest->printers[0]->uri);
7381         return;
7382       }
7383     }
7384 
7385    /*
7386     * Found the printer with the PPD file, now see if there is one...
7387     */
7388 
7389     if ((con->file = open(filename, O_RDONLY)) < 0)
7390     {
7391       send_ipp_status(con, IPP_STATUS_ERROR_NOT_FOUND, _("The PPD file \"%s\" could not be opened: %s"), ippGetString(uri, 0, NULL), strerror(errno));
7392       return;
7393     }
7394 
7395     fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
7396 
7397     con->pipe_pid = 0;
7398 
7399     ippSetStatusCode(con->response, IPP_STATUS_OK);
7400   }
7401   else
7402     send_ipp_status(con, IPP_STATUS_ERROR_NOT_FOUND, _("The PPD file \"%s\" could not be found."), ippGetString(uri, 0, NULL));
7403 }
7404 
7405 
7406 /*
7407  * 'get_ppds()' - Get the list of PPD files on the local system.
7408  */
7409 
7410 static void
get_ppds(cupsd_client_t * con)7411 get_ppds(cupsd_client_t *con)		/* I - Client connection */
7412 {
7413   http_status_t		status;		/* Policy status */
7414   ipp_attribute_t	*limit,		/* Limit attribute */
7415 			*device,	/* ppd-device-id attribute */
7416 			*language,	/* ppd-natural-language attribute */
7417 			*make,		/* ppd-make attribute */
7418 			*model,		/* ppd-make-and-model attribute */
7419 			*model_number,	/* ppd-model-number attribute */
7420 			*product,	/* ppd-product attribute */
7421 			*psversion,	/* ppd-psverion attribute */
7422 			*type,		/* ppd-type attribute */
7423 			*requested,	/* requested-attributes attribute */
7424 			*exclude,	/* exclude-schemes attribute */
7425 			*include;	/* include-schemes attribute */
7426   char			command[1024],	/* cups-driverd command */
7427 			options[4096],	/* Options to pass to command */
7428 			device_str[256],/* Escaped ppd-device-id string */
7429 			language_str[256],
7430 					/* Escaped ppd-natural-language */
7431 			make_str[256],	/* Escaped ppd-make string */
7432 			model_str[256],	/* Escaped ppd-make-and-model string */
7433 			model_number_str[256],
7434 					/* ppd-model-number string */
7435 			product_str[256],
7436 					/* Escaped ppd-product string */
7437 			psversion_str[256],
7438 					/* Escaped ppd-psversion string */
7439 			type_str[256],	/* Escaped ppd-type string */
7440 			requested_str[256],
7441 					/* String for requested attributes */
7442 			exclude_str[512],
7443 					/* String for excluded schemes */
7444 			include_str[512];
7445 					/* String for included schemes */
7446 
7447 
7448   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", (void *)con, con->number);
7449 
7450  /*
7451   * Check policy...
7452   */
7453 
7454   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7455   {
7456     send_http_error(con, status, NULL);
7457     return;
7458   }
7459 
7460  /*
7461   * Run cups-driverd command with the given options...
7462   */
7463 
7464   limit        = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
7465   device       = ippFindAttribute(con->request, "ppd-device-id", IPP_TAG_TEXT);
7466   language     = ippFindAttribute(con->request, "ppd-natural-language",
7467                                   IPP_TAG_LANGUAGE);
7468   make         = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
7469   model        = ippFindAttribute(con->request, "ppd-make-and-model",
7470                                   IPP_TAG_TEXT);
7471   model_number = ippFindAttribute(con->request, "ppd-model-number",
7472                                   IPP_TAG_INTEGER);
7473   product      = ippFindAttribute(con->request, "ppd-product", IPP_TAG_TEXT);
7474   psversion    = ippFindAttribute(con->request, "ppd-psversion", IPP_TAG_TEXT);
7475   type         = ippFindAttribute(con->request, "ppd-type", IPP_TAG_KEYWORD);
7476   requested    = ippFindAttribute(con->request, "requested-attributes",
7477                                   IPP_TAG_KEYWORD);
7478   exclude      = ippFindAttribute(con->request, "exclude-schemes",
7479                                   IPP_TAG_NAME);
7480   include      = ippFindAttribute(con->request, "include-schemes",
7481                                   IPP_TAG_NAME);
7482 
7483   if (requested)
7484     url_encode_attr(requested, requested_str, sizeof(requested_str));
7485   else
7486     strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
7487 
7488   if (device)
7489     url_encode_attr(device, device_str, sizeof(device_str));
7490   else
7491     device_str[0] = '\0';
7492 
7493   if (language)
7494     url_encode_attr(language, language_str, sizeof(language_str));
7495   else
7496     language_str[0] = '\0';
7497 
7498   if (make)
7499     url_encode_attr(make, make_str, sizeof(make_str));
7500   else
7501     make_str[0] = '\0';
7502 
7503   if (model)
7504     url_encode_attr(model, model_str, sizeof(model_str));
7505   else
7506     model_str[0] = '\0';
7507 
7508   if (model_number)
7509     snprintf(model_number_str, sizeof(model_number_str), "ppd-model-number=%d",
7510              model_number->values[0].integer);
7511   else
7512     model_number_str[0] = '\0';
7513 
7514   if (product)
7515     url_encode_attr(product, product_str, sizeof(product_str));
7516   else
7517     product_str[0] = '\0';
7518 
7519   if (psversion)
7520     url_encode_attr(psversion, psversion_str, sizeof(psversion_str));
7521   else
7522     psversion_str[0] = '\0';
7523 
7524   if (type)
7525     url_encode_attr(type, type_str, sizeof(type_str));
7526   else
7527     type_str[0] = '\0';
7528 
7529   if (exclude)
7530     url_encode_attr(exclude, exclude_str, sizeof(exclude_str));
7531   else
7532     exclude_str[0] = '\0';
7533 
7534   if (include)
7535     url_encode_attr(include, include_str, sizeof(include_str));
7536   else
7537     include_str[0] = '\0';
7538 
7539   snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
7540   snprintf(options, sizeof(options),
7541            "list+%d+%d+%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
7542            con->request->request.op.request_id,
7543            limit ? limit->values[0].integer : 0,
7544 	   requested_str,
7545 	   device ? "%20" : "", device_str,
7546 	   language ? "%20" : "", language_str,
7547 	   make ? "%20" : "", make_str,
7548 	   model ? "%20" : "", model_str,
7549 	   model_number ? "%20" : "", model_number_str,
7550 	   product ? "%20" : "", product_str,
7551 	   psversion ? "%20" : "", psversion_str,
7552 	   type ? "%20" : "", type_str,
7553 	   exclude_str[0] ? "%20" : "", exclude_str,
7554 	   include_str[0] ? "%20" : "", include_str);
7555 
7556   if (cupsdSendCommand(con, command, options, 0))
7557   {
7558    /*
7559     * Command started successfully, don't send an IPP response here...
7560     */
7561 
7562     ippDelete(con->response);
7563     con->response = NULL;
7564   }
7565   else
7566   {
7567    /*
7568     * Command failed, return "internal error" so the user knows something
7569     * went wrong...
7570     */
7571 
7572     send_ipp_status(con, IPP_INTERNAL_ERROR,
7573                     _("cups-driverd failed to execute."));
7574   }
7575 }
7576 
7577 
7578 /*
7579  * 'get_printer_attrs()' - Get printer attributes.
7580  */
7581 
7582 static void
get_printer_attrs(cupsd_client_t * con,ipp_attribute_t * uri)7583 get_printer_attrs(cupsd_client_t  *con,	/* I - Client connection */
7584 		  ipp_attribute_t *uri)	/* I - Printer URI */
7585 {
7586   http_status_t		status;		/* Policy status */
7587   cups_ptype_t		dtype;		/* Destination type (printer/class) */
7588   cupsd_printer_t	*printer;	/* Printer/class */
7589   cups_array_t		*ra;		/* Requested attributes array */
7590 
7591 
7592   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", (void *)con,
7593                   con->number, uri->values[0].string.text);
7594 
7595  /*
7596   * Is the destination valid?
7597   */
7598 
7599   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7600   {
7601    /*
7602     * Bad URI...
7603     */
7604 
7605     send_ipp_status(con, IPP_NOT_FOUND,
7606                     _("The printer or class does not exist."));
7607     return;
7608   }
7609 
7610  /*
7611   * Check policy...
7612   */
7613 
7614   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7615   {
7616     send_http_error(con, status, printer);
7617     return;
7618   }
7619 
7620  /*
7621   * Send the attributes...
7622   */
7623 
7624   ra = create_requested_array(con->request);
7625 
7626   copy_printer_attrs(con, printer, ra);
7627 
7628   cupsArrayDelete(ra);
7629 
7630   con->response->request.status.status_code = IPP_OK;
7631 }
7632 
7633 
7634 /*
7635  * 'get_printer_supported()' - Get printer supported values.
7636  */
7637 
7638 static void
get_printer_supported(cupsd_client_t * con,ipp_attribute_t * uri)7639 get_printer_supported(
7640     cupsd_client_t  *con,		/* I - Client connection */
7641     ipp_attribute_t *uri)		/* I - Printer URI */
7642 {
7643   http_status_t		status;		/* Policy status */
7644   cups_ptype_t		dtype;		/* Destination type (printer/class) */
7645   cupsd_printer_t	*printer;	/* Printer/class */
7646 
7647 
7648   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", (void *)con,
7649                   con->number, uri->values[0].string.text);
7650 
7651  /*
7652   * Is the destination valid?
7653   */
7654 
7655   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7656   {
7657    /*
7658     * Bad URI...
7659     */
7660 
7661     send_ipp_status(con, IPP_NOT_FOUND,
7662                     _("The printer or class does not exist."));
7663     return;
7664   }
7665 
7666  /*
7667   * Check policy...
7668   */
7669 
7670   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
7671   {
7672     send_http_error(con, status, printer);
7673     return;
7674   }
7675 
7676  /*
7677   * Return a list of attributes that can be set via Set-Printer-Attributes.
7678   */
7679 
7680   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7681                 "printer-geo-location", 0);
7682   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7683                 "printer-info", 0);
7684   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7685                 "printer-location", 0);
7686   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7687                 "printer-organization", 0);
7688   ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ADMINDEFINE,
7689                 "printer-organizational-unit", 0);
7690 
7691   con->response->request.status.status_code = IPP_OK;
7692 }
7693 
7694 
7695 /*
7696  * 'get_printers()' - Get a list of printers or classes.
7697  */
7698 
7699 static void
get_printers(cupsd_client_t * con,int type)7700 get_printers(cupsd_client_t *con,	/* I - Client connection */
7701              int            type)	/* I - 0 or CUPS_PRINTER_CLASS */
7702 {
7703   http_status_t	status;			/* Policy status */
7704   ipp_attribute_t *attr;		/* Current attribute */
7705   int		limit;			/* Max number of printers to return */
7706   int		count;			/* Number of printers that match */
7707   int		printer_id;		/* Printer we are interested in */
7708   cupsd_printer_t *printer;		/* Current printer pointer */
7709   cups_ptype_t	printer_type,		/* printer-type attribute */
7710 		printer_mask;		/* printer-type-mask attribute */
7711   char		*location;		/* Location string */
7712   const char	*username;		/* Current user */
7713   char		*first_printer_name;	/* first-printer-name attribute */
7714   cups_array_t	*ra;			/* Requested attributes array */
7715   int		local;			/* Local connection? */
7716 
7717 
7718   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", (void *)con,
7719                   con->number, type);
7720 
7721  /*
7722   * Check policy...
7723   */
7724 
7725   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
7726   {
7727     send_http_error(con, status, NULL);
7728     return;
7729   }
7730 
7731  /*
7732   * Check for printers...
7733   */
7734 
7735   if (!Printers || !cupsArrayCount(Printers))
7736   {
7737     send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
7738     return;
7739   }
7740 
7741  /*
7742   * See if they want to limit the number of printers reported...
7743   */
7744 
7745   if ((attr = ippFindAttribute(con->request, "limit",
7746                                IPP_TAG_INTEGER)) != NULL)
7747     limit = attr->values[0].integer;
7748   else
7749     limit = 10000000;
7750 
7751   if ((attr = ippFindAttribute(con->request, "first-printer-name",
7752                                IPP_TAG_NAME)) != NULL)
7753     first_printer_name = attr->values[0].string.text;
7754   else
7755     first_printer_name = NULL;
7756 
7757  /*
7758   * Support filtering...
7759   */
7760 
7761   if ((attr = ippFindAttribute(con->request, "printer-id", IPP_TAG_INTEGER)) != NULL)
7762   {
7763     if ((printer_id = ippGetInteger(attr, 0)) <= 0)
7764     {
7765       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Bad \"printer-id\" value %d."), printer_id);
7766       return;
7767     }
7768   }
7769   else
7770     printer_id = 0;
7771 
7772   if ((attr = ippFindAttribute(con->request, "printer-type",
7773                                IPP_TAG_ENUM)) != NULL)
7774     printer_type = (cups_ptype_t)attr->values[0].integer;
7775   else
7776     printer_type = (cups_ptype_t)0;
7777 
7778   if ((attr = ippFindAttribute(con->request, "printer-type-mask",
7779                                IPP_TAG_ENUM)) != NULL)
7780     printer_mask = (cups_ptype_t)attr->values[0].integer;
7781   else
7782     printer_mask = (cups_ptype_t)0;
7783 
7784   local = httpAddrLocalhost(&(con->clientaddr));
7785 
7786   if ((attr = ippFindAttribute(con->request, "printer-location",
7787                                IPP_TAG_TEXT)) != NULL)
7788     location = attr->values[0].string.text;
7789   else
7790     location = NULL;
7791 
7792   if (con->username[0])
7793     username = con->username;
7794   else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
7795                                     IPP_TAG_NAME)) != NULL)
7796     username = attr->values[0].string.text;
7797   else
7798     username = NULL;
7799 
7800   ra = create_requested_array(con->request);
7801 
7802  /*
7803   * OK, build a list of printers for this printer...
7804   */
7805 
7806   if (first_printer_name)
7807   {
7808     if ((printer = cupsdFindDest(first_printer_name)) == NULL)
7809       printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7810   }
7811   else
7812     printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
7813 
7814   for (count = 0;
7815        count < limit && printer;
7816        printer = (cupsd_printer_t *)cupsArrayNext(Printers))
7817   {
7818     if (!local && !printer->shared)
7819       continue;
7820 
7821     if (printer_id && printer->printer_id != printer_id)
7822       continue;
7823 
7824     if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
7825         (printer->type & printer_mask) == printer_type &&
7826 	(!location ||
7827 	 (printer->location && !_cups_strcasecmp(printer->location, location))))
7828     {
7829      /*
7830       * If a username is specified, see if it is allowed or denied
7831       * access...
7832       */
7833 
7834       if (cupsArrayCount(printer->users) && username &&
7835 	  !user_allowed(printer, username))
7836         continue;
7837 
7838      /*
7839       * Add the group separator as needed...
7840       */
7841 
7842       if (count > 0)
7843         ippAddSeparator(con->response);
7844 
7845       count ++;
7846 
7847      /*
7848       * Send the attributes...
7849       */
7850 
7851       copy_printer_attrs(con, printer, ra);
7852     }
7853   }
7854 
7855   cupsArrayDelete(ra);
7856 
7857   con->response->request.status.status_code = IPP_OK;
7858 }
7859 
7860 
7861 /*
7862  * 'get_subscription_attrs()' - Get subscription attributes.
7863  */
7864 
7865 static void
get_subscription_attrs(cupsd_client_t * con,int sub_id)7866 get_subscription_attrs(
7867     cupsd_client_t *con,		/* I - Client connection */
7868     int            sub_id)		/* I - Subscription ID */
7869 {
7870   http_status_t		status;		/* Policy status */
7871   cupsd_subscription_t	*sub;		/* Subscription */
7872   cupsd_policy_t	*policy;	/* Current security policy */
7873   cups_array_t		*ra,		/* Requested attributes array */
7874 			*exclude;	/* Private attributes array */
7875 
7876 
7877   cupsdLogMessage(CUPSD_LOG_DEBUG2,
7878                   "get_subscription_attrs(con=%p[%d], sub_id=%d)",
7879                   (void *)con, con->number, sub_id);
7880 
7881  /*
7882   * Expire subscriptions as needed...
7883   */
7884 
7885   cupsdExpireSubscriptions(NULL, NULL);
7886 
7887  /*
7888   * Is the subscription ID valid?
7889   */
7890 
7891   if ((sub = cupsdFindSubscription(sub_id)) == NULL)
7892   {
7893    /*
7894     * Bad subscription ID...
7895     */
7896 
7897     send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
7898                     sub_id);
7899     return;
7900   }
7901 
7902  /*
7903   * Check policy...
7904   */
7905 
7906   if (sub->dest)
7907     policy = sub->dest->op_policy_ptr;
7908   else
7909     policy = DefaultPolicyPtr;
7910 
7911   if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
7912   {
7913     send_http_error(con, status, sub->dest);
7914     return;
7915   }
7916 
7917   exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
7918 
7919  /*
7920   * Copy the subscription attributes to the response using the
7921   * requested-attributes attribute that may be provided by the client.
7922   */
7923 
7924   ra = create_requested_array(con->request);
7925 
7926   copy_subscription_attrs(con, sub, ra, exclude);
7927 
7928   cupsArrayDelete(ra);
7929 
7930   con->response->request.status.status_code = IPP_OK;
7931 }
7932 
7933 
7934 /*
7935  * 'get_subscriptions()' - Get subscriptions.
7936  */
7937 
7938 static void
get_subscriptions(cupsd_client_t * con,ipp_attribute_t * uri)7939 get_subscriptions(cupsd_client_t  *con,	/* I - Client connection */
7940                   ipp_attribute_t *uri)	/* I - Printer/job URI */
7941 {
7942   http_status_t		status;		/* Policy status */
7943   int			count;		/* Number of subscriptions */
7944   int			limit;		/* Limit */
7945   cupsd_subscription_t	*sub;		/* Subscription */
7946   cups_array_t		*ra;		/* Requested attributes array */
7947   ipp_attribute_t	*attr;		/* Attribute */
7948   cups_ptype_t		dtype;		/* Destination type (printer/class) */
7949   char			scheme[HTTP_MAX_URI],
7950 					/* Scheme portion of URI */
7951 			username[HTTP_MAX_URI],
7952 					/* Username portion of URI */
7953 			host[HTTP_MAX_URI],
7954 					/* Host portion of URI */
7955 			resource[HTTP_MAX_URI];
7956 					/* Resource portion of URI */
7957   int			port;		/* Port portion of URI */
7958   cupsd_job_t		*job;		/* Job pointer */
7959   cupsd_printer_t	*printer;	/* Printer */
7960   cupsd_policy_t	*policy;	/* Policy */
7961   cups_array_t		*exclude;	/* Private attributes array */
7962 
7963 
7964   cupsdLogMessage(CUPSD_LOG_DEBUG2,
7965                   "get_subscriptions(con=%p[%d], uri=%s)",
7966                   (void *)con, con->number, uri->values[0].string.text);
7967 
7968  /*
7969   * Is the destination valid?
7970   */
7971 
7972   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
7973                   sizeof(scheme), username, sizeof(username), host,
7974 		  sizeof(host), &port, resource, sizeof(resource));
7975 
7976   if (!strcmp(resource, "/") ||
7977       (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
7978       (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
7979       (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
7980   {
7981     printer = NULL;
7982     job     = NULL;
7983   }
7984   else if (!strncmp(resource, "/jobs/", 6) && resource[6])
7985   {
7986     int job_id = atoi(resource + 6);
7987     printer = NULL;
7988     job     = cupsdFindJob(job_id);
7989 
7990     if (!job)
7991     {
7992       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
7993                       job_id);
7994       return;
7995     }
7996   }
7997   else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
7998   {
7999    /*
8000     * Bad URI...
8001     */
8002 
8003     send_ipp_status(con, IPP_NOT_FOUND,
8004                     _("The printer or class does not exist."));
8005     return;
8006   }
8007   else if ((attr = ippFindAttribute(con->request, "notify-job-id",
8008                                     IPP_TAG_INTEGER)) != NULL)
8009   {
8010     job = cupsdFindJob(attr->values[0].integer);
8011 
8012     if (!job)
8013     {
8014       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
8015                       attr->values[0].integer);
8016       return;
8017     }
8018   }
8019   else
8020     job = NULL;
8021 
8022  /*
8023   * Check policy...
8024   */
8025 
8026   if (printer)
8027     policy = printer->op_policy_ptr;
8028   else
8029     policy = DefaultPolicyPtr;
8030 
8031   if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
8032   {
8033     send_http_error(con, status, printer);
8034     return;
8035   }
8036 
8037  /*
8038   * Expire subscriptions as needed...
8039   */
8040 
8041   cupsdExpireSubscriptions(NULL, NULL);
8042 
8043  /*
8044   * Copy the subscription attributes to the response using the
8045   * requested-attributes attribute that may be provided by the client.
8046   */
8047 
8048   ra = create_requested_array(con->request);
8049 
8050   if ((attr = ippFindAttribute(con->request, "limit",
8051                                IPP_TAG_INTEGER)) != NULL)
8052     limit = attr->values[0].integer;
8053   else
8054     limit = 0;
8055 
8056  /*
8057   * See if we only want to see subscriptions for a specific user...
8058   */
8059 
8060   if ((attr = ippFindAttribute(con->request, "my-subscriptions",
8061                                IPP_TAG_BOOLEAN)) != NULL &&
8062       attr->values[0].boolean)
8063     strlcpy(username, get_username(con), sizeof(username));
8064   else
8065     username[0] = '\0';
8066 
8067   for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
8068        sub;
8069        sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
8070     if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
8071         (!username[0] || !_cups_strcasecmp(username, sub->owner)))
8072     {
8073       ippAddSeparator(con->response);
8074 
8075       exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
8076 						 policy, con, sub->dest,
8077 						 sub->owner);
8078 
8079       copy_subscription_attrs(con, sub, ra, exclude);
8080 
8081       count ++;
8082       if (limit && count >= limit)
8083         break;
8084     }
8085 
8086   cupsArrayDelete(ra);
8087 
8088   if (count)
8089     con->response->request.status.status_code = IPP_OK;
8090   else
8091     send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
8092 }
8093 
8094 
8095 /*
8096  * 'get_username()' - Get the username associated with a request.
8097  */
8098 
8099 static const char *			/* O - Username */
get_username(cupsd_client_t * con)8100 get_username(cupsd_client_t *con)	/* I - Connection */
8101 {
8102   ipp_attribute_t	*attr;		/* Attribute */
8103 
8104 
8105   if (con->username[0])
8106     return (con->username);
8107   else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
8108                                     IPP_TAG_NAME)) != NULL)
8109     return (attr->values[0].string.text);
8110   else
8111     return ("anonymous");
8112 }
8113 
8114 
8115 /*
8116  * 'hold_job()' - Hold a print job.
8117  */
8118 
8119 static void
hold_job(cupsd_client_t * con,ipp_attribute_t * uri)8120 hold_job(cupsd_client_t  *con,		/* I - Client connection */
8121          ipp_attribute_t *uri)		/* I - Job or Printer URI */
8122 {
8123   ipp_attribute_t *attr;		/* Current job-hold-until */
8124   const char	*when;			/* New value */
8125   int		jobid;			/* Job ID */
8126   char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
8127 		username[HTTP_MAX_URI],	/* Username portion of URI */
8128 		host[HTTP_MAX_URI],	/* Host portion of URI */
8129 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
8130   int		port;			/* Port portion of URI */
8131   cupsd_job_t	*job;			/* Job information */
8132 
8133 
8134   cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", (void *)con, con->number,
8135                   uri->values[0].string.text);
8136 
8137  /*
8138   * See if we have a job URI or a printer URI...
8139   */
8140 
8141   if (!strcmp(uri->name, "printer-uri"))
8142   {
8143    /*
8144     * Got a printer URI; see if we also have a job-id attribute...
8145     */
8146 
8147     if ((attr = ippFindAttribute(con->request, "job-id",
8148                                  IPP_TAG_INTEGER)) == NULL)
8149     {
8150       send_ipp_status(con, IPP_BAD_REQUEST,
8151                       _("Got a printer-uri attribute but no job-id."));
8152       return;
8153     }
8154 
8155     jobid = attr->values[0].integer;
8156   }
8157   else
8158   {
8159    /*
8160     * Got a job URI; parse it to get the job ID...
8161     */
8162 
8163     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8164                     sizeof(scheme), username, sizeof(username), host,
8165 		    sizeof(host), &port, resource, sizeof(resource));
8166 
8167     if (strncmp(resource, "/jobs/", 6))
8168     {
8169      /*
8170       * Not a valid URI!
8171       */
8172 
8173       send_ipp_status(con, IPP_BAD_REQUEST,
8174                       _("Bad job-uri \"%s\"."),
8175                       uri->values[0].string.text);
8176       return;
8177     }
8178 
8179     jobid = atoi(resource + 6);
8180   }
8181 
8182  /*
8183   * See if the job exists...
8184   */
8185 
8186   if ((job = cupsdFindJob(jobid)) == NULL)
8187   {
8188    /*
8189     * Nope - return a "not found" error...
8190     */
8191 
8192     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8193     return;
8194   }
8195 
8196  /*
8197   * See if the job is owned by the requesting user...
8198   */
8199 
8200   if (!validate_user(job, con, job->username, username, sizeof(username)))
8201   {
8202     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8203 		    cupsdFindDest(job->dest));
8204     return;
8205   }
8206 
8207  /*
8208   * See if the job is in a state that allows holding...
8209   */
8210 
8211   if (job->state_value > IPP_JOB_STOPPED)
8212   {
8213    /*
8214     * Return a "not-possible" error...
8215     */
8216 
8217     send_ipp_status(con, IPP_NOT_POSSIBLE,
8218 		    _("Job #%d is finished and cannot be altered."),
8219 		    job->id);
8220     return;
8221   }
8222 
8223  /*
8224   * Hold the job and return...
8225   */
8226 
8227   if ((attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
8228   {
8229     if ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || !ippValidateAttribute(attr))
8230     {
8231       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
8232       ippCopyAttribute(con->response, attr, 0);
8233       return;
8234     }
8235 
8236     when = ippGetString(attr, 0, NULL);
8237 
8238     cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
8239 		  "Job job-hold-until value changed by user.");
8240   }
8241   else
8242     when = "indefinite";
8243 
8244   cupsdSetJobHoldUntil(job, when, 1);
8245   cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".",
8246                    username);
8247 
8248   con->response->request.status.status_code = IPP_OK;
8249 }
8250 
8251 
8252 /*
8253  * 'hold_new_jobs()' - Hold pending/new jobs on a printer or class.
8254  */
8255 
8256 static void
hold_new_jobs(cupsd_client_t * con,ipp_attribute_t * uri)8257 hold_new_jobs(cupsd_client_t  *con,	/* I - Connection */
8258               ipp_attribute_t *uri)	/* I - Printer URI */
8259 {
8260   http_status_t		status;		/* Policy status */
8261   cups_ptype_t		dtype;		/* Destination type (printer/class) */
8262   cupsd_printer_t	*printer;	/* Printer data */
8263 
8264 
8265   cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", (void *)con,
8266                   con->number, uri->values[0].string.text);
8267 
8268  /*
8269   * Is the destination valid?
8270   */
8271 
8272   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
8273   {
8274    /*
8275     * Bad URI...
8276     */
8277 
8278     send_ipp_status(con, IPP_NOT_FOUND,
8279                     _("The printer or class does not exist."));
8280     return;
8281   }
8282 
8283  /*
8284   * Check policy...
8285   */
8286 
8287   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
8288   {
8289     send_http_error(con, status, printer);
8290     return;
8291   }
8292 
8293  /*
8294   * Hold pending/new jobs sent to the printer...
8295   */
8296 
8297   printer->holding_new_jobs = 1;
8298 
8299   cupsdSetPrinterReasons(printer, "+hold-new-jobs");
8300 
8301   if (dtype & CUPS_PRINTER_CLASS)
8302     cupsdLogMessage(CUPSD_LOG_INFO,
8303                     "Class \"%s\" now holding pending/new jobs (\"%s\").",
8304                     printer->name, get_username(con));
8305   else
8306     cupsdLogMessage(CUPSD_LOG_INFO,
8307                     "Printer \"%s\" now holding pending/new jobs (\"%s\").",
8308                     printer->name, get_username(con));
8309 
8310  /*
8311   * Everything was ok, so return OK status...
8312   */
8313 
8314   con->response->request.status.status_code = IPP_OK;
8315 }
8316 
8317 
8318 /*
8319  * 'move_job()' - Move a job to a new destination.
8320  */
8321 
8322 static void
move_job(cupsd_client_t * con,ipp_attribute_t * uri)8323 move_job(cupsd_client_t  *con,		/* I - Client connection */
8324 	 ipp_attribute_t *uri)		/* I - Job URI */
8325 {
8326   http_status_t	status;			/* Policy status */
8327   ipp_attribute_t *attr;		/* Current attribute */
8328   int		jobid;			/* Job ID */
8329   cupsd_job_t	*job;			/* Current job */
8330   const char	*src;			/* Source printer/class */
8331   cups_ptype_t	stype,			/* Source type (printer or class) */
8332 		dtype;			/* Destination type (printer/class) */
8333   char		scheme[HTTP_MAX_URI],	/* Scheme portion of URI */
8334 		username[HTTP_MAX_URI],	/* Username portion of URI */
8335 		host[HTTP_MAX_URI],	/* Host portion of URI */
8336 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
8337   int		port;			/* Port portion of URI */
8338   cupsd_printer_t *sprinter,		/* Source printer */
8339 		*dprinter;		/* Destination printer */
8340 
8341 
8342   cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", (void *)con, con->number,
8343                   uri->values[0].string.text);
8344 
8345  /*
8346   * Get the new printer or class...
8347   */
8348 
8349   if ((attr = ippFindAttribute(con->request, "job-printer-uri",
8350                                IPP_TAG_URI)) == NULL)
8351   {
8352    /*
8353     * Need job-printer-uri...
8354     */
8355 
8356     send_ipp_status(con, IPP_BAD_REQUEST,
8357                     _("job-printer-uri attribute missing."));
8358     return;
8359   }
8360 
8361   if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
8362   {
8363    /*
8364     * Bad URI...
8365     */
8366 
8367     send_ipp_status(con, IPP_NOT_FOUND,
8368                     _("The printer or class does not exist."));
8369     return;
8370   }
8371 
8372  /*
8373   * See if we have a job URI or a printer URI...
8374   */
8375 
8376   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
8377                   sizeof(scheme), username, sizeof(username), host,
8378 		  sizeof(host), &port, resource, sizeof(resource));
8379 
8380   if (!strcmp(uri->name, "printer-uri"))
8381   {
8382    /*
8383     * Got a printer URI; see if we also have a job-id attribute...
8384     */
8385 
8386     if ((attr = ippFindAttribute(con->request, "job-id",
8387                                  IPP_TAG_INTEGER)) == NULL)
8388     {
8389      /*
8390       * Move all jobs...
8391       */
8392 
8393       if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
8394                                    &sprinter)) == NULL)
8395       {
8396        /*
8397 	* Bad URI...
8398 	*/
8399 
8400 	send_ipp_status(con, IPP_NOT_FOUND,
8401                 	_("The printer or class does not exist."));
8402 	return;
8403       }
8404 
8405       job = NULL;
8406     }
8407     else
8408     {
8409      /*
8410       * Otherwise, just move a single job...
8411       */
8412 
8413       if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
8414       {
8415        /*
8416 	* Nope - return a "not found" error...
8417 	*/
8418 
8419 	send_ipp_status(con, IPP_NOT_FOUND,
8420                 	_("Job #%d does not exist."), attr->values[0].integer);
8421 	return;
8422       }
8423       else
8424       {
8425        /*
8426         * Job found, initialize source pointers...
8427 	*/
8428 
8429 	src      = NULL;
8430 	sprinter = NULL;
8431       }
8432     }
8433   }
8434   else
8435   {
8436    /*
8437     * Got a job URI; parse it to get the job ID...
8438     */
8439 
8440     if (strncmp(resource, "/jobs/", 6))
8441     {
8442      /*
8443       * Not a valid URI!
8444       */
8445 
8446       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
8447                       uri->values[0].string.text);
8448       return;
8449     }
8450 
8451    /*
8452     * See if the job exists...
8453     */
8454 
8455     jobid = atoi(resource + 6);
8456 
8457     if ((job = cupsdFindJob(jobid)) == NULL)
8458     {
8459      /*
8460       * Nope - return a "not found" error...
8461       */
8462 
8463       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
8464       return;
8465     }
8466     else
8467     {
8468      /*
8469       * Job found, initialize source pointers...
8470       */
8471 
8472       src      = NULL;
8473       sprinter = NULL;
8474     }
8475   }
8476 
8477  /*
8478   * Check the policy of the destination printer...
8479   */
8480 
8481   if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
8482                                  job ? job->username : NULL)) != HTTP_OK)
8483   {
8484     send_http_error(con, status, dprinter);
8485     return;
8486   }
8487 
8488  /*
8489   * Now move the job or jobs...
8490   */
8491 
8492   if (job)
8493   {
8494    /*
8495     * See if the job has been completed...
8496     */
8497 
8498     if (job->state_value > IPP_JOB_STOPPED)
8499     {
8500      /*
8501       * Return a "not-possible" error...
8502       */
8503 
8504       send_ipp_status(con, IPP_NOT_POSSIBLE,
8505                       _("Job #%d is finished and cannot be altered."),
8506 		      job->id);
8507       return;
8508     }
8509 
8510    /*
8511     * See if the job is owned by the requesting user...
8512     */
8513 
8514     if (!validate_user(job, con, job->username, username, sizeof(username)))
8515     {
8516       send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
8517                       cupsdFindDest(job->dest));
8518       return;
8519     }
8520 
8521    /*
8522     * Move the job to a different printer or class...
8523     */
8524 
8525     cupsdMoveJob(job, dprinter);
8526   }
8527   else
8528   {
8529    /*
8530     * Got the source printer, now look through the jobs...
8531     */
8532 
8533     for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
8534          job;
8535 	 job = (cupsd_job_t *)cupsArrayNext(Jobs))
8536     {
8537      /*
8538       * See if the job is pointing at the source printer or has not been
8539       * completed...
8540       */
8541 
8542       if (_cups_strcasecmp(job->dest, src) ||
8543           job->state_value > IPP_JOB_STOPPED)
8544 	continue;
8545 
8546      /*
8547       * See if the job can be moved by the requesting user...
8548       */
8549 
8550       if (!validate_user(job, con, job->username, username, sizeof(username)))
8551         continue;
8552 
8553      /*
8554       * Move the job to a different printer or class...
8555       */
8556 
8557       cupsdMoveJob(job, dprinter);
8558     }
8559   }
8560 
8561  /*
8562   * Start jobs if possible...
8563   */
8564 
8565   cupsdCheckJobs();
8566 
8567  /*
8568   * Return with "everything is OK" status...
8569   */
8570 
8571   con->response->request.status.status_code = IPP_OK;
8572 }
8573 
8574 
8575 /*
8576  * 'ppd_parse_line()' - Parse a PPD default line.
8577  */
8578 
8579 static int				/* O - 0 on success, -1 on failure */
ppd_parse_line(const char * line,char * option,int olen,char * choice,int clen)8580 ppd_parse_line(const char *line,	/* I - Line */
8581                char       *option,	/* O - Option name */
8582 	       int        olen,		/* I - Size of option name */
8583                char       *choice,	/* O - Choice name */
8584 	       int        clen)		/* I - Size of choice name */
8585 {
8586  /*
8587   * Verify this is a default option line...
8588   */
8589 
8590   if (strncmp(line, "*Default", 8))
8591     return (-1);
8592 
8593  /*
8594   * Read the option name...
8595   */
8596 
8597   for (line += 8, olen --;
8598        *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8599        line ++)
8600     if (olen > 0)
8601     {
8602       *option++ = *line;
8603       olen --;
8604     }
8605 
8606   *option = '\0';
8607 
8608  /*
8609   * Skip everything else up to the colon (:)...
8610   */
8611 
8612   while (*line && *line != ':')
8613     line ++;
8614 
8615   if (!*line)
8616     return (-1);
8617 
8618   line ++;
8619 
8620  /*
8621   * Now grab the option choice, skipping leading whitespace...
8622   */
8623 
8624   while (isspace(*line & 255))
8625     line ++;
8626 
8627   for (clen --;
8628        *line > ' ' && *line < 0x7f && *line != ':' && *line != '/';
8629        line ++)
8630     if (clen > 0)
8631     {
8632       *choice++ = *line;
8633       clen --;
8634     }
8635 
8636   *choice = '\0';
8637 
8638  /*
8639   * Return with no errors...
8640   */
8641 
8642   return (0);
8643 }
8644 
8645 
8646 /*
8647  * 'print_job()' - Print a file to a printer or class.
8648  */
8649 
8650 static void
print_job(cupsd_client_t * con,ipp_attribute_t * uri)8651 print_job(cupsd_client_t  *con,		/* I - Client connection */
8652 	  ipp_attribute_t *uri)		/* I - Printer URI */
8653 {
8654   ipp_attribute_t *attr;		/* Current attribute */
8655   ipp_attribute_t *doc_name;		/* document-name attribute */
8656   ipp_attribute_t *format;		/* Document-format attribute */
8657   const char	*default_format;	/* document-format-default value */
8658   cupsd_job_t	*job;			/* New job */
8659   char		filename[1024];		/* Job filename */
8660   mime_type_t	*filetype;		/* Type of file */
8661   char		super[MIME_MAX_SUPER],	/* Supertype of file */
8662 		type[MIME_MAX_TYPE],	/* Subtype of file */
8663 		mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
8664 					/* Textual name of mime type */
8665   cupsd_printer_t *printer;		/* Printer data */
8666   struct stat	fileinfo;		/* File information */
8667   int		kbytes;			/* Size of file */
8668   int		compression;		/* Document compression */
8669 
8670 
8671   cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", (void *)con, con->number,
8672                   uri->values[0].string.text);
8673 
8674  /*
8675   * Validate print file attributes, for now just document-format and
8676   * compression (CUPS only supports "none" and "gzip")...
8677   */
8678 
8679   compression = CUPS_FILE_NONE;
8680 
8681   if ((attr = ippFindAttribute(con->request, "compression",
8682                                IPP_TAG_KEYWORD)) != NULL)
8683   {
8684     if (strcmp(attr->values[0].string.text, "none")
8685 #ifdef HAVE_LIBZ
8686         && strcmp(attr->values[0].string.text, "gzip")
8687 #endif /* HAVE_LIBZ */
8688       )
8689     {
8690       send_ipp_status(con, IPP_ATTRIBUTES,
8691                       _("Unsupported compression \"%s\"."),
8692         	      attr->values[0].string.text);
8693       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
8694 	           "compression", NULL, attr->values[0].string.text);
8695       return;
8696     }
8697 
8698 #ifdef HAVE_LIBZ
8699     if (!strcmp(attr->values[0].string.text, "gzip"))
8700       compression = CUPS_FILE_GZIP;
8701 #endif /* HAVE_LIBZ */
8702   }
8703 
8704  /*
8705   * Do we have a file to print?
8706   */
8707 
8708   if (!con->filename)
8709   {
8710     send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
8711     return;
8712   }
8713 
8714  /*
8715   * Is the destination valid?
8716   */
8717 
8718   if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
8719   {
8720    /*
8721     * Bad URI...
8722     */
8723 
8724     send_ipp_status(con, IPP_NOT_FOUND,
8725                     _("The printer or class does not exist."));
8726     return;
8727   }
8728 
8729  /*
8730   * Is it a format we support?
8731   */
8732 
8733   doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
8734   if (doc_name)
8735     ippSetName(con->request, &doc_name, "document-name-supplied");
8736 
8737   if ((format = ippFindAttribute(con->request, "document-format",
8738                                  IPP_TAG_MIMETYPE)) != NULL)
8739   {
8740    /*
8741     * Grab format from client...
8742     */
8743 
8744     if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]", super,
8745                type) != 2)
8746     {
8747       send_ipp_status(con, IPP_BAD_REQUEST,
8748                       _("Bad document-format \"%s\"."),
8749 		      format->values[0].string.text);
8750       return;
8751     }
8752 
8753     ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, ippGetString(format, 0, NULL));
8754   }
8755   else if ((default_format = cupsGetOption("document-format",
8756                                            printer->num_options,
8757 					   printer->options)) != NULL)
8758   {
8759    /*
8760     * Use default document format...
8761     */
8762 
8763     if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
8764     {
8765       send_ipp_status(con, IPP_BAD_REQUEST,
8766                       _("Bad document-format \"%s\"."),
8767 		      default_format);
8768       return;
8769     }
8770   }
8771   else
8772   {
8773    /*
8774     * Auto-type it!
8775     */
8776 
8777     strlcpy(super, "application", sizeof(super));
8778     strlcpy(type, "octet-stream", sizeof(type));
8779   }
8780 
8781   _cupsRWLockRead(&MimeDatabase->lock);
8782 
8783   if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
8784   {
8785    /*
8786     * Auto-type the file...
8787     */
8788 
8789     cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job ???] Auto-typing file...");
8790 
8791 
8792     filetype = mimeFileType(MimeDatabase, con->filename,
8793                             doc_name ? doc_name->values[0].string.text : NULL,
8794 			    &compression);
8795 
8796     if (!filetype)
8797       filetype = mimeType(MimeDatabase, super, type);
8798 
8799     cupsdLogMessage(CUPSD_LOG_INFO, "[Job ???] Request file type is %s/%s.",
8800 		    filetype->super, filetype->type);
8801 
8802     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type);
8803     ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, mimetype);
8804   }
8805   else
8806     filetype = mimeType(MimeDatabase, super, type);
8807 
8808   _cupsRWUnlock(&MimeDatabase->lock);
8809 
8810   if (filetype &&
8811       (!format ||
8812        (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
8813   {
8814    /*
8815     * Replace the document-format attribute value with the auto-typed or
8816     * default one.
8817     */
8818 
8819     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
8820              filetype->type);
8821 
8822     if (format)
8823       ippSetString(con->request, &format, 0, mimetype);
8824     else
8825       ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
8826 	           "document-format", NULL, mimetype);
8827   }
8828   else if (!filetype)
8829   {
8830     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
8831                     _("Unsupported document-format \"%s\"."),
8832 		    format ? format->values[0].string.text :
8833 			     "application/octet-stream");
8834     cupsdLogMessage(CUPSD_LOG_INFO,
8835                     "Hint: Do you have the raw file printing rules enabled?");
8836 
8837     if (format)
8838       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
8839                    "document-format", NULL, format->values[0].string.text);
8840 
8841     return;
8842   }
8843 
8844  /*
8845   * Read any embedded job ticket info from PS files...
8846   */
8847 
8848   if (!_cups_strcasecmp(filetype->super, "application") &&
8849       (!_cups_strcasecmp(filetype->type, "postscript") ||
8850        !_cups_strcasecmp(filetype->type, "pdf")))
8851     read_job_ticket(con);
8852 
8853  /*
8854   * Create the job object...
8855   */
8856 
8857   if ((job = add_job(con, printer, filetype)) == NULL)
8858     return;
8859 
8860  /*
8861   * Update quota data...
8862   */
8863 
8864   if (stat(con->filename, &fileinfo))
8865     kbytes = 0;
8866   else
8867     kbytes = (fileinfo.st_size + 1023) / 1024;
8868 
8869   cupsdUpdateQuota(printer, job->username, 0, kbytes);
8870 
8871   job->koctets += kbytes;
8872 
8873   if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
8874     attr->values[0].integer += kbytes;
8875 
8876  /*
8877   * Add the job file...
8878   */
8879 
8880   if (add_file(con, job, filetype, compression))
8881     return;
8882 
8883   snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
8884   if (rename(con->filename, filename))
8885   {
8886     cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to rename job document file \"%s\": %s", filename, strerror(errno));
8887 
8888     send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to rename job document file."));
8889     return;
8890   }
8891 
8892   cupsdClearString(&con->filename);
8893 
8894  /*
8895   * See if we need to add the ending sheet...
8896   */
8897 
8898   if (cupsdTimeoutJob(job))
8899     return;
8900 
8901  /*
8902   * Log and save the job...
8903   */
8904 
8905   cupsdLogJob(job, CUPSD_LOG_INFO,
8906 	      "File of type %s/%s queued by \"%s\".",
8907 	      filetype->super, filetype->type, job->username);
8908   cupsdLogJob(job, CUPSD_LOG_DEBUG, "hold_until=%d", (int)job->hold_until);
8909   cupsdLogJob(job, CUPSD_LOG_INFO, "Queued on \"%s\" by \"%s\".",
8910 	      job->dest, job->username);
8911 
8912  /*
8913   * Start the job if possible...
8914   */
8915 
8916   cupsdCheckJobs();
8917 }
8918 
8919 
8920 /*
8921  * 'read_job_ticket()' - Read a job ticket embedded in a print file.
8922  *
8923  * This function only gets called when printing a single PDF or PostScript
8924  * file using the Print-Job operation.  It doesn't work for Create-Job +
8925  * Send-File, since the job attributes need to be set at job creation
8926  * time for banners to work.  The embedded job ticket stuff is here
8927  * primarily to allow the Windows printer driver for CUPS to pass in JCL
8928  * options and IPP attributes which otherwise would be lost.
8929  *
8930  * The format of a job ticket is simple:
8931  *
8932  *     %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
8933  *
8934  *     %cupsJobTicket: attr1=value1
8935  *     %cupsJobTicket: attr2=value2
8936  *     ...
8937  *     %cupsJobTicket: attrN=valueN
8938  *
8939  * Job ticket lines must appear immediately after the first line that
8940  * specifies PostScript (%!PS-Adobe-3.0) or PDF (%PDF) format, and CUPS
8941  * stops looking for job ticket info when it finds a line that does not begin
8942  * with "%cupsJobTicket:".
8943  *
8944  * The maximum length of a job ticket line, including the prefix, is
8945  * 255 characters to conform with the Adobe DSC.
8946  *
8947  * Read-only attributes are rejected with a notice to the error log in
8948  * case a malicious user tries anything.  Since the job ticket is read
8949  * prior to attribute validation in print_job(), job ticket attributes
8950  * will go through the same validation as IPP attributes...
8951  */
8952 
8953 static void
read_job_ticket(cupsd_client_t * con)8954 read_job_ticket(cupsd_client_t *con)	/* I - Client connection */
8955 {
8956   cups_file_t		*fp;		/* File to read from */
8957   char			line[256];	/* Line data */
8958   int			num_options;	/* Number of options */
8959   cups_option_t		*options;	/* Options */
8960   ipp_t			*ticket;	/* New attributes */
8961   ipp_attribute_t	*attr,		/* Current attribute */
8962 			*attr2,		/* Job attribute */
8963 			*prev2;		/* Previous job attribute */
8964 
8965 
8966  /*
8967   * First open the print file...
8968   */
8969 
8970   if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
8971   {
8972     cupsdLogMessage(CUPSD_LOG_ERROR,
8973                     "Unable to open print file for job ticket - %s",
8974                     strerror(errno));
8975     return;
8976   }
8977 
8978  /*
8979   * Skip the first line...
8980   */
8981 
8982   if (cupsFileGets(fp, line, sizeof(line)) == NULL)
8983   {
8984     cupsdLogMessage(CUPSD_LOG_ERROR,
8985                     "Unable to read from print file for job ticket - %s",
8986                     strerror(errno));
8987     cupsFileClose(fp);
8988     return;
8989   }
8990 
8991   if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
8992   {
8993    /*
8994     * Not a DSC-compliant file, so no job ticket info will be available...
8995     */
8996 
8997     cupsFileClose(fp);
8998     return;
8999   }
9000 
9001  /*
9002   * Read job ticket info from the file...
9003   */
9004 
9005   num_options = 0;
9006   options     = NULL;
9007 
9008   while (cupsFileGets(fp, line, sizeof(line)))
9009   {
9010    /*
9011     * Stop at the first non-ticket line...
9012     */
9013 
9014     if (strncmp(line, "%cupsJobTicket:", 15))
9015       break;
9016 
9017    /*
9018     * Add the options to the option array...
9019     */
9020 
9021     num_options = cupsParseOptions(line + 15, num_options, &options);
9022   }
9023 
9024  /*
9025   * Done with the file; see if we have any options...
9026   */
9027 
9028   cupsFileClose(fp);
9029 
9030   if (num_options == 0)
9031     return;
9032 
9033  /*
9034   * OK, convert the options to an attribute list, and apply them to
9035   * the request...
9036   */
9037 
9038   ticket = ippNew();
9039   cupsEncodeOptions(ticket, num_options, options);
9040 
9041  /*
9042   * See what the user wants to change.
9043   */
9044 
9045   for (attr = ticket->attrs; attr; attr = attr->next)
9046   {
9047     if (attr->group_tag != IPP_TAG_JOB || !attr->name)
9048       continue;
9049 
9050     if (!strncmp(attr->name, "date-time-at-", 13) ||
9051         !strcmp(attr->name, "job-impressions-completed") ||
9052 	!strcmp(attr->name, "job-media-sheets-completed") ||
9053 	!strncmp(attr->name, "job-k-octets", 12) ||
9054 	!strcmp(attr->name, "job-id") ||
9055 	!strcmp(attr->name, "job-originating-host-name") ||
9056         !strcmp(attr->name, "job-originating-user-name") ||
9057 	!strcmp(attr->name, "job-pages-completed") ||
9058 	!strcmp(attr->name, "job-printer-uri") ||
9059 	!strncmp(attr->name, "job-state", 9) ||
9060 	!strcmp(attr->name, "job-uri") ||
9061 	!strncmp(attr->name, "time-at-", 8))
9062       continue; /* Read-only attrs */
9063 
9064     if ((attr2 = ippFindAttribute(con->request, attr->name,
9065                                   IPP_TAG_ZERO)) != NULL)
9066     {
9067      /*
9068       * Some other value; first free the old value...
9069       */
9070 
9071       if (con->request->attrs == attr2)
9072       {
9073 	con->request->attrs = attr2->next;
9074 	prev2               = NULL;
9075       }
9076       else
9077       {
9078 	for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
9079 	  if (prev2->next == attr2)
9080 	  {
9081 	    prev2->next = attr2->next;
9082 	    break;
9083 	  }
9084       }
9085 
9086       if (con->request->last == attr2)
9087         con->request->last = prev2;
9088 
9089       ippDeleteAttribute(NULL, attr2);
9090     }
9091 
9092    /*
9093     * Add new option by copying it...
9094     */
9095 
9096     ippCopyAttribute(con->request, attr, 0);
9097   }
9098 
9099  /*
9100   * Then free the attribute list and option array...
9101   */
9102 
9103   ippDelete(ticket);
9104   cupsFreeOptions(num_options, options);
9105 }
9106 
9107 
9108 /*
9109  * 'reject_jobs()' - Reject print jobs to a printer.
9110  */
9111 
9112 static void
reject_jobs(cupsd_client_t * con,ipp_attribute_t * uri)9113 reject_jobs(cupsd_client_t  *con,	/* I - Client connection */
9114             ipp_attribute_t *uri)	/* I - Printer or class URI */
9115 {
9116   http_status_t	status;			/* Policy status */
9117   cups_ptype_t	dtype;			/* Destination type (printer/class) */
9118   cupsd_printer_t *printer;		/* Printer data */
9119   ipp_attribute_t *attr;		/* printer-state-message text */
9120 
9121 
9122   cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", (void *)con,
9123                   con->number, uri->values[0].string.text);
9124 
9125  /*
9126   * Is the destination valid?
9127   */
9128 
9129   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9130   {
9131    /*
9132     * Bad URI...
9133     */
9134 
9135     send_ipp_status(con, IPP_NOT_FOUND,
9136                     _("The printer or class does not exist."));
9137     return;
9138   }
9139 
9140  /*
9141   * Check policy...
9142   */
9143 
9144   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9145   {
9146     send_http_error(con, status, printer);
9147     return;
9148   }
9149 
9150  /*
9151   * Reject jobs sent to the printer...
9152   */
9153 
9154   printer->accepting = 0;
9155 
9156   if ((attr = ippFindAttribute(con->request, "printer-state-message",
9157                                IPP_TAG_TEXT)) == NULL)
9158     strlcpy(printer->state_message, "Rejecting Jobs",
9159             sizeof(printer->state_message));
9160   else
9161     strlcpy(printer->state_message, attr->values[0].string.text,
9162             sizeof(printer->state_message));
9163 
9164   cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
9165                 "No longer accepting jobs.");
9166 
9167   if (dtype & CUPS_PRINTER_CLASS)
9168   {
9169     cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
9170 
9171     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
9172                     printer->name, get_username(con));
9173   }
9174   else
9175   {
9176     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
9177 
9178     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
9179                     printer->name, get_username(con));
9180   }
9181 
9182  /*
9183   * Everything was ok, so return OK status...
9184   */
9185 
9186   con->response->request.status.status_code = IPP_OK;
9187 }
9188 
9189 
9190 /*
9191  * 'release_held_new_jobs()' - Release pending/new jobs on a printer or class.
9192  */
9193 
9194 static void
release_held_new_jobs(cupsd_client_t * con,ipp_attribute_t * uri)9195 release_held_new_jobs(
9196     cupsd_client_t  *con,		/* I - Connection */
9197     ipp_attribute_t *uri)		/* I - Printer URI */
9198 {
9199   http_status_t		status;		/* Policy status */
9200   cups_ptype_t		dtype;		/* Destination type (printer/class) */
9201   cupsd_printer_t	*printer;	/* Printer data */
9202 
9203 
9204   cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", (void *)con,
9205                   con->number, uri->values[0].string.text);
9206 
9207  /*
9208   * Is the destination valid?
9209   */
9210 
9211   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
9212   {
9213    /*
9214     * Bad URI...
9215     */
9216 
9217     send_ipp_status(con, IPP_NOT_FOUND,
9218                     _("The printer or class does not exist."));
9219     return;
9220   }
9221 
9222  /*
9223   * Check policy...
9224   */
9225 
9226   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
9227   {
9228     send_http_error(con, status, printer);
9229     return;
9230   }
9231 
9232  /*
9233   * Hold pending/new jobs sent to the printer...
9234   */
9235 
9236   printer->holding_new_jobs = 0;
9237 
9238   cupsdSetPrinterReasons(printer, "-hold-new-jobs");
9239 
9240   if (dtype & CUPS_PRINTER_CLASS)
9241     cupsdLogMessage(CUPSD_LOG_INFO,
9242                     "Class \"%s\" now printing pending/new jobs (\"%s\").",
9243                     printer->name, get_username(con));
9244   else
9245     cupsdLogMessage(CUPSD_LOG_INFO,
9246                     "Printer \"%s\" now printing pending/new jobs (\"%s\").",
9247                     printer->name, get_username(con));
9248 
9249   cupsdCheckJobs();
9250 
9251  /*
9252   * Everything was ok, so return OK status...
9253   */
9254 
9255   con->response->request.status.status_code = IPP_OK;
9256 }
9257 
9258 
9259 /*
9260  * 'release_job()' - Release a held print job.
9261  */
9262 
9263 static void
release_job(cupsd_client_t * con,ipp_attribute_t * uri)9264 release_job(cupsd_client_t  *con,	/* I - Client connection */
9265             ipp_attribute_t *uri)	/* I - Job or Printer URI */
9266 {
9267   ipp_attribute_t *attr;		/* Current attribute */
9268   int		jobid;			/* Job ID */
9269   char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
9270 		username[HTTP_MAX_URI],	/* Username portion of URI */
9271 		host[HTTP_MAX_URI],	/* Host portion of URI */
9272 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
9273   int		port;			/* Port portion of URI */
9274   cupsd_job_t	*job;			/* Job information */
9275 
9276 
9277   cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", (void *)con,
9278                   con->number, uri->values[0].string.text);
9279 
9280  /*
9281   * See if we have a job URI or a printer URI...
9282   */
9283 
9284   if (!strcmp(uri->name, "printer-uri"))
9285   {
9286    /*
9287     * Got a printer URI; see if we also have a job-id attribute...
9288     */
9289 
9290     if ((attr = ippFindAttribute(con->request, "job-id",
9291                                  IPP_TAG_INTEGER)) == NULL)
9292     {
9293       send_ipp_status(con, IPP_BAD_REQUEST,
9294                       _("Got a printer-uri attribute but no job-id."));
9295       return;
9296     }
9297 
9298     jobid = attr->values[0].integer;
9299   }
9300   else
9301   {
9302    /*
9303     * Got a job URI; parse it to get the job ID...
9304     */
9305 
9306     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9307                     sizeof(scheme), username, sizeof(username), host,
9308 		    sizeof(host), &port, resource, sizeof(resource));
9309 
9310     if (strncmp(resource, "/jobs/", 6))
9311     {
9312      /*
9313       * Not a valid URI!
9314       */
9315 
9316       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9317                       uri->values[0].string.text);
9318       return;
9319     }
9320 
9321     jobid = atoi(resource + 6);
9322   }
9323 
9324  /*
9325   * See if the job exists...
9326   */
9327 
9328   if ((job = cupsdFindJob(jobid)) == NULL)
9329   {
9330    /*
9331     * Nope - return a "not found" error...
9332     */
9333 
9334     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9335     return;
9336   }
9337 
9338  /*
9339   * See if job is "held"...
9340   */
9341 
9342   if (job->state_value != IPP_JOB_HELD)
9343   {
9344    /*
9345     * Nope - return a "not possible" error...
9346     */
9347 
9348     send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
9349     return;
9350   }
9351 
9352  /*
9353   * See if the job is owned by the requesting user...
9354   */
9355 
9356   if (!validate_user(job, con, job->username, username, sizeof(username)))
9357   {
9358     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9359                     cupsdFindDest(job->dest));
9360     return;
9361   }
9362 
9363  /*
9364   * Reset the job-hold-until value to "no-hold"...
9365   */
9366 
9367   if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
9368                                IPP_TAG_KEYWORD)) == NULL)
9369     attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
9370 
9371   if (attr)
9372   {
9373     ippSetValueTag(job->attrs, &attr, IPP_TAG_KEYWORD);
9374     ippSetString(job->attrs, &attr, 0, "no-hold");
9375 
9376     cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
9377                   "Job job-hold-until value changed by user.");
9378     ippSetString(job->attrs, &job->reasons, 0, "none");
9379   }
9380 
9381  /*
9382   * Release the job and return...
9383   */
9384 
9385   cupsdReleaseJob(job);
9386 
9387   cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
9388                 "Job released by user.");
9389 
9390   cupsdLogJob(job, CUPSD_LOG_INFO, "Released by \"%s\".", username);
9391 
9392   con->response->request.status.status_code = IPP_OK;
9393 
9394   cupsdCheckJobs();
9395 }
9396 
9397 
9398 /*
9399  * 'renew_subscription()' - Renew an existing subscription...
9400  */
9401 
9402 static void
renew_subscription(cupsd_client_t * con,int sub_id)9403 renew_subscription(
9404     cupsd_client_t *con,		/* I - Client connection */
9405     int            sub_id)		/* I - Subscription ID */
9406 {
9407   http_status_t		status;		/* Policy status */
9408   cupsd_subscription_t	*sub;		/* Subscription */
9409   ipp_attribute_t	*lease;		/* notify-lease-duration */
9410 
9411 
9412   cupsdLogMessage(CUPSD_LOG_DEBUG2,
9413                   "renew_subscription(con=%p[%d], sub_id=%d)",
9414                   (void *)con, con->number, sub_id);
9415 
9416  /*
9417   * Is the subscription ID valid?
9418   */
9419 
9420   if ((sub = cupsdFindSubscription(sub_id)) == NULL)
9421   {
9422    /*
9423     * Bad subscription ID...
9424     */
9425 
9426     send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
9427                     sub_id);
9428     return;
9429   }
9430 
9431   if (sub->job)
9432   {
9433    /*
9434     * Job subscriptions cannot be renewed...
9435     */
9436 
9437     send_ipp_status(con, IPP_NOT_POSSIBLE,
9438                     _("Job subscriptions cannot be renewed."));
9439     return;
9440   }
9441 
9442  /*
9443   * Check policy...
9444   */
9445 
9446   if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
9447                                              DefaultPolicyPtr,
9448                                  con, sub->owner)) != HTTP_OK)
9449   {
9450     send_http_error(con, status, sub->dest);
9451     return;
9452   }
9453 
9454  /*
9455   * Renew the subscription...
9456   */
9457 
9458   lease = ippFindAttribute(con->request, "notify-lease-duration",
9459                            IPP_TAG_INTEGER);
9460 
9461   sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
9462 
9463   if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
9464   {
9465     cupsdLogMessage(CUPSD_LOG_INFO,
9466                     "renew_subscription: Limiting notify-lease-duration to "
9467 		    "%d seconds.",
9468 		    MaxLeaseDuration);
9469     sub->lease = MaxLeaseDuration;
9470   }
9471 
9472   sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
9473 
9474   cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
9475 
9476   con->response->request.status.status_code = IPP_OK;
9477 
9478   ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
9479                 "notify-lease-duration", sub->lease);
9480 }
9481 
9482 
9483 /*
9484  * 'restart_job()' - Restart an old print job.
9485  */
9486 
9487 static void
restart_job(cupsd_client_t * con,ipp_attribute_t * uri)9488 restart_job(cupsd_client_t  *con,	/* I - Client connection */
9489             ipp_attribute_t *uri)	/* I - Job or Printer URI */
9490 {
9491   ipp_attribute_t *attr;		/* Current attribute */
9492   int		jobid;			/* Job ID */
9493   cupsd_job_t	*job;			/* Job information */
9494   char		scheme[HTTP_MAX_URI],	/* Method portion of URI */
9495 		username[HTTP_MAX_URI],	/* Username portion of URI */
9496 		host[HTTP_MAX_URI],	/* Host portion of URI */
9497 		resource[HTTP_MAX_URI];	/* Resource portion of URI */
9498   int		port;			/* Port portion of URI */
9499 
9500 
9501   cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", (void *)con,
9502                   con->number, uri->values[0].string.text);
9503 
9504  /*
9505   * See if we have a job URI or a printer URI...
9506   */
9507 
9508   if (!strcmp(uri->name, "printer-uri"))
9509   {
9510    /*
9511     * Got a printer URI; see if we also have a job-id attribute...
9512     */
9513 
9514     if ((attr = ippFindAttribute(con->request, "job-id",
9515                                  IPP_TAG_INTEGER)) == NULL)
9516     {
9517       send_ipp_status(con, IPP_BAD_REQUEST,
9518                       _("Got a printer-uri attribute but no job-id."));
9519       return;
9520     }
9521 
9522     jobid = attr->values[0].integer;
9523   }
9524   else
9525   {
9526    /*
9527     * Got a job URI; parse it to get the job ID...
9528     */
9529 
9530     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9531                     sizeof(scheme), username, sizeof(username), host,
9532 		    sizeof(host), &port, resource, sizeof(resource));
9533 
9534     if (strncmp(resource, "/jobs/", 6))
9535     {
9536      /*
9537       * Not a valid URI!
9538       */
9539 
9540       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9541                       uri->values[0].string.text);
9542       return;
9543     }
9544 
9545     jobid = atoi(resource + 6);
9546   }
9547 
9548  /*
9549   * See if the job exists...
9550   */
9551 
9552   if ((job = cupsdFindJob(jobid)) == NULL)
9553   {
9554    /*
9555     * Nope - return a "not found" error...
9556     */
9557 
9558     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9559     return;
9560   }
9561 
9562  /*
9563   * See if job is in any of the "completed" states...
9564   */
9565 
9566   if (job->state_value <= IPP_JOB_PROCESSING)
9567   {
9568    /*
9569     * Nope - return a "not possible" error...
9570     */
9571 
9572     send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
9573                     jobid);
9574     return;
9575   }
9576 
9577  /*
9578   * See if we have retained the job files...
9579   */
9580 
9581   cupsdLoadJob(job);
9582 
9583   if (!job->attrs || job->num_files == 0)
9584   {
9585    /*
9586     * Nope - return a "not possible" error...
9587     */
9588 
9589     send_ipp_status(con, IPP_NOT_POSSIBLE,
9590                     _("Job #%d cannot be restarted - no files."), jobid);
9591     return;
9592   }
9593 
9594  /*
9595   * See if the job is owned by the requesting user...
9596   */
9597 
9598   if (!validate_user(job, con, job->username, username, sizeof(username)))
9599   {
9600     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9601                     cupsdFindDest(job->dest));
9602     return;
9603   }
9604 
9605  /*
9606   * See if the job-hold-until attribute is specified...
9607   */
9608 
9609   if ((attr = ippFindAttribute(con->request, "job-hold-until",
9610                                IPP_TAG_KEYWORD)) == NULL)
9611     attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
9612 
9613   if (attr && strcmp(attr->values[0].string.text, "no-hold"))
9614   {
9615    /*
9616     * Return the job to a held state...
9617     */
9618 
9619     cupsdLogJob(job, CUPSD_LOG_DEBUG,
9620 		"Restarted by \"%s\" with job-hold-until=%s.",
9621                 username, attr->values[0].string.text);
9622     cupsdSetJobHoldUntil(job, attr->values[0].string.text, 1);
9623     cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
9624 		     "Job restarted by user with job-hold-until=%s",
9625 		     attr->values[0].string.text);
9626   }
9627   else
9628   {
9629    /*
9630     * Restart the job...
9631     */
9632 
9633     cupsdRestartJob(job);
9634     cupsdCheckJobs();
9635   }
9636 
9637   cupsdLogJob(job, CUPSD_LOG_INFO, "Restarted by \"%s\".", username);
9638 
9639   con->response->request.status.status_code = IPP_OK;
9640 }
9641 
9642 
9643 /*
9644  * 'save_auth_info()' - Save authentication information for a job.
9645  */
9646 
9647 static void
save_auth_info(cupsd_client_t * con,cupsd_job_t * job,ipp_attribute_t * auth_info)9648 save_auth_info(
9649     cupsd_client_t  *con,		/* I - Client connection */
9650     cupsd_job_t     *job,		/* I - Job */
9651     ipp_attribute_t *auth_info)		/* I - auth-info attribute, if any */
9652 {
9653   int			i;		/* Looping var */
9654   char			filename[1024];	/* Job authentication filename */
9655   cups_file_t		*fp;		/* Job authentication file */
9656   char			line[65536];	/* Line for file */
9657   cupsd_printer_t	*dest;		/* Destination printer/class */
9658 
9659 
9660  /*
9661   * This function saves the in-memory authentication information for
9662   * a job so that it can be used to authenticate with a remote host.
9663   * The information is stored in a file that is readable only by the
9664   * root user.  The fields are Base-64 encoded, each on a separate line,
9665   * followed by random number (up to 1024) of newlines to limit the
9666   * amount of information that is exposed.
9667   *
9668   * Because of the potential for exposing of authentication information,
9669   * this functionality is only enabled when running cupsd as root.
9670   *
9671   * This caching only works for the Basic and BasicDigest authentication
9672   * types.  Digest authentication cannot be cached this way, and in
9673   * the future Kerberos authentication may make all of this obsolete.
9674   *
9675   * Authentication information is saved whenever an authenticated
9676   * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
9677   * performed.
9678   *
9679   * This information is deleted after a job is completed or canceled,
9680   * so reprints may require subsequent re-authentication.
9681   */
9682 
9683   if (RunUser)
9684     return;
9685 
9686   if ((dest = cupsdFindDest(job->dest)) == NULL)
9687     return;
9688 
9689  /*
9690   * Create the authentication file and change permissions...
9691   */
9692 
9693   snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
9694   if ((fp = cupsFileOpen(filename, "w")) == NULL)
9695   {
9696     cupsdLogMessage(CUPSD_LOG_ERROR,
9697                     "Unable to save authentication info to \"%s\" - %s",
9698                     filename, strerror(errno));
9699     return;
9700   }
9701 
9702   fchown(cupsFileNumber(fp), 0, 0);
9703   fchmod(cupsFileNumber(fp), 0400);
9704 
9705   cupsFilePuts(fp, "CUPSD-AUTH-V3\n");
9706 
9707   for (i = 0;
9708        i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9709        i ++)
9710     cupsdClearString(job->auth_env + i);
9711 
9712   if (auth_info && auth_info->num_values == dest->num_auth_info_required)
9713   {
9714    /*
9715     * Write 1 to 3 auth values...
9716     */
9717 
9718     for (i = 0;
9719          i < auth_info->num_values &&
9720 	     i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
9721 	 i ++)
9722     {
9723       if (strcmp(dest->auth_info_required[i], "negotiate"))
9724       {
9725 	httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text, (int)strlen(auth_info->values[i].string.text));
9726 	cupsFilePutConf(fp, dest->auth_info_required[i], line);
9727       }
9728       else
9729 	cupsFilePutConf(fp, dest->auth_info_required[i],
9730 	                auth_info->values[i].string.text);
9731 
9732       if (!strcmp(dest->auth_info_required[i], "username"))
9733         cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
9734 	                auth_info->values[i].string.text);
9735       else if (!strcmp(dest->auth_info_required[i], "domain"))
9736         cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s",
9737 	                auth_info->values[i].string.text);
9738       else if (!strcmp(dest->auth_info_required[i], "password"))
9739         cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s",
9740 	                auth_info->values[i].string.text);
9741       else if (!strcmp(dest->auth_info_required[i], "negotiate"))
9742         cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s",
9743 	                auth_info->values[i].string.text);
9744       else
9745         i --;
9746     }
9747   }
9748   else if (auth_info && auth_info->num_values == 2 &&
9749            dest->num_auth_info_required == 1 &&
9750            !strcmp(dest->auth_info_required[0], "negotiate"))
9751   {
9752    /*
9753     * Allow fallback to username+password for Kerberized queues...
9754     */
9755 
9756     httpEncode64_2(line, sizeof(line), auth_info->values[0].string.text, (int)strlen(auth_info->values[0].string.text));
9757     cupsFilePutConf(fp, "username", line);
9758 
9759     cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s",
9760                     auth_info->values[0].string.text);
9761 
9762     httpEncode64_2(line, sizeof(line), auth_info->values[1].string.text, (int)strlen(auth_info->values[1].string.text));
9763     cupsFilePutConf(fp, "password", line);
9764 
9765     cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s",
9766                     auth_info->values[1].string.text);
9767   }
9768   else if (con->username[0])
9769   {
9770    /*
9771     * Write the authenticated username...
9772     */
9773 
9774     httpEncode64_2(line, sizeof(line), con->username, (int)strlen(con->username));
9775     cupsFilePutConf(fp, "username", line);
9776 
9777     cupsdSetStringf(job->auth_env + 0, "AUTH_USERNAME=%s", con->username);
9778 
9779    /*
9780     * Write the authenticated password...
9781     */
9782 
9783     httpEncode64_2(line, sizeof(line), con->password, (int)strlen(con->password));
9784     cupsFilePutConf(fp, "password", line);
9785 
9786     cupsdSetStringf(job->auth_env + 1, "AUTH_PASSWORD=%s", con->password);
9787   }
9788 
9789 #ifdef HAVE_GSSAPI
9790   if (con->gss_uid > 0)
9791   {
9792     cupsFilePrintf(fp, "uid %d\n", (int)con->gss_uid);
9793     cupsdSetStringf(&job->auth_uid, "AUTH_UID=%d", (int)con->gss_uid);
9794   }
9795 #endif /* HAVE_GSSAPI */
9796 
9797  /*
9798   * Write a random number of newlines to the end of the file...
9799   */
9800 
9801   for (i = (CUPS_RAND() % 1024); i >= 0; i --)
9802     cupsFilePutChar(fp, '\n');
9803 
9804  /*
9805   * Close the file and return...
9806   */
9807 
9808   cupsFileClose(fp);
9809 }
9810 
9811 
9812 /*
9813  * 'send_document()' - Send a file to a printer or class.
9814  */
9815 
9816 static void
send_document(cupsd_client_t * con,ipp_attribute_t * uri)9817 send_document(cupsd_client_t  *con,	/* I - Client connection */
9818 	      ipp_attribute_t *uri)	/* I - Printer URI */
9819 {
9820   ipp_attribute_t	*attr;		/* Current attribute */
9821   ipp_attribute_t	*format;	/* Request's document-format attribute */
9822   ipp_attribute_t	*jformat;	/* Job's document-format attribute */
9823   const char		*default_format;/* document-format-default value */
9824   int			jobid;		/* Job ID number */
9825   cupsd_job_t		*job;		/* Current job */
9826   char			job_uri[HTTP_MAX_URI],
9827 					/* Job URI */
9828 			scheme[HTTP_MAX_URI],
9829 					/* Method portion of URI */
9830 			username[HTTP_MAX_URI],
9831 					/* Username portion of URI */
9832 			host[HTTP_MAX_URI],
9833 					/* Host portion of URI */
9834 			resource[HTTP_MAX_URI];
9835 					/* Resource portion of URI */
9836   int			port;		/* Port portion of URI */
9837   mime_type_t		*filetype;	/* Type of file */
9838   char			super[MIME_MAX_SUPER],
9839 					/* Supertype of file */
9840 			type[MIME_MAX_TYPE],
9841 					/* Subtype of file */
9842 			mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
9843 					/* Textual name of mime type */
9844   char			filename[1024];	/* Job filename */
9845   cupsd_printer_t	*printer;	/* Current printer */
9846   struct stat		fileinfo;	/* File information */
9847   int			kbytes;		/* Size of file */
9848   int			compression;	/* Type of compression */
9849   int			start_job;	/* Start the job? */
9850 
9851 
9852   cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", (void *)con,
9853                   con->number, uri->values[0].string.text);
9854 
9855  /*
9856   * See if we have a job URI or a printer URI...
9857   */
9858 
9859   if (!strcmp(uri->name, "printer-uri"))
9860   {
9861    /*
9862     * Got a printer URI; see if we also have a job-id attribute...
9863     */
9864 
9865     if ((attr = ippFindAttribute(con->request, "job-id",
9866                                  IPP_TAG_INTEGER)) == NULL)
9867     {
9868       send_ipp_status(con, IPP_BAD_REQUEST,
9869                       _("Got a printer-uri attribute but no job-id."));
9870       return;
9871     }
9872 
9873     jobid = attr->values[0].integer;
9874   }
9875   else
9876   {
9877    /*
9878     * Got a job URI; parse it to get the job ID...
9879     */
9880 
9881     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
9882                     sizeof(scheme), username, sizeof(username), host,
9883 		    sizeof(host), &port, resource, sizeof(resource));
9884 
9885     if (strncmp(resource, "/jobs/", 6))
9886     {
9887      /*
9888       * Not a valid URI!
9889       */
9890 
9891       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
9892                       uri->values[0].string.text);
9893       return;
9894     }
9895 
9896     jobid = atoi(resource + 6);
9897   }
9898 
9899  /*
9900   * See if the job exists...
9901   */
9902 
9903   if ((job = cupsdFindJob(jobid)) == NULL)
9904   {
9905    /*
9906     * Nope - return a "not found" error...
9907     */
9908 
9909     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
9910     return;
9911   }
9912 
9913   printer = cupsdFindDest(job->dest);
9914 
9915  /*
9916   * See if the job is owned by the requesting user...
9917   */
9918 
9919   if (!validate_user(job, con, job->username, username, sizeof(username)))
9920   {
9921     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
9922                     cupsdFindDest(job->dest));
9923     return;
9924   }
9925 
9926  /*
9927   * OK, see if the client is sending the document compressed - CUPS
9928   * only supports "none" and "gzip".
9929   */
9930 
9931   compression = CUPS_FILE_NONE;
9932 
9933   if ((attr = ippFindAttribute(con->request, "compression",
9934                                IPP_TAG_KEYWORD)) != NULL)
9935   {
9936     if (strcmp(attr->values[0].string.text, "none")
9937 #ifdef HAVE_LIBZ
9938         && strcmp(attr->values[0].string.text, "gzip")
9939 #endif /* HAVE_LIBZ */
9940       )
9941     {
9942       send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
9943         	      attr->values[0].string.text);
9944       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
9945 	           "compression", NULL, attr->values[0].string.text);
9946       return;
9947     }
9948 
9949 #ifdef HAVE_LIBZ
9950     if (!strcmp(attr->values[0].string.text, "gzip"))
9951       compression = CUPS_FILE_GZIP;
9952 #endif /* HAVE_LIBZ */
9953   }
9954 
9955  /*
9956   * Do we have a file to print?
9957   */
9958 
9959   if ((attr = ippFindAttribute(con->request, "last-document",
9960 	                       IPP_TAG_BOOLEAN)) == NULL)
9961   {
9962     send_ipp_status(con, IPP_BAD_REQUEST,
9963                     _("Missing last-document attribute in request."));
9964     return;
9965   }
9966 
9967   if (!con->filename)
9968   {
9969    /*
9970     * Check for an empty request with "last-document" set to true, which is
9971     * used to close an "open" job by RFC 2911, section 3.3.2.
9972     */
9973 
9974     if (job->num_files > 0 && attr->values[0].boolean)
9975       goto last_document;
9976 
9977     send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
9978     return;
9979   }
9980 
9981  /*
9982   * Is it a format we support?
9983   */
9984 
9985   cupsdLoadJob(job);
9986 
9987   if ((format = ippFindAttribute(con->request, "document-format",
9988                                  IPP_TAG_MIMETYPE)) != NULL)
9989   {
9990    /*
9991     * Grab format from client...
9992     */
9993 
9994     if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
9995                super, type) != 2)
9996     {
9997       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
9998 	              format->values[0].string.text);
9999       return;
10000     }
10001 
10002     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-supplied", NULL, ippGetString(format, 0, NULL));
10003   }
10004   else if ((default_format = cupsGetOption("document-format",
10005                                            printer->num_options,
10006 					   printer->options)) != NULL)
10007   {
10008    /*
10009     * Use default document format...
10010     */
10011 
10012     if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
10013     {
10014       send_ipp_status(con, IPP_BAD_REQUEST,
10015                       _("Bad document-format-default \"%s\"."), default_format);
10016       return;
10017     }
10018   }
10019   else
10020   {
10021    /*
10022     * No document format attribute?  Auto-type it!
10023     */
10024 
10025     strlcpy(super, "application", sizeof(super));
10026     strlcpy(type, "octet-stream", sizeof(type));
10027   }
10028 
10029   _cupsRWLockRead(&MimeDatabase->lock);
10030 
10031   if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
10032   {
10033    /*
10034     * Auto-type the file...
10035     */
10036 
10037     ipp_attribute_t	*doc_name;	/* document-name attribute */
10038 
10039 
10040     cupsdLogJob(job, CUPSD_LOG_DEBUG, "Auto-typing file...");
10041 
10042     doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
10043     filetype = mimeFileType(MimeDatabase, con->filename,
10044                             doc_name ? doc_name->values[0].string.text : NULL,
10045 			    &compression);
10046 
10047     if (!filetype)
10048       filetype = mimeType(MimeDatabase, super, type);
10049 
10050     if (filetype)
10051       cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
10052 		  filetype->super, filetype->type);
10053 
10054     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super, filetype->type);
10055     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format-detected", NULL, mimetype);
10056   }
10057   else
10058     filetype = mimeType(MimeDatabase, super, type);
10059 
10060   _cupsRWUnlock(&MimeDatabase->lock);
10061 
10062   if (filetype)
10063   {
10064    /*
10065     * Replace the document-format attribute value with the auto-typed or
10066     * default one.
10067     */
10068 
10069     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
10070              filetype->type);
10071 
10072     if ((jformat = ippFindAttribute(job->attrs, "document-format",
10073                                     IPP_TAG_MIMETYPE)) != NULL)
10074       ippSetString(job->attrs, &jformat, 0, mimetype);
10075     else
10076       ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
10077 	           "document-format", NULL, mimetype);
10078   }
10079   else if (!filetype)
10080   {
10081     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10082                     _("Unsupported document-format \"%s/%s\"."), super, type);
10083     cupsdLogMessage(CUPSD_LOG_INFO,
10084                     "Hint: Do you have the raw file printing rules enabled?");
10085 
10086     if (format)
10087       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10088                    "document-format", NULL, format->values[0].string.text);
10089 
10090     return;
10091   }
10092 
10093   if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
10094   {
10095     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
10096              filetype->type);
10097 
10098     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
10099                     _("Unsupported document-format \"%s\"."), mimetype);
10100 
10101     ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
10102                  "document-format", NULL, mimetype);
10103 
10104     return;
10105   }
10106 
10107  /*
10108   * Add the file to the job...
10109   */
10110 
10111   if (add_file(con, job, filetype, compression))
10112     return;
10113 
10114   if ((attr = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME)) != NULL)
10115     ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "document-name-supplied", NULL, ippGetString(attr, 0, NULL));
10116 
10117   if (stat(con->filename, &fileinfo))
10118     kbytes = 0;
10119   else
10120     kbytes = (fileinfo.st_size + 1023) / 1024;
10121 
10122   cupsdUpdateQuota(printer, job->username, 0, kbytes);
10123 
10124   job->koctets += kbytes;
10125 
10126   if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
10127     attr->values[0].integer += kbytes;
10128 
10129   snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id, job->num_files);
10130   if (rename(con->filename, filename))
10131   {
10132     cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to rename job document file \"%s\": %s", filename, strerror(errno));
10133 
10134     send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to rename job document file."));
10135     return;
10136   }
10137 
10138   cupsdClearString(&con->filename);
10139 
10140   cupsdLogJob(job, CUPSD_LOG_INFO, "File of type %s/%s queued by \"%s\".",
10141 	      filetype->super, filetype->type, job->username);
10142 
10143  /*
10144   * Start the job if this is the last document...
10145   */
10146 
10147   last_document:
10148 
10149   if ((attr = ippFindAttribute(con->request, "last-document",
10150                                IPP_TAG_BOOLEAN)) != NULL &&
10151       attr->values[0].boolean)
10152   {
10153    /*
10154     * See if we need to add the ending sheet...
10155     */
10156 
10157     if (cupsdTimeoutJob(job))
10158       return;
10159 
10160     if (job->state_value == IPP_JOB_STOPPED)
10161     {
10162       job->state->values[0].integer = IPP_JOB_PENDING;
10163       job->state_value              = IPP_JOB_PENDING;
10164 
10165       ippSetString(job->attrs, &job->reasons, 0, "none");
10166     }
10167     else if (job->state_value == IPP_JOB_HELD)
10168     {
10169       if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10170                                    IPP_TAG_KEYWORD)) == NULL)
10171 	attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10172 
10173       if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
10174       {
10175 	job->state->values[0].integer = IPP_JOB_PENDING;
10176 	job->state_value              = IPP_JOB_PENDING;
10177 
10178 	ippSetString(job->attrs, &job->reasons, 0, "none");
10179       }
10180       else
10181 	ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
10182     }
10183 
10184     job->dirty = 1;
10185     cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10186 
10187     start_job = 1;
10188   }
10189   else
10190   {
10191     if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
10192                                  IPP_TAG_KEYWORD)) == NULL)
10193       attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
10194 
10195     if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
10196     {
10197       job->state->values[0].integer = IPP_JOB_HELD;
10198       job->state_value              = IPP_JOB_HELD;
10199       job->hold_until               = time(NULL) + MultipleOperationTimeout;
10200 
10201       ippSetString(job->attrs, &job->reasons, 0, "job-incoming");
10202 
10203       job->dirty = 1;
10204       cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10205     }
10206 
10207     start_job = 0;
10208   }
10209 
10210  /*
10211   * Fill in the response info...
10212   */
10213 
10214   httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL, con->clientname, con->clientport, "/jobs/%d", jobid);
10215   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
10216 
10217   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
10218 
10219   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
10220   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, job->reasons->values[0].string.text);
10221 
10222   con->response->request.status.status_code = IPP_OK;
10223 
10224  /*
10225   * Start the job if necessary...
10226   */
10227 
10228   if (start_job)
10229     cupsdCheckJobs();
10230 }
10231 
10232 
10233 /*
10234  * 'send_http_error()' - Send a HTTP error back to the IPP client.
10235  */
10236 
10237 static void
send_http_error(cupsd_client_t * con,http_status_t status,cupsd_printer_t * printer)10238 send_http_error(
10239     cupsd_client_t  *con,		/* I - Client connection */
10240     http_status_t   status,		/* I - HTTP status code */
10241     cupsd_printer_t *printer)		/* I - Printer, if any */
10242 {
10243   ipp_attribute_t	*uri;		/* Request URI, if any */
10244 
10245 
10246   if ((uri = ippFindAttribute(con->request, "printer-uri",
10247                               IPP_TAG_URI)) == NULL)
10248     uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
10249 
10250   cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
10251                   "[Client %d] Returning HTTP %s for %s (%s) from %s",
10252                   con->number, httpStatus(status),
10253 		  con->request ?
10254 		      ippOpString(con->request->request.op.operation_id) :
10255 		      "no operation-id",
10256 		  uri ? uri->values[0].string.text : "no URI",
10257 		  con->http->hostname);
10258 
10259   if (printer)
10260   {
10261     int		auth_type;		/* Type of authentication required */
10262 
10263 
10264     auth_type = CUPSD_AUTH_NONE;
10265 
10266     if (status == HTTP_UNAUTHORIZED &&
10267         printer->num_auth_info_required > 0 &&
10268         !strcmp(printer->auth_info_required[0], "negotiate") &&
10269 	con->request &&
10270 	(con->request->request.op.operation_id == IPP_PRINT_JOB ||
10271 	 con->request->request.op.operation_id == IPP_CREATE_JOB ||
10272 	 con->request->request.op.operation_id == CUPS_AUTHENTICATE_JOB))
10273     {
10274      /*
10275       * Creating and authenticating jobs requires Kerberos...
10276       */
10277 
10278       auth_type = CUPSD_AUTH_NEGOTIATE;
10279     }
10280     else
10281     {
10282      /*
10283       * Use policy/location-defined authentication requirements...
10284       */
10285 
10286       char	resource[HTTP_MAX_URI];	/* Resource portion of URI */
10287       cupsd_location_t *auth;		/* Pointer to authentication element */
10288 
10289 
10290       if (printer->type & CUPS_PRINTER_CLASS)
10291 	snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
10292       else
10293 	snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
10294 
10295       if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
10296 	  auth->type == CUPSD_AUTH_NONE)
10297 	auth = cupsdFindPolicyOp(printer->op_policy_ptr,
10298 				 con->request ?
10299 				     con->request->request.op.operation_id :
10300 				     IPP_PRINT_JOB);
10301 
10302       if (auth)
10303       {
10304         if (auth->type == CUPSD_AUTH_DEFAULT)
10305 	  auth_type = cupsdDefaultAuthType();
10306 	else
10307 	  auth_type = auth->type;
10308       }
10309     }
10310 
10311     cupsdSendError(con, status, auth_type);
10312   }
10313   else
10314     cupsdSendError(con, status, CUPSD_AUTH_NONE);
10315 
10316   ippDelete(con->response);
10317   con->response = NULL;
10318 
10319   return;
10320 }
10321 
10322 
10323 /*
10324  * 'send_ipp_status()' - Send a status back to the IPP client.
10325  */
10326 
10327 static void
send_ipp_status(cupsd_client_t * con,ipp_status_t status,const char * message,...)10328 send_ipp_status(cupsd_client_t *con,	/* I - Client connection */
10329                 ipp_status_t   status,	/* I - IPP status code */
10330 	        const char     *message,/* I - Status message */
10331 	        ...)			/* I - Additional args as needed */
10332 {
10333   va_list	ap;			/* Pointer to additional args */
10334   char		formatted[1024];	/* Formatted error message */
10335 
10336 
10337   va_start(ap, message);
10338   vsnprintf(formatted, sizeof(formatted),
10339             _cupsLangString(con->language, message), ap);
10340   va_end(ap);
10341 
10342   cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
10343 		  ippOpString(con->request->request.op.operation_id),
10344 		  ippErrorString(status), formatted);
10345 
10346   con->response->request.status.status_code = status;
10347 
10348   if (ippFindAttribute(con->response, "attributes-charset",
10349                        IPP_TAG_ZERO) == NULL)
10350     ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
10351                  "attributes-charset", NULL, "utf-8");
10352 
10353   if (ippFindAttribute(con->response, "attributes-natural-language",
10354                        IPP_TAG_ZERO) == NULL)
10355     ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
10356                  "attributes-natural-language", NULL, DefaultLanguage);
10357 
10358   ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
10359                "status-message", NULL, formatted);
10360 }
10361 
10362 
10363 /*
10364  * 'send_response()' - Send the IPP response.
10365  */
10366 
10367 static int				/* O - 1 on success, 0 on failure */
send_response(cupsd_client_t * con)10368 send_response(cupsd_client_t *con)	/* I - Client */
10369 {
10370   ipp_attribute_t	*uri;		/* Target URI */
10371   int			ret = 0;	/* Return value */
10372   static _cups_mutex_t	mutex = _CUPS_MUTEX_INITIALIZER;
10373 					/* Mutex for logging/access */
10374 
10375 
10376   _cupsMutexLock(&mutex);
10377 
10378   if ((uri = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) == NULL)
10379   {
10380     if ((uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) == NULL)
10381       uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
10382   }
10383 
10384   cupsdLogClient(con, con->response->request.status.status_code >= IPP_STATUS_ERROR_BAD_REQUEST && con->response->request.status.status_code != IPP_STATUS_ERROR_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, "Returning IPP %s for %s (%s) from %s.",  ippErrorString(con->response->request.status.status_code), ippOpString(con->request->request.op.operation_id), uri ? uri->values[0].string.text : "no URI", con->http->hostname);
10385 
10386   httpClearFields(con->http);
10387 
10388 #ifdef CUPSD_USE_CHUNKING
10389  /*
10390   * Because older versions of CUPS (1.1.17 and older) and some IPP
10391   * clients do not implement chunking properly, we cannot use
10392   * chunking by default.  This may become the default in future
10393   * CUPS releases, or we might add a configuration directive for
10394   * it.
10395   */
10396 
10397   if (con->http->version == HTTP_1_1)
10398   {
10399     cupsdLogClient(con, CUPSD_LOG_DEBUG, "Transfer-Encoding: chunked");
10400     cupsdSetLength(con->http, 0);
10401   }
10402   else
10403 #endif /* CUPSD_USE_CHUNKING */
10404   {
10405     size_t	length;			/* Length of response */
10406 
10407 
10408     length = ippLength(con->response);
10409 
10410     if (con->file >= 0 && !con->pipe_pid)
10411     {
10412       struct stat	fileinfo;	/* File information */
10413 
10414       if (!fstat(con->file, &fileinfo))
10415 	length += (size_t)fileinfo.st_size;
10416     }
10417 
10418     cupsdLogClient(con, CUPSD_LOG_DEBUG, "Content-Length: " CUPS_LLFMT, CUPS_LLCAST length);
10419     httpSetLength(con->http, length);
10420   }
10421 
10422   if (cupsdSendHeader(con, HTTP_STATUS_OK, "application/ipp", CUPSD_AUTH_NONE))
10423   {
10424    /*
10425     * Tell the caller the response header was sent successfully...
10426     */
10427 
10428     cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, (cupsd_selfunc_t)cupsdWriteClient, con);
10429 
10430     ret = 1;
10431   }
10432 
10433   _cupsMutexUnlock(&mutex);
10434 
10435   return (ret);
10436 }
10437 
10438 
10439 /*
10440  * 'set_default()' - Set the default destination...
10441  */
10442 
10443 static void
set_default(cupsd_client_t * con,ipp_attribute_t * uri)10444 set_default(cupsd_client_t  *con,	/* I - Client connection */
10445             ipp_attribute_t *uri)	/* I - Printer URI */
10446 {
10447   http_status_t		status;		/* Policy status */
10448   cups_ptype_t		dtype;		/* Destination type (printer/class) */
10449   cupsd_printer_t	*printer,	/* Printer */
10450 			*oldprinter;	/* Old default printer */
10451 
10452 
10453   cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", (void *)con,
10454                   con->number, uri->values[0].string.text);
10455 
10456  /*
10457   * Is the destination valid?
10458   */
10459 
10460   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10461   {
10462    /*
10463     * Bad URI...
10464     */
10465 
10466     send_ipp_status(con, IPP_NOT_FOUND,
10467                     _("The printer or class does not exist."));
10468     return;
10469   }
10470 
10471  /*
10472   * Check policy...
10473   */
10474 
10475   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
10476   {
10477     send_http_error(con, status, NULL);
10478     return;
10479   }
10480 
10481  /*
10482   * Set it as the default...
10483   */
10484 
10485   oldprinter     = DefaultPrinter;
10486   DefaultPrinter = printer;
10487 
10488   if (oldprinter)
10489     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, oldprinter, NULL,
10490                   "%s is no longer the default printer.", oldprinter->name);
10491 
10492   cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
10493 		"%s is now the default printer.", printer->name);
10494 
10495   cupsdMarkDirty(CUPSD_DIRTY_PRINTERS | CUPSD_DIRTY_CLASSES |
10496                  CUPSD_DIRTY_PRINTCAP);
10497 
10498   cupsdLogMessage(CUPSD_LOG_INFO,
10499                   "Default destination set to \"%s\" by \"%s\".",
10500 		  printer->name, get_username(con));
10501 
10502  /*
10503   * Everything was ok, so return OK status...
10504   */
10505 
10506   con->response->request.status.status_code = IPP_OK;
10507 }
10508 
10509 
10510 /*
10511  * 'set_job_attrs()' - Set job attributes.
10512  */
10513 
10514 static void
set_job_attrs(cupsd_client_t * con,ipp_attribute_t * uri)10515 set_job_attrs(cupsd_client_t  *con,	/* I - Client connection */
10516 	      ipp_attribute_t *uri)	/* I - Job URI */
10517 {
10518   ipp_attribute_t	*attr,		/* Current attribute */
10519 			*attr2;		/* Job attribute */
10520   int			jobid;		/* Job ID */
10521   cupsd_job_t		*job;		/* Current job */
10522   char			scheme[HTTP_MAX_URI],
10523 					/* Method portion of URI */
10524 			username[HTTP_MAX_URI],
10525 					/* Username portion of URI */
10526 			host[HTTP_MAX_URI],
10527 					/* Host portion of URI */
10528 			resource[HTTP_MAX_URI];
10529 					/* Resource portion of URI */
10530   int			port;		/* Port portion of URI */
10531   int			event;		/* Events? */
10532   int			check_jobs;	/* Check jobs? */
10533 
10534 
10535   cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", (void *)con,
10536                   con->number, uri->values[0].string.text);
10537 
10538  /*
10539   * Start with "everything is OK" status...
10540   */
10541 
10542   con->response->request.status.status_code = IPP_OK;
10543 
10544  /*
10545   * See if we have a job URI or a printer URI...
10546   */
10547 
10548   if (!strcmp(uri->name, "printer-uri"))
10549   {
10550    /*
10551     * Got a printer URI; see if we also have a job-id attribute...
10552     */
10553 
10554     if ((attr = ippFindAttribute(con->request, "job-id",
10555                                  IPP_TAG_INTEGER)) == NULL)
10556     {
10557       send_ipp_status(con, IPP_BAD_REQUEST,
10558                       _("Got a printer-uri attribute but no job-id."));
10559       return;
10560     }
10561 
10562     jobid = attr->values[0].integer;
10563   }
10564   else
10565   {
10566    /*
10567     * Got a job URI; parse it to get the job ID...
10568     */
10569 
10570     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
10571                     sizeof(scheme), username, sizeof(username), host,
10572 		    sizeof(host), &port, resource, sizeof(resource));
10573 
10574     if (strncmp(resource, "/jobs/", 6))
10575     {
10576      /*
10577       * Not a valid URI!
10578       */
10579 
10580       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
10581                       uri->values[0].string.text);
10582       return;
10583     }
10584 
10585     jobid = atoi(resource + 6);
10586   }
10587 
10588  /*
10589   * See if the job exists...
10590   */
10591 
10592   if ((job = cupsdFindJob(jobid)) == NULL)
10593   {
10594    /*
10595     * Nope - return a "not found" error...
10596     */
10597 
10598     send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
10599     return;
10600   }
10601 
10602  /*
10603   * See if the job has been completed...
10604   */
10605 
10606   if (job->state_value > IPP_JOB_STOPPED)
10607   {
10608    /*
10609     * Return a "not-possible" error...
10610     */
10611 
10612     send_ipp_status(con, IPP_NOT_POSSIBLE,
10613                     _("Job #%d is finished and cannot be altered."), jobid);
10614     return;
10615   }
10616 
10617  /*
10618   * See if the job is owned by the requesting user...
10619   */
10620 
10621   if (!validate_user(job, con, job->username, username, sizeof(username)))
10622   {
10623     send_http_error(con, con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED,
10624                     cupsdFindDest(job->dest));
10625     return;
10626   }
10627 
10628  /*
10629   * See what the user wants to change.
10630   */
10631 
10632   cupsdLoadJob(job);
10633 
10634   check_jobs = 0;
10635   event      = 0;
10636 
10637   for (attr = con->request->attrs; attr; attr = attr->next)
10638   {
10639     if (attr->group_tag != IPP_TAG_JOB || !attr->name)
10640       continue;
10641 
10642     if (!strcmp(attr->name, "attributes-charset") ||
10643 	!strcmp(attr->name, "attributes-natural-language") ||
10644 	!strncmp(attr->name, "date-time-at-", 13) ||
10645 	!strncmp(attr->name, "document-compression", 20) ||
10646 	!strncmp(attr->name, "document-format", 15) ||
10647 	!strcmp(attr->name, "job-detailed-status-messages") ||
10648 	!strcmp(attr->name, "job-document-access-errors") ||
10649 	!strcmp(attr->name, "job-id") ||
10650 	!strcmp(attr->name, "job-impressions-completed") ||
10651 	!strcmp(attr->name, "job-k-octets-completed") ||
10652 	!strcmp(attr->name, "job-media-sheets-completed") ||
10653         !strcmp(attr->name, "job-originating-host-name") ||
10654         !strcmp(attr->name, "job-originating-user-name") ||
10655 	!strcmp(attr->name, "job-pages-completed") ||
10656 	!strcmp(attr->name, "job-printer-up-time") ||
10657 	!strcmp(attr->name, "job-printer-uri") ||
10658 	!strcmp(attr->name, "job-sheets") ||
10659 	!strcmp(attr->name, "job-state-message") ||
10660 	!strcmp(attr->name, "job-state-reasons") ||
10661 	!strcmp(attr->name, "job-uri") ||
10662 	!strcmp(attr->name, "number-of-documents") ||
10663 	!strcmp(attr->name, "number-of-intervening-jobs") ||
10664 	!strcmp(attr->name, "output-device-assigned") ||
10665 	!strncmp(attr->name, "time-at-", 8))
10666     {
10667      /*
10668       * Read-only attrs!
10669       */
10670 
10671       send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
10672                       _("%s cannot be changed."), attr->name);
10673 
10674       attr2 = ippCopyAttribute(con->response, attr, 0);
10675       ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
10676       continue;
10677     }
10678 
10679     if (!ippValidateAttribute(attr))
10680     {
10681       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Bad '%s' value."), attr->name);
10682       ippCopyAttribute(con->response, attr, 0);
10683       return;
10684     }
10685 
10686     if (!strcmp(attr->name, "job-hold-until"))
10687     {
10688       const char *when = ippGetString(attr, 0, NULL);
10689 					/* job-hold-until value */
10690 
10691       if ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1)
10692       {
10693 	send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
10694 	ippCopyAttribute(con->response, attr, 0);
10695 	return;
10696       }
10697 
10698       cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s", when);
10699       cupsdSetJobHoldUntil(job, when, 0);
10700 
10701       if (!strcmp(when, "no-hold"))
10702       {
10703 	cupsdReleaseJob(job);
10704 	check_jobs = 1;
10705       }
10706       else
10707 	cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".", username);
10708 
10709       event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
10710     }
10711     else if (!strcmp(attr->name, "job-priority"))
10712     {
10713      /*
10714       * Change the job priority...
10715       */
10716 
10717       if (attr->value_tag != IPP_TAG_INTEGER)
10718       {
10719 	send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
10720 
10721 	attr2 = ippCopyAttribute(con->response, attr, 0);
10722 	ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
10723       }
10724       else if (job->state_value >= IPP_JOB_PROCESSING)
10725       {
10726 	send_ipp_status(con, IPP_NOT_POSSIBLE,
10727 	                _("Job is completed and cannot be changed."));
10728 	return;
10729       }
10730       else if (con->response->request.status.status_code == IPP_OK)
10731       {
10732         cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-priority to %d",
10733 	            attr->values[0].integer);
10734         cupsdSetJobPriority(job, attr->values[0].integer);
10735 
10736 	check_jobs = 1;
10737         event      |= CUPSD_EVENT_JOB_CONFIG_CHANGED |
10738 	              CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED;
10739       }
10740     }
10741     else if (!strcmp(attr->name, "job-state"))
10742     {
10743      /*
10744       * Change the job state...
10745       */
10746 
10747       if (attr->value_tag != IPP_TAG_ENUM)
10748       {
10749 	send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
10750 
10751 	attr2 = ippCopyAttribute(con->response, attr, 0);
10752 	ippSetGroupTag(con->response, &attr2, IPP_TAG_UNSUPPORTED_GROUP);
10753       }
10754       else
10755       {
10756         switch (attr->values[0].integer)
10757 	{
10758 	  case IPP_JOB_PENDING :
10759 	  case IPP_JOB_HELD :
10760 	      if (job->state_value > IPP_JOB_HELD)
10761 	      {
10762 		send_ipp_status(con, IPP_NOT_POSSIBLE,
10763 		                _("Job state cannot be changed."));
10764 		return;
10765 	      }
10766               else if (con->response->request.status.status_code == IPP_OK)
10767 	      {
10768 		cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10769 			    attr->values[0].integer);
10770                 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer, CUPSD_JOB_DEFAULT, "Job state changed by \"%s\"", username);
10771 		check_jobs = 1;
10772 	      }
10773 	      break;
10774 
10775 	  case IPP_JOB_PROCESSING :
10776 	  case IPP_JOB_STOPPED :
10777 	      if (job->state_value != (ipp_jstate_t)attr->values[0].integer)
10778 	      {
10779 		send_ipp_status(con, IPP_NOT_POSSIBLE,
10780 		                _("Job state cannot be changed."));
10781 		return;
10782 	      }
10783 	      break;
10784 
10785 	  case IPP_JOB_CANCELED :
10786 	  case IPP_JOB_ABORTED :
10787 	  case IPP_JOB_COMPLETED :
10788 	      if (job->state_value > IPP_JOB_PROCESSING)
10789 	      {
10790 		send_ipp_status(con, IPP_NOT_POSSIBLE,
10791 		                _("Job state cannot be changed."));
10792 		return;
10793 	      }
10794               else if (con->response->request.status.status_code == IPP_OK)
10795 	      {
10796 		cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-state to %d",
10797 			    attr->values[0].integer);
10798                 cupsdSetJobState(job, (ipp_jstate_t)attr->values[0].integer,
10799 		                 CUPSD_JOB_DEFAULT,
10800 				 "Job state changed by \"%s\"", username);
10801                 check_jobs = 1;
10802 	      }
10803 	      break;
10804 	}
10805       }
10806     }
10807     else if (con->response->request.status.status_code != IPP_OK)
10808       continue;
10809     else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10810                                        IPP_TAG_ZERO)) != NULL)
10811     {
10812      /*
10813       * Some other value; first free the old value...
10814       */
10815 
10816       if (job->attrs->prev)
10817         job->attrs->prev->next = attr2->next;
10818       else
10819         job->attrs->attrs = attr2->next;
10820 
10821       if (job->attrs->last == attr2)
10822         job->attrs->last = job->attrs->prev;
10823 
10824       ippDeleteAttribute(NULL, attr2);
10825 
10826      /*
10827       * Then copy the attribute...
10828       */
10829 
10830       ippCopyAttribute(job->attrs, attr, 0);
10831     }
10832     else if (attr->value_tag == IPP_TAG_DELETEATTR)
10833     {
10834      /*
10835       * Delete the attribute...
10836       */
10837 
10838       if ((attr2 = ippFindAttribute(job->attrs, attr->name,
10839                                     IPP_TAG_ZERO)) != NULL)
10840       {
10841         if (job->attrs->prev)
10842 	  job->attrs->prev->next = attr2->next;
10843 	else
10844 	  job->attrs->attrs = attr2->next;
10845 
10846         if (attr2 == job->attrs->last)
10847 	  job->attrs->last = job->attrs->prev;
10848 
10849         ippDeleteAttribute(NULL, attr2);
10850 
10851         event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10852       }
10853     }
10854     else
10855     {
10856      /*
10857       * Add new option by copying it...
10858       */
10859 
10860       ippCopyAttribute(job->attrs, attr, 0);
10861 
10862       event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
10863     }
10864   }
10865 
10866  /*
10867   * Save the job...
10868   */
10869 
10870   job->dirty = 1;
10871   cupsdMarkDirty(CUPSD_DIRTY_JOBS);
10872 
10873  /*
10874   * Send events as needed...
10875   */
10876 
10877   if (event & CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED)
10878     cupsdAddEvent(CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED,
10879                   cupsdFindDest(job->dest), job,
10880                   "Job priority changed by user.");
10881 
10882   if (event & CUPSD_EVENT_JOB_STATE)
10883     cupsdAddEvent(CUPSD_EVENT_JOB_STATE, cupsdFindDest(job->dest), job,
10884                   job->state_value == IPP_JOB_HELD ?
10885 		      "Job held by user." : "Job restarted by user.");
10886 
10887   if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
10888     cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
10889                   "Job options changed by user.");
10890 
10891  /*
10892   * Start jobs if possible...
10893   */
10894 
10895   if (check_jobs)
10896     cupsdCheckJobs();
10897 }
10898 
10899 
10900 /*
10901  * 'set_printer_attrs()' - Set printer attributes.
10902  */
10903 
10904 static void
set_printer_attrs(cupsd_client_t * con,ipp_attribute_t * uri)10905 set_printer_attrs(cupsd_client_t  *con,	/* I - Client connection */
10906                   ipp_attribute_t *uri)	/* I - Printer */
10907 {
10908   http_status_t		status;		/* Policy status */
10909   cups_ptype_t		dtype;		/* Destination type (printer/class) */
10910   cupsd_printer_t	*printer;	/* Printer/class */
10911   ipp_attribute_t	*attr;		/* Printer attribute */
10912   int			changed = 0;	/* Was anything changed? */
10913 
10914 
10915   cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", (void *)con,
10916                   con->number, uri->values[0].string.text);
10917 
10918  /*
10919   * Is the destination valid?
10920   */
10921 
10922   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
10923   {
10924    /*
10925     * Bad URI...
10926     */
10927 
10928     send_ipp_status(con, IPP_NOT_FOUND,
10929                     _("The printer or class does not exist."));
10930     return;
10931   }
10932 
10933  /*
10934   * Check policy...
10935   */
10936 
10937   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
10938   {
10939     send_http_error(con, status, printer);
10940     return;
10941   }
10942 
10943  /*
10944   * Return a list of attributes that can be set via Set-Printer-Attributes.
10945   */
10946 
10947   if ((attr = ippFindAttribute(con->request, "printer-location",
10948                                IPP_TAG_TEXT)) != NULL)
10949   {
10950     cupsdSetString(&printer->location, attr->values[0].string.text);
10951     changed = 1;
10952   }
10953 
10954   if ((attr = ippFindAttribute(con->request, "printer-geo-location", IPP_TAG_URI)) != NULL && !strncmp(attr->values[0].string.text, "geo:", 4))
10955   {
10956     cupsdSetString(&printer->geo_location, attr->values[0].string.text);
10957     changed = 1;
10958   }
10959 
10960   if ((attr = ippFindAttribute(con->request, "printer-organization", IPP_TAG_TEXT)) != NULL)
10961   {
10962     cupsdSetString(&printer->organization, attr->values[0].string.text);
10963     changed = 1;
10964   }
10965 
10966   if ((attr = ippFindAttribute(con->request, "printer-organizational-unit", IPP_TAG_TEXT)) != NULL)
10967   {
10968     cupsdSetString(&printer->organizational_unit, attr->values[0].string.text);
10969     changed = 1;
10970   }
10971 
10972   if ((attr = ippFindAttribute(con->request, "printer-info",
10973                                IPP_TAG_TEXT)) != NULL)
10974   {
10975     cupsdSetString(&printer->info, attr->values[0].string.text);
10976     changed = 1;
10977   }
10978 
10979  /*
10980   * Update the printer attributes and return...
10981   */
10982 
10983   if (changed)
10984   {
10985     printer->config_time = time(NULL);
10986 
10987     cupsdSetPrinterAttrs(printer);
10988     cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
10989 
10990     cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL,
10991                   "Printer \"%s\" description or location changed by \"%s\".",
10992 		  printer->name, get_username(con));
10993 
10994     cupsdLogMessage(CUPSD_LOG_INFO,
10995                     "Printer \"%s\" description or location changed by \"%s\".",
10996                     printer->name, get_username(con));
10997   }
10998 
10999   con->response->request.status.status_code = IPP_OK;
11000 }
11001 
11002 
11003 /*
11004  * 'set_printer_defaults()' - Set printer default options from a request.
11005  */
11006 
11007 static int				/* O - 1 on success, 0 on failure */
set_printer_defaults(cupsd_client_t * con,cupsd_printer_t * printer)11008 set_printer_defaults(
11009     cupsd_client_t  *con,		/* I - Client connection */
11010     cupsd_printer_t *printer)		/* I - Printer */
11011 {
11012   int			i;		/* Looping var */
11013   ipp_attribute_t 	*attr;		/* Current attribute */
11014   size_t		namelen;	/* Length of attribute name */
11015   char			name[256],	/* New attribute name */
11016 			value[256];	/* String version of integer attrs */
11017 
11018 
11019   for (attr = con->request->attrs; attr; attr = attr->next)
11020   {
11021    /*
11022     * Skip non-printer attributes...
11023     */
11024 
11025     if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
11026       continue;
11027 
11028     cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
11029 
11030     if (!strcmp(attr->name, "job-sheets-default"))
11031     {
11032      /*
11033       * Only allow keywords and names...
11034       */
11035 
11036       if (printer->temporary)
11037         goto temporary_printer;
11038 
11039       if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
11040         continue;
11041 
11042      /*
11043       * Only allow job-sheets-default to be set when running without a
11044       * system high classification level...
11045       */
11046 
11047       if (Classification)
11048         continue;
11049 
11050       cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
11051 
11052       if (attr->num_values > 1)
11053 	cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
11054       else
11055 	cupsdSetString(&printer->job_sheets[1], "none");
11056     }
11057     else if (!strcmp(attr->name, "requesting-user-name-allowed"))
11058     {
11059       if (printer->temporary)
11060         goto temporary_printer;
11061 
11062       cupsdFreeStrings(&(printer->users));
11063 
11064       printer->deny_users = 0;
11065 
11066       if (attr->value_tag == IPP_TAG_NAME &&
11067           (attr->num_values > 1 ||
11068 	   strcmp(attr->values[0].string.text, "all")))
11069       {
11070 	for (i = 0; i < attr->num_values; i ++)
11071 	  cupsdAddString(&(printer->users), attr->values[i].string.text);
11072       }
11073     }
11074     else if (!strcmp(attr->name, "requesting-user-name-denied"))
11075     {
11076       if (printer->temporary)
11077         goto temporary_printer;
11078 
11079       cupsdFreeStrings(&(printer->users));
11080 
11081       printer->deny_users = 1;
11082 
11083       if (attr->value_tag == IPP_TAG_NAME &&
11084           (attr->num_values > 1 ||
11085 	   strcmp(attr->values[0].string.text, "none")))
11086       {
11087 	for (i = 0; i < attr->num_values; i ++)
11088 	  cupsdAddString(&(printer->users), attr->values[i].string.text);
11089       }
11090     }
11091     else if (!strcmp(attr->name, "job-quota-period"))
11092     {
11093       if (printer->temporary)
11094         goto temporary_printer;
11095 
11096       if (attr->value_tag != IPP_TAG_INTEGER)
11097         continue;
11098 
11099       cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
11100         	      attr->values[0].integer);
11101       cupsdFreeQuotas(printer);
11102 
11103       printer->quota_period = attr->values[0].integer;
11104     }
11105     else if (!strcmp(attr->name, "job-k-limit"))
11106     {
11107       if (printer->temporary)
11108         goto temporary_printer;
11109 
11110       if (attr->value_tag != IPP_TAG_INTEGER)
11111         continue;
11112 
11113       cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
11114         	      attr->values[0].integer);
11115       cupsdFreeQuotas(printer);
11116 
11117       printer->k_limit = attr->values[0].integer;
11118     }
11119     else if (!strcmp(attr->name, "job-page-limit"))
11120     {
11121       if (printer->temporary)
11122         goto temporary_printer;
11123 
11124       if (attr->value_tag != IPP_TAG_INTEGER)
11125         continue;
11126 
11127       cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
11128         	      attr->values[0].integer);
11129       cupsdFreeQuotas(printer);
11130 
11131       printer->page_limit = attr->values[0].integer;
11132     }
11133     else if (!strcmp(attr->name, "printer-op-policy"))
11134     {
11135       cupsd_policy_t *p;		/* Policy */
11136 
11137 
11138       if (printer->temporary)
11139         goto temporary_printer;
11140 
11141       if (attr->value_tag != IPP_TAG_NAME)
11142         continue;
11143 
11144       if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
11145       {
11146 	cupsdLogMessage(CUPSD_LOG_DEBUG,
11147                 	"Setting printer-op-policy to \"%s\"...",
11148                 	attr->values[0].string.text);
11149 	cupsdSetString(&printer->op_policy, attr->values[0].string.text);
11150 	printer->op_policy_ptr = p;
11151       }
11152       else
11153       {
11154 	send_ipp_status(con, IPP_NOT_POSSIBLE,
11155                 	_("Unknown printer-op-policy \"%s\"."),
11156                 	attr->values[0].string.text);
11157 	return (0);
11158       }
11159     }
11160     else if (!strcmp(attr->name, "printer-error-policy"))
11161     {
11162       if (printer->temporary)
11163         goto temporary_printer;
11164 
11165       if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
11166         continue;
11167 
11168       if (strcmp(attr->values[0].string.text, "retry-current-job") &&
11169           ((printer->type & CUPS_PRINTER_CLASS) ||
11170 	   (strcmp(attr->values[0].string.text, "abort-job") &&
11171 	    strcmp(attr->values[0].string.text, "retry-job") &&
11172 	    strcmp(attr->values[0].string.text, "stop-printer"))))
11173       {
11174 	send_ipp_status(con, IPP_NOT_POSSIBLE,
11175                 	_("Unknown printer-error-policy \"%s\"."),
11176                 	attr->values[0].string.text);
11177 	return (0);
11178       }
11179 
11180       cupsdLogMessage(CUPSD_LOG_DEBUG,
11181                       "Setting printer-error-policy to \"%s\"...",
11182                       attr->values[0].string.text);
11183       cupsdSetString(&printer->error_policy, attr->values[0].string.text);
11184     }
11185 
11186    /*
11187     * Skip any other non-default attributes...
11188     */
11189 
11190     namelen = strlen(attr->name);
11191     if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
11192         namelen > (sizeof(name) - 1) || attr->num_values != 1)
11193       continue;
11194 
11195     if (printer->temporary)
11196       goto temporary_printer;
11197 
11198    /*
11199     * OK, anything else must be a user-defined default...
11200     */
11201 
11202     strlcpy(name, attr->name, sizeof(name));
11203     name[namelen - 8] = '\0';		/* Strip "-default" */
11204 
11205     switch (attr->value_tag)
11206     {
11207       case IPP_TAG_DELETEATTR :
11208           printer->num_options = cupsRemoveOption(name,
11209 						  printer->num_options,
11210 						  &(printer->options));
11211           cupsdLogMessage(CUPSD_LOG_DEBUG,
11212 	                  "Deleting %s", attr->name);
11213           break;
11214 
11215       case IPP_TAG_NAME :
11216       case IPP_TAG_TEXT :
11217       case IPP_TAG_KEYWORD :
11218       case IPP_TAG_URI :
11219           printer->num_options = cupsAddOption(name,
11220 	                                       attr->values[0].string.text,
11221 					       printer->num_options,
11222 					       &(printer->options));
11223           cupsdLogMessage(CUPSD_LOG_DEBUG,
11224 	                  "Setting %s to \"%s\"...", attr->name,
11225 			  attr->values[0].string.text);
11226           break;
11227 
11228       case IPP_TAG_BOOLEAN :
11229           printer->num_options = cupsAddOption(name,
11230 	                                       attr->values[0].boolean ?
11231 					           "true" : "false",
11232 					       printer->num_options,
11233 					       &(printer->options));
11234           cupsdLogMessage(CUPSD_LOG_DEBUG,
11235 	                  "Setting %s to %s...", attr->name,
11236 			  attr->values[0].boolean ? "true" : "false");
11237           break;
11238 
11239       case IPP_TAG_INTEGER :
11240       case IPP_TAG_ENUM :
11241           printer->num_options = cupsAddIntegerOption(name, attr->values[0].integer, printer->num_options, &(printer->options));
11242           cupsdLogMessage(CUPSD_LOG_DEBUG,
11243 	                  "Setting %s to %s...", attr->name, value);
11244           break;
11245 
11246       case IPP_TAG_RANGE :
11247           snprintf(value, sizeof(value), "%d-%d", attr->values[0].range.lower, attr->values[0].range.upper);
11248           printer->num_options = cupsAddOption(name, value,
11249 					       printer->num_options,
11250 					       &(printer->options));
11251           cupsdLogMessage(CUPSD_LOG_DEBUG,
11252 	                  "Setting %s to %s...", attr->name, value);
11253           break;
11254 
11255       case IPP_TAG_RESOLUTION :
11256           snprintf(value, sizeof(value), "%dx%d%s", attr->values[0].resolution.xres, attr->values[0].resolution.yres, attr->values[0].resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
11257           printer->num_options = cupsAddOption(name, value,
11258 					       printer->num_options,
11259 					       &(printer->options));
11260           cupsdLogMessage(CUPSD_LOG_DEBUG,
11261 	                  "Setting %s to %s...", attr->name, value);
11262           break;
11263 
11264       default :
11265           /* Do nothing for other values */
11266 	  break;
11267     }
11268   }
11269 
11270   return (1);
11271 
11272  /*
11273   * If we get here this is a temporary printer and you can't set defaults for
11274   * this kind of queue...
11275   */
11276 
11277   temporary_printer:
11278 
11279   send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("Unable to save value for \"%s\" with a temporary printer."), attr->name);
11280 
11281   return (0);
11282 }
11283 
11284 
11285 /*
11286  * 'start_printer()' - Start a printer.
11287  */
11288 
11289 static void
start_printer(cupsd_client_t * con,ipp_attribute_t * uri)11290 start_printer(cupsd_client_t  *con,	/* I - Client connection */
11291               ipp_attribute_t *uri)	/* I - Printer URI */
11292 {
11293   int			i;		/* Temporary variable */
11294   http_status_t		status;		/* Policy status */
11295   cups_ptype_t		dtype;		/* Destination type (printer/class) */
11296   cupsd_printer_t	*printer;	/* Printer data */
11297 
11298 
11299   cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", (void *)con,
11300                   con->number, uri->values[0].string.text);
11301 
11302  /*
11303   * Is the destination valid?
11304   */
11305 
11306   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11307   {
11308    /*
11309     * Bad URI...
11310     */
11311 
11312     send_ipp_status(con, IPP_NOT_FOUND,
11313                     _("The printer or class does not exist."));
11314     return;
11315   }
11316 
11317  /*
11318   * Check policy...
11319   */
11320 
11321   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11322   {
11323     send_http_error(con, status, printer);
11324     return;
11325   }
11326 
11327  /*
11328   * Start the printer...
11329   */
11330 
11331   printer->state_message[0] = '\0';
11332 
11333   cupsdStartPrinter(printer, 1);
11334 
11335   if (dtype & CUPS_PRINTER_CLASS)
11336     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
11337                     printer->name, get_username(con));
11338   else
11339     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
11340                     printer->name, get_username(con));
11341 
11342   cupsdCheckJobs();
11343 
11344  /*
11345   * Check quotas...
11346   */
11347 
11348   if ((i = check_quotas(con, printer)) < 0)
11349   {
11350     send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
11351     return;
11352   }
11353   else if (i == 0)
11354   {
11355     send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
11356     return;
11357   }
11358 
11359  /*
11360   * Everything was ok, so return OK status...
11361   */
11362 
11363   con->response->request.status.status_code = IPP_OK;
11364 }
11365 
11366 
11367 /*
11368  * 'stop_printer()' - Stop a printer.
11369  */
11370 
11371 static void
stop_printer(cupsd_client_t * con,ipp_attribute_t * uri)11372 stop_printer(cupsd_client_t  *con,	/* I - Client connection */
11373              ipp_attribute_t *uri)	/* I - Printer URI */
11374 {
11375   http_status_t		status;		/* Policy status */
11376   cups_ptype_t		dtype;		/* Destination type (printer/class) */
11377   cupsd_printer_t	*printer;	/* Printer data */
11378   ipp_attribute_t	*attr;		/* printer-state-message attribute */
11379 
11380 
11381   cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", (void *)con,
11382                   con->number, uri->values[0].string.text);
11383 
11384  /*
11385   * Is the destination valid?
11386   */
11387 
11388   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11389   {
11390    /*
11391     * Bad URI...
11392     */
11393 
11394     send_ipp_status(con, IPP_NOT_FOUND,
11395                     _("The printer or class does not exist."));
11396     return;
11397   }
11398 
11399  /*
11400   * Check policy...
11401   */
11402 
11403   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11404   {
11405     send_http_error(con, status, printer);
11406     return;
11407   }
11408 
11409  /*
11410   * Stop the printer...
11411   */
11412 
11413   if ((attr = ippFindAttribute(con->request, "printer-state-message",
11414                                IPP_TAG_TEXT)) == NULL)
11415     strlcpy(printer->state_message, "Paused", sizeof(printer->state_message));
11416   else
11417   {
11418     strlcpy(printer->state_message, attr->values[0].string.text,
11419             sizeof(printer->state_message));
11420   }
11421 
11422   cupsdStopPrinter(printer, 1);
11423 
11424   if (dtype & CUPS_PRINTER_CLASS)
11425     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
11426                     printer->name, get_username(con));
11427   else
11428     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
11429                     printer->name, get_username(con));
11430 
11431  /*
11432   * Everything was ok, so return OK status...
11433   */
11434 
11435   con->response->request.status.status_code = IPP_OK;
11436 }
11437 
11438 
11439 /*
11440  * 'url_encode_attr()' - URL-encode a string attribute.
11441  */
11442 
11443 static void
url_encode_attr(ipp_attribute_t * attr,char * buffer,size_t bufsize)11444 url_encode_attr(ipp_attribute_t *attr,	/* I - Attribute */
11445                 char            *buffer,/* I - String buffer */
11446 		size_t          bufsize)/* I - Size of buffer */
11447 {
11448   int	i;				/* Looping var */
11449   char	*bufptr,			/* Pointer into buffer */
11450 	*bufend;			/* End of buffer */
11451 
11452 
11453   strlcpy(buffer, attr->name, bufsize);
11454   bufptr = buffer + strlen(buffer);
11455   bufend = buffer + bufsize - 1;
11456 
11457   for (i = 0; i < attr->num_values; i ++)
11458   {
11459     if (bufptr >= bufend)
11460       break;
11461 
11462     if (i)
11463       *bufptr++ = ',';
11464     else
11465       *bufptr++ = '=';
11466 
11467     if (bufptr >= bufend)
11468       break;
11469 
11470     *bufptr++ = '\'';
11471 
11472     bufptr = url_encode_string(attr->values[i].string.text, bufptr, (size_t)(bufend - bufptr + 1));
11473 
11474     if (bufptr >= bufend)
11475       break;
11476 
11477     *bufptr++ = '\'';
11478   }
11479 
11480   *bufptr = '\0';
11481 }
11482 
11483 
11484 /*
11485  * 'url_encode_string()' - URL-encode a string.
11486  */
11487 
11488 static char *				/* O - End of string */
url_encode_string(const char * s,char * buffer,size_t bufsize)11489 url_encode_string(const char *s,	/* I - String */
11490                   char       *buffer,	/* I - String buffer */
11491 		  size_t     bufsize)	/* I - Size of buffer */
11492 {
11493   char	*bufptr,			/* Pointer into buffer */
11494 	*bufend;			/* End of buffer */
11495   static const char *hex = "0123456789ABCDEF";
11496 					/* Hex digits */
11497 
11498 
11499   bufptr = buffer;
11500   bufend = buffer + bufsize - 1;
11501 
11502   while (*s && bufptr < bufend)
11503   {
11504     if (*s == ' ' || *s == '%' || *s == '+')
11505     {
11506       if (bufptr >= (bufend - 2))
11507 	break;
11508 
11509       *bufptr++ = '%';
11510       *bufptr++ = hex[(*s >> 4) & 15];
11511       *bufptr++ = hex[*s & 15];
11512 
11513       s ++;
11514     }
11515     else if (*s == '\'' || *s == '\\')
11516     {
11517       if (bufptr >= (bufend - 1))
11518 	break;
11519 
11520       *bufptr++ = '\\';
11521       *bufptr++ = *s++;
11522     }
11523     else
11524       *bufptr++ = *s++;
11525   }
11526 
11527   *bufptr = '\0';
11528 
11529   return (bufptr);
11530 }
11531 
11532 
11533 /*
11534  * 'user_allowed()' - See if a user is allowed to print to a queue.
11535  */
11536 
11537 static int				/* O - 0 if not allowed, 1 if allowed */
user_allowed(cupsd_printer_t * p,const char * username)11538 user_allowed(cupsd_printer_t *p,	/* I - Printer or class */
11539              const char      *username)	/* I - Username */
11540 {
11541   struct passwd	*pw;			/* User password data */
11542   char		baseuser[256],		/* Base username */
11543 		*baseptr,		/* Pointer to "@" in base username */
11544 		*name;			/* Current user name */
11545 
11546 
11547   if (cupsArrayCount(p->users) == 0)
11548     return (1);
11549 
11550   if (!strcmp(username, "root"))
11551     return (1);
11552 
11553   if (strchr(username, '@'))
11554   {
11555    /*
11556     * Strip @REALM for username check...
11557     */
11558 
11559     strlcpy(baseuser, username, sizeof(baseuser));
11560 
11561     if ((baseptr = strchr(baseuser, '@')) != NULL)
11562       *baseptr = '\0';
11563 
11564     username = baseuser;
11565   }
11566 
11567   pw = getpwnam(username);
11568   endpwent();
11569 
11570   for (name = (char *)cupsArrayFirst(p->users);
11571        name;
11572        name = (char *)cupsArrayNext(p->users))
11573   {
11574     if (name[0] == '@')
11575     {
11576      /*
11577       * Check group membership...
11578       */
11579 
11580       if (cupsdCheckGroup(username, pw, name + 1))
11581         break;
11582     }
11583     else if (name[0] == '#')
11584     {
11585      /*
11586       * Check UUID...
11587       */
11588 
11589       if (cupsdCheckGroup(username, pw, name))
11590         break;
11591     }
11592     else if (!_cups_strcasecmp(username, name))
11593       break;
11594   }
11595 
11596   return ((name != NULL) != p->deny_users);
11597 }
11598 
11599 
11600 /*
11601  * 'validate_job()' - Validate printer options and destination.
11602  */
11603 
11604 static void
validate_job(cupsd_client_t * con,ipp_attribute_t * uri)11605 validate_job(cupsd_client_t  *con,	/* I - Client connection */
11606 	     ipp_attribute_t *uri)	/* I - Printer URI */
11607 {
11608   http_status_t		status;		/* Policy status */
11609   ipp_attribute_t	*attr;		/* Current attribute */
11610 #ifdef HAVE_TLS
11611   ipp_attribute_t	*auth_info;	/* auth-info attribute */
11612 #endif /* HAVE_TLS */
11613   ipp_attribute_t	*format,	/* Document-format attribute */
11614 			*name;		/* Job-name attribute */
11615   cups_ptype_t		dtype;		/* Destination type (printer/class) */
11616   char			super[MIME_MAX_SUPER],
11617 					/* Supertype of file */
11618 			type[MIME_MAX_TYPE];
11619 					/* Subtype of file */
11620   cupsd_printer_t	*printer;	/* Printer */
11621 
11622 
11623   cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", (void *)con,
11624                   con->number, uri->values[0].string.text);
11625 
11626  /*
11627   * OK, see if the client is sending the document compressed - CUPS
11628   * doesn't support compression yet...
11629   */
11630 
11631   if ((attr = ippFindAttribute(con->request, "compression",
11632                                IPP_TAG_KEYWORD)) != NULL)
11633   {
11634     if (strcmp(attr->values[0].string.text, "none")
11635 #ifdef HAVE_LIBZ
11636         && strcmp(attr->values[0].string.text, "gzip")
11637 #endif /* HAVE_LIBZ */
11638       )
11639     {
11640       send_ipp_status(con, IPP_ATTRIBUTES,
11641                       _("Unsupported 'compression' value \"%s\"."),
11642         	      attr->values[0].string.text);
11643       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
11644 	           "compression", NULL, attr->values[0].string.text);
11645       return;
11646     }
11647   }
11648 
11649  /*
11650   * Is it a format we support?
11651   */
11652 
11653   if ((format = ippFindAttribute(con->request, "document-format",
11654                                  IPP_TAG_MIMETYPE)) != NULL)
11655   {
11656     if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
11657                super, type) != 2)
11658     {
11659       send_ipp_status(con, IPP_BAD_REQUEST,
11660                       _("Bad 'document-format' value \"%s\"."),
11661 		      format->values[0].string.text);
11662       return;
11663     }
11664 
11665     _cupsRWLockRead(&MimeDatabase->lock);
11666 
11667     if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
11668 	!mimeType(MimeDatabase, super, type))
11669     {
11670       cupsdLogMessage(CUPSD_LOG_INFO,
11671                       "Hint: Do you have the raw file printing rules enabled?");
11672       send_ipp_status(con, IPP_DOCUMENT_FORMAT,
11673                       _("Unsupported 'document-format' value \"%s\"."),
11674 		      format->values[0].string.text);
11675       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
11676                    "document-format", NULL, format->values[0].string.text);
11677 
11678       _cupsRWUnlock(&MimeDatabase->lock);
11679 
11680       return;
11681     }
11682 
11683     _cupsRWUnlock(&MimeDatabase->lock);
11684   }
11685 
11686  /*
11687   * Is the job-hold-until value valid?
11688   */
11689 
11690   if ((attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_ZERO)) != NULL && ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || !ippValidateAttribute(attr)))
11691   {
11692     send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
11693     ippCopyAttribute(con->response, attr, 0);
11694     return;
11695   }
11696 
11697  /*
11698   * Is the job-name valid?
11699   */
11700 
11701   if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
11702   {
11703     if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
11704         name->num_values != 1 || !ippValidateAttribute(name))
11705     {
11706       if (StrictConformance)
11707       {
11708 	send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-name' value."));
11709 	ippCopyAttribute(con->response, name, 0);
11710 	return;
11711       }
11712       else
11713       {
11714         cupsdLogMessage(CUPSD_LOG_WARN, "Unsupported 'job-name' value, deleting from request.");
11715         ippDeleteAttribute(con->request, name);
11716       }
11717     }
11718   }
11719 
11720  /*
11721   * Is the destination valid?
11722   */
11723 
11724   if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
11725   {
11726    /*
11727     * Bad URI...
11728     */
11729 
11730     send_ipp_status(con, IPP_NOT_FOUND,
11731                     _("The printer or class does not exist."));
11732     return;
11733   }
11734 
11735  /*
11736   * Check policy...
11737   */
11738 
11739 #ifdef HAVE_TLS
11740   auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
11741 #endif /* HAVE_TLS */
11742 
11743   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
11744   {
11745     send_http_error(con, status, printer);
11746     return;
11747   }
11748   else if (printer->num_auth_info_required == 1 &&
11749            !strcmp(printer->auth_info_required[0], "negotiate") &&
11750            !con->username[0])
11751   {
11752     send_http_error(con, HTTP_UNAUTHORIZED, printer);
11753     return;
11754   }
11755 #ifdef HAVE_TLS
11756   else if (auth_info && !con->http->tls &&
11757            !httpAddrLocalhost(con->http->hostaddr))
11758   {
11759    /*
11760     * Require encryption of auth-info over non-local connections...
11761     */
11762 
11763     send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
11764     return;
11765   }
11766 #endif /* HAVE_TLS */
11767 
11768  /*
11769   * Everything was ok, so return OK status...
11770   */
11771 
11772   con->response->request.status.status_code = IPP_OK;
11773 }
11774 
11775 
11776 /*
11777  * 'validate_name()' - Make sure the printer name only contains valid chars.
11778  */
11779 
11780 static int			/* O - 0 if name is no good, 1 if good */
validate_name(const char * name)11781 validate_name(const char *name)	/* I - Name to check */
11782 {
11783   const char	*ptr;		/* Pointer into name */
11784 
11785 
11786  /*
11787   * Scan the whole name...
11788   */
11789 
11790   for (ptr = name; *ptr; ptr ++)
11791     if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
11792       return (0);
11793 
11794  /*
11795   * All the characters are good; validate the length, too...
11796   */
11797 
11798   return ((ptr - name) < 128);
11799 }
11800 
11801 
11802 /*
11803  * 'validate_user()' - Validate the user for the request.
11804  */
11805 
11806 static int				/* O - 1 if permitted, 0 otherwise */
validate_user(cupsd_job_t * job,cupsd_client_t * con,const char * owner,char * username,size_t userlen)11807 validate_user(cupsd_job_t    *job,	/* I - Job */
11808               cupsd_client_t *con,	/* I - Client connection */
11809               const char     *owner,	/* I - Owner of job/resource */
11810               char           *username,	/* O - Authenticated username */
11811 	      size_t         userlen)	/* I - Length of username */
11812 {
11813   cupsd_printer_t	*printer;	/* Printer for job */
11814 
11815 
11816   cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, userlen=" CUPS_LLFMT ")", job->id, con ? con->number : 0, owner ? owner : "(null)", (void *)username, CUPS_LLCAST userlen);
11817 
11818  /*
11819   * Validate input...
11820   */
11821 
11822   if (!con || !owner || !username || userlen <= 0)
11823     return (0);
11824 
11825  /*
11826   * Get the best authenticated username that is available.
11827   */
11828 
11829   strlcpy(username, get_username(con), userlen);
11830 
11831  /*
11832   * Check the username against the owner...
11833   */
11834 
11835   printer = cupsdFindDest(job->dest);
11836 
11837   return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
11838                            con, owner) == HTTP_OK);
11839 }
11840