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