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