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