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