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