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