~ubuntu-branches/ubuntu/utopic/cups/utopic

« back to all changes in this revision

Viewing changes to .pc/fixes-for-jobs-with-multiple-files-and-multiple-formats.patch/backend/ipp.c

  • Committer: Package Import Robot
  • Author(s): Didier Raboud, Till Kamppeter, Steve Langasek, Didier Raboud
  • Date: 2014-01-03 18:42:39 UTC
  • mfrom: (99.2.3 sid)
  • Revision ID: package-import@ubuntu.com-20140103184239-85wju2l7weie4dgo
Tags: 1.7.0-1
* New 1.7.0 upstream release

[ Till Kamppeter ]
* Refresh most patches with quilt
* Removed usb-backend-do-not-crash-if-usb-disabled-in-bios and
  cupsd-no-crash-on-avahi-threaded-poll-shutdown patches as they got
  applied upstream
* Removed drop-arch-specifics-from-doc patch as it is not needed
  anymore
* Updated drop_unnecessary_dependencies, manpage-hyphen-minus,
  manpage-translations and ppd-poll-with-client-conf patches manually
  to apply to the new CUPS version
* Added error counting exception from
  usb-backend-do-not-crash-if-usb-disabled-in-bios to
  tests-ignore-warnings
* Install the newly added ippfind utility and its manpage in
  cups-client
* Added pwg.h to libcups2-dev package
* Call dh_auto_clean only if the file Makedefs is present, to avoid a
  FTBFS
* Added color management extensions from Joe Simon's GSoC 2013
  project.
* Patch cups-files.conf to activate CUPS daemon syncing of files when
  closing, so that config files (like printers.conf) do not
  mysteriously disappear (LP: #1157972)
* In the AppArmor profile, allow execution of programs in
  /etc/cups/interfaces/, needed to make CUPS working with queues based
  on System V interface scripts, especially PPD-less queues
  auto-generated by cups-browsed from cups-filters 1.0.41 on.
* Silenced AppArmor noise from udev.conf in syslog (LP: #1229766)

[ Steve Langasek ]
* Add cups-filters (>= 1.0.42) as alternative to foomatic-filters
  (which is deprecated) in package relationships

[ Didier Raboud ]
* Remove Roger Leigh from uploaders on his request with thanks for his
  past work!
* Switch avahi LSB Should-Start dependency to be avahi-daemon; also
  bump package relationship to >= 0.6.31-3~ (Closes: #731608)
* Refresh the manpage translation files
* Move the USB backend quirk rules file to cups-server-common
* Add 38 new 1.7.0 libcups2 symbols
* Mark one C++ libcupsppdc1 symbol as optional as it isn't exported in
  1.7.0 anymore
* Import Fedora patches:
  - to avoid sign-extending CRCs in gz decompression
  - to build with full read-only relocations
  - to fix job history logging (upstream patch)
  - to set the internal default for SyncOnClose to Yes, instead of
    only configuring it to Yes
  - to fix a stringpool corruption issue
  - to prevent USB timeouts causing incorrect print output
* Import Fedora patch updates:
  - to dont-use-dbus-from-two-threads patch so it removes a call to
    avahi_threaded_poll_stop()
  - to avoid_stale_lockfile_in_dbus_notifier patch to call _exit when
    handling SIGTERM
* Move manpage-translations patch at the very end of the patch series
  to have it include all our patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * "$Id: ipp.c 11353 2013-10-23 19:53:08Z msweet $"
 
3
 *
 
4
 * IPP backend for CUPS.
 
5
 *
 
6
 * Copyright 2007-2013 by Apple Inc.
 
7
 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
 
8
 *
 
9
 * These coded instructions, statements, and computer programs are the
 
10
 * property of Apple Inc. and are protected by Federal copyright
 
11
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
 
12
 * "LICENSE" which should have been included with this file.  If this
 
13
 * file is missing or damaged, see the license at "http://www.cups.org/".
 
14
 *
 
15
 * This file is subject to the Apple OS-Developed Software exception.
 
16
 */
 
17
 
 
18
/*
 
19
 * Include necessary headers.
 
20
 */
 
21
 
 
22
#include "backend-private.h"
 
23
#include <cups/array-private.h>
 
24
#include <sys/types.h>
 
25
#include <sys/stat.h>
 
26
#include <sys/wait.h>
 
27
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
 
28
#  include <xpc/xpc.h>
 
29
#  define kPMPrintUIToolAgent   "com.apple.printuitool.agent"
 
30
#  define kPMStartJob           100
 
31
#  define kPMWaitForJob         101
 
32
#  ifdef HAVE_XPC_PRIVATE_H
 
33
#    include <xpc/private.h>
 
34
#  else
 
35
extern void     xpc_connection_set_target_uid(xpc_connection_t connection,
 
36
                                              uid_t uid);
 
37
#  endif /* HAVE_XPC_PRIVATE_H */
 
38
#endif /* HAVE_GSSAPI && HAVE_XPC */
 
39
 
 
40
 
 
41
/*
 
42
 * Bits for job-state-reasons we care about...
 
43
 */
 
44
 
 
45
#define _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED  0x01
 
46
#define _CUPS_JSR_ACCOUNT_CLOSED                0x02
 
47
#define _CUPS_JSR_ACCOUNT_INFO_NEEDED           0x04
 
48
#define _CUPS_JSR_ACCOUNT_LIMIT_REACHED         0x08
 
49
#define _CUPS_JSR_JOB_PASSWORD_WAIT             0x10
 
50
#define _CUPS_JSR_JOB_RELEASE_WAIT              0x20
 
51
 
 
52
 
 
53
/*
 
54
 * Types...
 
55
 */
 
56
 
 
57
typedef struct _cups_monitor_s          /**** Monitoring data ****/
 
58
{
 
59
  const char            *uri,           /* Printer URI */
 
60
                        *hostname,      /* Hostname */
 
61
                        *user,          /* Username */
 
62
                        *resource;      /* Resource path */
 
63
  int                   port,           /* Port number */
 
64
                        version,        /* IPP version */
 
65
                        job_id,         /* Job ID for submitted job */
 
66
                        job_reasons,    /* Job state reasons bits */
 
67
                        get_job_attrs;  /* Support Get-Job-Attributes? */
 
68
  const char            *job_name;      /* Job name for submitted job */
 
69
  http_encryption_t     encryption;     /* Use encryption? */
 
70
  ipp_jstate_t          job_state;      /* Current job state */
 
71
  ipp_pstate_t          printer_state;  /* Current printer state */
 
72
} _cups_monitor_t;
 
73
 
 
74
 
 
75
/*
 
76
 * Globals...
 
77
 */
 
78
 
 
79
static const char       *auth_info_required;
 
80
                                        /* New auth-info-required value */
 
81
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
 
82
static int              child_pid = 0;  /* Child process ID */
 
83
#endif /* HAVE_GSSAPI && HAVE_XPC */
 
84
static const char * const jattrs[] =    /* Job attributes we want */
 
85
{
 
86
  "job-id",
 
87
  "job-impressions-completed",
 
88
  "job-media-sheets-completed",
 
89
  "job-name",
 
90
  "job-originating-user-name",
 
91
  "job-state",
 
92
  "job-state-reasons"
 
93
};
 
94
static int              job_canceled = 0;
 
95
                                        /* Job cancelled? */
 
96
static char             username[256] = "",
 
97
                                        /* Username for device URI */
 
98
                        *password = NULL;
 
99
                                        /* Password for device URI */
 
100
static const char * const pattrs[] =    /* Printer attributes we want */
 
101
{
 
102
#ifdef HAVE_LIBZ
 
103
  "compression-supported",
 
104
#endif /* HAVE_LIBZ */
 
105
  "copies-supported",
 
106
  "cups-version",
 
107
  "document-format-supported",
 
108
  "marker-colors",
 
109
  "marker-high-levels",
 
110
  "marker-levels",
 
111
  "marker-low-levels",
 
112
  "marker-message",
 
113
  "marker-names",
 
114
  "marker-types",
 
115
  "media-col-supported",
 
116
  "multiple-document-handling-supported",
 
117
  "operations-supported",
 
118
  "print-color-mode-supported",
 
119
  "printer-alert",
 
120
  "printer-alert-description",
 
121
  "printer-is-accepting-jobs",
 
122
  "printer-state",
 
123
  "printer-state-message",
 
124
  "printer-state-reasons"
 
125
};
 
126
static const char * const remote_job_states[] =
 
127
{                                       /* Remote job state keywords */
 
128
  "+cups-remote-pending",
 
129
  "+cups-remote-pending-held",
 
130
  "+cups-remote-processing",
 
131
  "+cups-remote-stopped",
 
132
  "+cups-remote-canceled",
 
133
  "+cups-remote-aborted",
 
134
  "+cups-remote-completed"
 
135
};
 
136
static _cups_mutex_t    report_mutex = _CUPS_MUTEX_INITIALIZER;
 
137
                                        /* Mutex to control access */
 
138
static int              num_attr_cache = 0;
 
139
                                        /* Number of cached attributes */
 
140
static cups_option_t    *attr_cache = NULL;
 
141
                                        /* Cached attributes */
 
142
static cups_array_t     *state_reasons; /* Array of printe-state-reasons keywords */
 
143
static char             tmpfilename[1024] = "";
 
144
                                        /* Temporary spool file name */
 
145
 
 
146
 
 
147
/*
 
148
 * Local functions...
 
149
 */
 
150
 
 
151
static void             cancel_job(http_t *http, const char *uri, int id,
 
152
                                   const char *resource, const char *user,
 
153
                                   int version);
 
154
static ipp_pstate_t     check_printer_state(http_t *http, const char *uri,
 
155
                                            const char *resource,
 
156
                                            const char *user, int version);
 
157
static void             *monitor_printer(_cups_monitor_t *monitor);
 
158
static ipp_t            *new_request(ipp_op_t op, int version, const char *uri,
 
159
                                     const char *user, const char *title,
 
160
                                     int num_options, cups_option_t *options,
 
161
                                     const char *compression, int copies,
 
162
                                     const char *format, _ppd_cache_t *pc,
 
163
                                     ppd_file_t *ppd,
 
164
                                     ipp_attribute_t *media_col_sup,
 
165
                                     ipp_attribute_t *doc_handling_sup,
 
166
                                     int print_color_mode);
 
167
static const char       *password_cb(const char *prompt, http_t *http,
 
168
                                     const char *method, const char *resource,
 
169
                                     int *user_data);
 
170
static const char       *quote_string(const char *s, char *q, size_t qsize);
 
171
static void             report_attr(ipp_attribute_t *attr);
 
172
static void             report_printer_state(ipp_t *ipp);
 
173
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
 
174
static int              run_as_user(char *argv[], uid_t uid,
 
175
                                    const char *device_uri, int fd);
 
176
#endif /* HAVE_GSSAPI && HAVE_XPC */
 
177
static void             sigterm_handler(int sig);
 
178
static int              timeout_cb(http_t *http, void *user_data);
 
179
static void             update_reasons(ipp_attribute_t *attr, const char *s);
 
180
 
 
181
 
 
182
/*
 
183
 * 'main()' - Send a file to the printer or server.
 
184
 *
 
185
 * Usage:
 
186
 *
 
187
 *    printer-uri job-id user title copies options [file]
 
188
 */
 
189
 
 
190
int                                     /* O - Exit status */
 
191
main(int  argc,                         /* I - Number of command-line args */
 
192
     char *argv[])                      /* I - Command-line arguments */
 
193
{
 
194
  int           i;                      /* Looping var */
 
195
  int           send_options;           /* Send job options? */
 
196
  int           num_options;            /* Number of printer options */
 
197
  cups_option_t *options;               /* Printer options */
 
198
  const char    *device_uri;            /* Device URI */
 
199
  char          scheme[255],            /* Scheme in URI */
 
200
                hostname[1024],         /* Hostname */
 
201
                resource[1024],         /* Resource info (printer name) */
 
202
                addrname[256],          /* Address name */
 
203
                *optptr,                /* Pointer to URI options */
 
204
                *name,                  /* Name of option */
 
205
                *value,                 /* Value of option */
 
206
                sep;                    /* Separator character */
 
207
  int           password_tries = 0;     /* Password tries */
 
208
  http_addrlist_t *addrlist;            /* Address of printer */
 
209
  int           snmp_enabled = 1;       /* Is SNMP enabled? */
 
210
  int           snmp_fd,                /* SNMP socket */
 
211
                start_count,            /* Page count via SNMP at start */
 
212
                page_count,             /* Page count via SNMP */
 
213
                have_supplies;          /* Printer supports supply levels? */
 
214
  int           num_files;              /* Number of files to print */
 
215
  char          **files,                /* Files to print */
 
216
                *compatfile = NULL;     /* Compatibility filename */
 
217
  off_t         compatsize = 0;         /* Size of compatibility file */
 
218
  int           port;                   /* Port number (not used) */
 
219
  char          portname[255];          /* Port name */
 
220
  char          uri[HTTP_MAX_URI];      /* Updated URI without user/pass */
 
221
  char          print_job_name[1024];   /* Update job-name for Print-Job */
 
222
  http_status_t http_status;            /* Status of HTTP request */
 
223
  ipp_status_t  ipp_status;             /* Status of IPP request */
 
224
  http_t        *http;                  /* HTTP connection */
 
225
  ipp_t         *request,               /* IPP request */
 
226
                *response,              /* IPP response */
 
227
                *supported;             /* get-printer-attributes response */
 
228
  time_t        start_time;             /* Time of first connect */
 
229
  int           contimeout;             /* Connection timeout */
 
230
  int           delay,                  /* Delay for retries */
 
231
                prev_delay;             /* Previous delay */
 
232
  const char    *compression;           /* Compression mode */
 
233
  int           waitjob,                /* Wait for job complete? */
 
234
                waitjob_tries = 0,      /* Number of times we've waited */
 
235
                waitprinter;            /* Wait for printer ready? */
 
236
  _cups_monitor_t monitor;              /* Monitoring data */
 
237
  ipp_attribute_t *job_id_attr;         /* job-id attribute */
 
238
  int           job_id;                 /* job-id value */
 
239
  ipp_attribute_t *job_sheets;          /* job-media-sheets-completed */
 
240
  ipp_attribute_t *job_state;           /* job-state */
 
241
#ifdef HAVE_LIBZ
 
242
  ipp_attribute_t *compression_sup;     /* compression-supported */
 
243
#endif /* HAVE_LIBZ */
 
244
  ipp_attribute_t *copies_sup;          /* copies-supported */
 
245
  ipp_attribute_t *cups_version;        /* cups-version */
 
246
  ipp_attribute_t *format_sup;          /* document-format-supported */
 
247
  ipp_attribute_t *job_auth;            /* job-authorization-uri */
 
248
  ipp_attribute_t *media_col_sup;       /* media-col-supported */
 
249
  ipp_attribute_t *operations_sup;      /* operations-supported */
 
250
  ipp_attribute_t *doc_handling_sup;    /* multiple-document-handling-supported */
 
251
  ipp_attribute_t *printer_state;       /* printer-state attribute */
 
252
  ipp_attribute_t *printer_accepting;   /* printer-is-accepting-jobs */
 
253
  int           create_job = 0,         /* Does printer support Create-Job? */
 
254
                get_job_attrs = 0,      /* Does printer support Get-Job-Attributes? */
 
255
                send_document = 0,      /* Does printer support Send-Document? */
 
256
                validate_job = 0,       /* Does printer support Validate-Job? */
 
257
                print_color_mode = 0;   /* Does printer support print-color-mode? */
 
258
  int           copies,                 /* Number of copies for job */
 
259
                copies_remaining;       /* Number of copies remaining */
 
260
  const char    *content_type,          /* CONTENT_TYPE environment variable */
 
261
                *final_content_type,    /* FINAL_CONTENT_TYPE environment var */
 
262
                *document_format;       /* document-format value */
 
263
  int           fd;                     /* File descriptor */
 
264
  off_t         bytes = 0;              /* Bytes copied */
 
265
  char          buffer[16384];          /* Copy buffer */
 
266
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
 
267
  struct sigaction action;              /* Actions for POSIX signals */
 
268
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
 
269
  int           version;                /* IPP version */
 
270
  ppd_file_t    *ppd = NULL;            /* PPD file */
 
271
  _ppd_cache_t  *pc = NULL;             /* PPD cache and mapping data */
 
272
  fd_set        input;                  /* Input set for select() */
 
273
 
 
274
 
 
275
 /*
 
276
  * Make sure status messages are not buffered...
 
277
  */
 
278
 
 
279
  setbuf(stderr, NULL);
 
280
 
 
281
 /*
 
282
  * Ignore SIGPIPE and catch SIGTERM signals...
 
283
  */
 
284
 
 
285
#ifdef HAVE_SIGSET
 
286
  sigset(SIGPIPE, SIG_IGN);
 
287
  sigset(SIGTERM, sigterm_handler);
 
288
#elif defined(HAVE_SIGACTION)
 
289
  memset(&action, 0, sizeof(action));
 
290
  action.sa_handler = SIG_IGN;
 
291
  sigaction(SIGPIPE, &action, NULL);
 
292
 
 
293
  sigemptyset(&action.sa_mask);
 
294
  sigaddset(&action.sa_mask, SIGTERM);
 
295
  action.sa_handler = sigterm_handler;
 
296
  sigaction(SIGTERM, &action, NULL);
 
297
#else
 
298
  signal(SIGPIPE, SIG_IGN);
 
299
  signal(SIGTERM, sigterm_handler);
 
300
#endif /* HAVE_SIGSET */
 
301
 
 
302
 /*
 
303
  * Check command-line...
 
304
  */
 
305
 
 
306
  if (argc == 1)
 
307
  {
 
308
    char *s;
 
309
 
 
310
    if ((s = strrchr(argv[0], '/')) != NULL)
 
311
      s ++;
 
312
    else
 
313
      s = argv[0];
 
314
 
 
315
    printf("network %s \"Unknown\" \"%s (%s)\"\n",
 
316
           s, _cupsLangString(cupsLangDefault(),
 
317
                              _("Internet Printing Protocol")), s);
 
318
    return (CUPS_BACKEND_OK);
 
319
  }
 
320
  else if (argc < 6)
 
321
  {
 
322
    _cupsLangPrintf(stderr,
 
323
                    _("Usage: %s job-id user title copies options [file]"),
 
324
                    argv[0]);
 
325
    return (CUPS_BACKEND_STOP);
 
326
  }
 
327
 
 
328
 /*
 
329
  * Get the device URI...
 
330
  */
 
331
 
 
332
  while ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
 
333
  {
 
334
    _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
 
335
    sleep(10);
 
336
 
 
337
    if (getenv("CLASS") != NULL)
 
338
      return (CUPS_BACKEND_FAILED);
 
339
  }
 
340
 
 
341
  if ((auth_info_required = getenv("AUTH_INFO_REQUIRED")) == NULL)
 
342
    auth_info_required = "none";
 
343
 
 
344
  state_reasons = _cupsArrayNewStrings(getenv("PRINTER_STATE_REASONS"), ',');
 
345
 
 
346
#ifdef HAVE_GSSAPI
 
347
 /*
 
348
  * For Kerberos, become the printing user (if we can) to get the credentials
 
349
  * that way.
 
350
  */
 
351
 
 
352
  if (!getuid() && (value = getenv("AUTH_UID")) != NULL &&
 
353
      !getenv("AUTH_PASSWORD"))
 
354
  {
 
355
    uid_t       uid = (uid_t)atoi(value);
 
356
                                        /* User ID */
 
357
 
 
358
#  ifdef HAVE_XPC
 
359
    if (uid > 0)
 
360
    {
 
361
      if (argc == 6)
 
362
        return (run_as_user(argv, uid, device_uri, 0));
 
363
      else
 
364
      {
 
365
        int status = 0;                 /* Exit status */
 
366
 
 
367
        for (i = 6; i < argc && !status && !job_canceled; i ++)
 
368
        {
 
369
          if ((fd = open(argv[i], O_RDONLY)) >= 0)
 
370
          {
 
371
            status = run_as_user(argv, uid, device_uri, fd);
 
372
            close(fd);
 
373
          }
 
374
          else
 
375
          {
 
376
            _cupsLangPrintError("ERROR", _("Unable to open print file"));
 
377
            status = CUPS_BACKEND_FAILED;
 
378
          }
 
379
        }
 
380
 
 
381
        return (status);
 
382
      }
 
383
    }
 
384
 
 
385
#  else /* No XPC, just try to run as the user ID */
 
386
    if (uid > 0)
 
387
      seteuid(uid);
 
388
#  endif /* HAVE_XPC */
 
389
  }
 
390
#endif /* HAVE_GSSAPI */
 
391
 
 
392
 /*
 
393
  * Get the (final) content type...
 
394
  */
 
395
 
 
396
  if ((content_type = getenv("CONTENT_TYPE")) == NULL)
 
397
    content_type = "application/octet-stream";
 
398
 
 
399
  if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
 
400
  {
 
401
    final_content_type = content_type;
 
402
 
 
403
    if (!strncmp(final_content_type, "printer/", 8))
 
404
      final_content_type = "application/vnd.cups-raw";
 
405
  }
 
406
 
 
407
 /*
 
408
  * Extract the hostname and printer name from the URI...
 
409
  */
 
410
 
 
411
  httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
 
412
                  username, sizeof(username), hostname, sizeof(hostname), &port,
 
413
                  resource, sizeof(resource));
 
414
 
 
415
  if (!port)
 
416
    port = IPP_PORT;                    /* Default to port 631 */
 
417
 
 
418
  if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
 
419
    cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
 
420
  else
 
421
    cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
 
422
 
 
423
 /*
 
424
  * See if there are any options...
 
425
  */
 
426
 
 
427
  compression = NULL;
 
428
  version     = 20;
 
429
  waitjob     = 1;
 
430
  waitprinter = 1;
 
431
  contimeout  = 7 * 24 * 60 * 60;
 
432
 
 
433
  if ((optptr = strchr(resource, '?')) != NULL)
 
434
  {
 
435
   /*
 
436
    * Yup, terminate the device name string and move to the first
 
437
    * character of the optptr...
 
438
    */
 
439
 
 
440
    *optptr++ = '\0';
 
441
 
 
442
   /*
 
443
    * Then parse the optptr...
 
444
    */
 
445
 
 
446
    while (*optptr)
 
447
    {
 
448
     /*
 
449
      * Get the name...
 
450
      */
 
451
 
 
452
      name = optptr;
 
453
 
 
454
      while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
 
455
        optptr ++;
 
456
 
 
457
      if ((sep = *optptr) != '\0')
 
458
        *optptr++ = '\0';
 
459
 
 
460
      if (sep == '=')
 
461
      {
 
462
       /*
 
463
        * Get the value...
 
464
        */
 
465
 
 
466
        value = optptr;
 
467
 
 
468
        while (*optptr && *optptr != '+' && *optptr != '&')
 
469
          optptr ++;
 
470
 
 
471
        if (*optptr)
 
472
          *optptr++ = '\0';
 
473
      }
 
474
      else
 
475
        value = (char *)"";
 
476
 
 
477
     /*
 
478
      * Process the option...
 
479
      */
 
480
 
 
481
      if (!_cups_strcasecmp(name, "waitjob"))
 
482
      {
 
483
       /*
 
484
        * Wait for job completion?
 
485
        */
 
486
 
 
487
        waitjob = !_cups_strcasecmp(value, "on") ||
 
488
                  !_cups_strcasecmp(value, "yes") ||
 
489
                  !_cups_strcasecmp(value, "true");
 
490
      }
 
491
      else if (!_cups_strcasecmp(name, "waitprinter"))
 
492
      {
 
493
       /*
 
494
        * Wait for printer idle?
 
495
        */
 
496
 
 
497
        waitprinter = !_cups_strcasecmp(value, "on") ||
 
498
                      !_cups_strcasecmp(value, "yes") ||
 
499
                      !_cups_strcasecmp(value, "true");
 
500
      }
 
501
      else if (!_cups_strcasecmp(name, "encryption"))
 
502
      {
 
503
       /*
 
504
        * Enable/disable encryption?
 
505
        */
 
506
 
 
507
        if (!_cups_strcasecmp(value, "always"))
 
508
          cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
 
509
        else if (!_cups_strcasecmp(value, "required"))
 
510
          cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
 
511
        else if (!_cups_strcasecmp(value, "never"))
 
512
          cupsSetEncryption(HTTP_ENCRYPT_NEVER);
 
513
        else if (!_cups_strcasecmp(value, "ifrequested"))
 
514
          cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
 
515
        else
 
516
        {
 
517
          _cupsLangPrintFilter(stderr, "ERROR",
 
518
                               _("Unknown encryption option value: \"%s\"."),
 
519
                               value);
 
520
        }
 
521
      }
 
522
      else if (!_cups_strcasecmp(name, "snmp"))
 
523
      {
 
524
        /*
 
525
         * Enable/disable SNMP stuff...
 
526
         */
 
527
 
 
528
         snmp_enabled = !value[0] || !_cups_strcasecmp(value, "on") ||
 
529
                        _cups_strcasecmp(value, "yes") ||
 
530
                        _cups_strcasecmp(value, "true");
 
531
      }
 
532
      else if (!_cups_strcasecmp(name, "version"))
 
533
      {
 
534
        if (!strcmp(value, "1.0"))
 
535
          version = 10;
 
536
        else if (!strcmp(value, "1.1"))
 
537
          version = 11;
 
538
        else if (!strcmp(value, "2.0"))
 
539
          version = 20;
 
540
        else if (!strcmp(value, "2.1"))
 
541
          version = 21;
 
542
        else if (!strcmp(value, "2.2"))
 
543
          version = 22;
 
544
        else
 
545
        {
 
546
          _cupsLangPrintFilter(stderr, "ERROR",
 
547
                               _("Unknown version option value: \"%s\"."),
 
548
                               value);
 
549
        }
 
550
      }
 
551
#ifdef HAVE_LIBZ
 
552
      else if (!_cups_strcasecmp(name, "compression"))
 
553
      {
 
554
        if (!_cups_strcasecmp(value, "true") || !_cups_strcasecmp(value, "yes") ||
 
555
            !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "gzip"))
 
556
          compression = "gzip";
 
557
        else if (!_cups_strcasecmp(value, "deflate"))
 
558
          compression = "deflate";
 
559
        else if (!_cups_strcasecmp(value, "false") ||
 
560
                 !_cups_strcasecmp(value, "no") ||
 
561
                 !_cups_strcasecmp(value, "off") ||
 
562
                 !_cups_strcasecmp(value, "none"))
 
563
          compression = "none";
 
564
      }
 
565
#endif /* HAVE_LIBZ */
 
566
      else if (!_cups_strcasecmp(name, "contimeout"))
 
567
      {
 
568
       /*
 
569
        * Set the connection timeout...
 
570
        */
 
571
 
 
572
        if (atoi(value) > 0)
 
573
          contimeout = atoi(value);
 
574
      }
 
575
      else
 
576
      {
 
577
       /*
 
578
        * Unknown option...
 
579
        */
 
580
 
 
581
        _cupsLangPrintFilter(stderr, "ERROR",
 
582
                             _("Unknown option \"%s\" with value \"%s\"."),
 
583
                             name, value);
 
584
      }
 
585
    }
 
586
  }
 
587
 
 
588
 /*
 
589
  * If we have 7 arguments, print the file named on the command-line.
 
590
  * Otherwise, copy stdin to a temporary file and print the temporary
 
591
  * file.
 
592
  */
 
593
 
 
594
  if (argc == 6)
 
595
  {
 
596
    num_files    = 0;
 
597
    files        = NULL;
 
598
    send_options = !_cups_strcasecmp(final_content_type, "application/pdf") ||
 
599
                   !_cups_strcasecmp(final_content_type, "application/vnd.cups-pdf") ||
 
600
                   !_cups_strncasecmp(final_content_type, "image/", 6);
 
601
 
 
602
    fputs("DEBUG: Sending stdin for job...\n", stderr);
 
603
  }
 
604
  else
 
605
  {
 
606
   /*
 
607
    * Point to the files on the command-line...
 
608
    */
 
609
 
 
610
    num_files    = argc - 6;
 
611
    files        = argv + 6;
 
612
    send_options = 1;
 
613
 
 
614
    fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
 
615
  }
 
616
 
 
617
 /*
 
618
  * Set the authentication info, if any...
 
619
  */
 
620
 
 
621
  cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
 
622
 
 
623
  if (username[0])
 
624
  {
 
625
   /*
 
626
    * Use authentication information in the device URI...
 
627
    */
 
628
 
 
629
    if ((password = strchr(username, ':')) != NULL)
 
630
      *password++ = '\0';
 
631
 
 
632
    cupsSetUser(username);
 
633
  }
 
634
  else
 
635
  {
 
636
   /*
 
637
    * Try loading authentication information from the environment.
 
638
    */
 
639
 
 
640
    const char *ptr = getenv("AUTH_USERNAME");
 
641
 
 
642
    if (ptr)
 
643
    {
 
644
      strlcpy(username, ptr, sizeof(username));
 
645
      cupsSetUser(ptr);
 
646
    }
 
647
 
 
648
    password = getenv("AUTH_PASSWORD");
 
649
  }
 
650
 
 
651
 /*
 
652
  * Try finding the remote server...
 
653
  */
 
654
 
 
655
  start_time = time(NULL);
 
656
 
 
657
  sprintf(portname, "%d", port);
 
658
 
 
659
  update_reasons(NULL, "+connecting-to-device");
 
660
  fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
 
661
 
 
662
  while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL)
 
663
  {
 
664
    _cupsLangPrintFilter(stderr, "INFO",
 
665
                         _("Unable to locate printer \"%s\"."), hostname);
 
666
    sleep(10);
 
667
 
 
668
    if (getenv("CLASS") != NULL)
 
669
    {
 
670
      update_reasons(NULL, "-connecting-to-device");
 
671
      return (CUPS_BACKEND_STOP);
 
672
    }
 
673
 
 
674
    if (job_canceled)
 
675
      return (CUPS_BACKEND_OK);
 
676
  }
 
677
 
 
678
  http = httpConnect2(hostname, port, addrlist, AF_UNSPEC, cupsEncryption(), 1,
 
679
                      0, NULL);
 
680
  httpSetTimeout(http, 30.0, timeout_cb, NULL);
 
681
 
 
682
 /*
 
683
  * See if the printer supports SNMP...
 
684
  */
 
685
 
 
686
  if (snmp_enabled)
 
687
    snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
 
688
  else
 
689
    snmp_fd = -1;
 
690
 
 
691
  if (snmp_fd >= 0)
 
692
    have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr),
 
693
                                         &start_count, NULL);
 
694
  else
 
695
    have_supplies = start_count = 0;
 
696
 
 
697
 /*
 
698
  * Wait for data from the filter...
 
699
  */
 
700
 
 
701
  if (num_files == 0)
 
702
  {
 
703
    if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 0, backendNetworkSideCB))
 
704
      return (CUPS_BACKEND_OK);
 
705
    else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0)
 
706
      return (CUPS_BACKEND_OK);
 
707
  }
 
708
 
 
709
 /*
 
710
  * Try connecting to the remote server...
 
711
  */
 
712
 
 
713
  delay = _cupsNextDelay(0, &prev_delay);
 
714
 
 
715
  do
 
716
  {
 
717
    fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
 
718
    _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer."));
 
719
 
 
720
    if (httpReconnect(http))
 
721
    {
 
722
      int error = errno;                /* Connection error */
 
723
 
 
724
      if (http->status == HTTP_PKI_ERROR)
 
725
        update_reasons(NULL, "+cups-certificate-error");
 
726
 
 
727
      if (job_canceled)
 
728
        break;
 
729
 
 
730
      if (getenv("CLASS") != NULL)
 
731
      {
 
732
       /*
 
733
        * If the CLASS environment variable is set, the job was submitted
 
734
        * to a class and not to a specific queue.  In this case, we want
 
735
        * to abort immediately so that the job can be requeued on the next
 
736
        * available printer in the class.
 
737
        */
 
738
 
 
739
        _cupsLangPrintFilter(stderr, "INFO",
 
740
                             _("Unable to contact printer, queuing on next "
 
741
                               "printer in class."));
 
742
 
 
743
       /*
 
744
        * Sleep 5 seconds to keep the job from requeuing too rapidly...
 
745
        */
 
746
 
 
747
        sleep(5);
 
748
 
 
749
        update_reasons(NULL, "-connecting-to-device");
 
750
 
 
751
        return (CUPS_BACKEND_FAILED);
 
752
      }
 
753
 
 
754
      fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
 
755
 
 
756
      if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
 
757
          errno == EHOSTUNREACH)
 
758
      {
 
759
        if (contimeout && (time(NULL) - start_time) > contimeout)
 
760
        {
 
761
          _cupsLangPrintFilter(stderr, "ERROR",
 
762
                               _("The printer is not responding."));
 
763
          update_reasons(NULL, "-connecting-to-device");
 
764
          return (CUPS_BACKEND_FAILED);
 
765
        }
 
766
 
 
767
        switch (error)
 
768
        {
 
769
          case EHOSTDOWN :
 
770
              _cupsLangPrintFilter(stderr, "WARNING",
 
771
                                   _("The printer may not exist or "
 
772
                                     "is unavailable at this time."));
 
773
              break;
 
774
 
 
775
          case EHOSTUNREACH :
 
776
              _cupsLangPrintFilter(stderr, "WARNING",
 
777
                                   _("The printer is unreachable at this "
 
778
                                     "time."));
 
779
              break;
 
780
 
 
781
          case ECONNREFUSED :
 
782
          default :
 
783
              _cupsLangPrintFilter(stderr, "WARNING",
 
784
                                   _("The printer is in use."));
 
785
              break;
 
786
        }
 
787
 
 
788
        sleep(delay);
 
789
 
 
790
        delay = _cupsNextDelay(delay, &prev_delay);
 
791
      }
 
792
      else
 
793
      {
 
794
        _cupsLangPrintFilter(stderr, "ERROR",
 
795
                             _("The printer is not responding."));
 
796
        sleep(30);
 
797
      }
 
798
 
 
799
      if (job_canceled)
 
800
        break;
 
801
    }
 
802
    else
 
803
      update_reasons(NULL, "-cups-certificate-error");
 
804
  }
 
805
  while (http->fd < 0);
 
806
 
 
807
  if (job_canceled)
 
808
    return (CUPS_BACKEND_OK);
 
809
  else if (!http)
 
810
    return (CUPS_BACKEND_FAILED);
 
811
 
 
812
  update_reasons(NULL, "-connecting-to-device");
 
813
  _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer."));
 
814
 
 
815
  fprintf(stderr, "DEBUG: Connected to %s:%d...\n",
 
816
          httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
 
817
          httpAddrPort(http->hostaddr));
 
818
 
 
819
 /*
 
820
  * Build a URI for the printer and fill the standard IPP attributes for
 
821
  * an IPP_PRINT_FILE request.  We can't use the URI in argv[0] because it
 
822
  * might contain username:password information...
 
823
  */
 
824
 
 
825
  httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
 
826
                  port, resource);
 
827
 
 
828
 /*
 
829
  * First validate the destination and see if the device supports multiple
 
830
  * copies...
 
831
  */
 
832
 
 
833
#ifdef HAVE_LIBZ
 
834
  compression_sup  = NULL;
 
835
#endif /* HAVE_LIBZ */
 
836
  copies_sup       = NULL;
 
837
  cups_version     = NULL;
 
838
  format_sup       = NULL;
 
839
  media_col_sup    = NULL;
 
840
  supported        = NULL;
 
841
  operations_sup   = NULL;
 
842
  doc_handling_sup = NULL;
 
843
 
 
844
  do
 
845
  {
 
846
   /*
 
847
    * Check for side-channel requests...
 
848
    */
 
849
 
 
850
    backendCheckSideChannel(snmp_fd, http->hostaddr);
 
851
 
 
852
   /*
 
853
    * Build the IPP request...
 
854
    */
 
855
 
 
856
    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
 
857
    request->request.op.version[0] = version / 10;
 
858
    request->request.op.version[1] = version % 10;
 
859
 
 
860
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
861
                 NULL, uri);
 
862
 
 
863
    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
864
                  "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
 
865
                  NULL, pattrs);
 
866
 
 
867
   /*
 
868
    * Do the request...
 
869
    */
 
870
 
 
871
    fputs("DEBUG: Getting supported attributes...\n", stderr);
 
872
 
 
873
    if (http->version < HTTP_1_1)
 
874
    {
 
875
      fprintf(stderr, "DEBUG: Printer responded with HTTP version %d.%d.\n",
 
876
              http->version / 100, http->version % 100);
 
877
      update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
878
                           "cups-ipp-wrong-http-version");
 
879
    }
 
880
 
 
881
    supported  = cupsDoRequest(http, request, resource);
 
882
    ipp_status = cupsLastError();
 
883
 
 
884
    fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
 
885
            ippErrorString(ipp_status), cupsLastErrorString());
 
886
 
 
887
    if (ipp_status <= IPP_OK_CONFLICT)
 
888
      password_tries = 0;
 
889
    else
 
890
    {
 
891
      fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
 
892
              ippErrorString(ipp_status));
 
893
 
 
894
      if (ipp_status == IPP_PRINTER_BUSY ||
 
895
          ipp_status == IPP_SERVICE_UNAVAILABLE)
 
896
      {
 
897
        if (contimeout && (time(NULL) - start_time) > contimeout)
 
898
        {
 
899
          _cupsLangPrintFilter(stderr, "ERROR",
 
900
                               _("The printer is not responding."));
 
901
          return (CUPS_BACKEND_FAILED);
 
902
        }
 
903
 
 
904
        _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
 
905
 
 
906
        report_printer_state(supported);
 
907
 
 
908
        sleep(delay);
 
909
 
 
910
        delay = _cupsNextDelay(delay, &prev_delay);
 
911
      }
 
912
      else if ((ipp_status == IPP_BAD_REQUEST ||
 
913
                ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
 
914
      {
 
915
       /*
 
916
        * Switch to IPP/1.1 or IPP/1.0...
 
917
        */
 
918
 
 
919
        if (version >= 20)
 
920
        {
 
921
          _cupsLangPrintFilter(stderr, "INFO", _("Preparing to print."));
 
922
          fprintf(stderr,
 
923
                  "DEBUG: The printer does not support IPP/%d.%d, trying "
 
924
                  "IPP/1.1.\n", version / 10, version % 10);
 
925
          version = 11;
 
926
        }
 
927
        else
 
928
        {
 
929
          _cupsLangPrintFilter(stderr, "INFO", _("Preparing to print."));
 
930
          fprintf(stderr,
 
931
                  "DEBUG: The printer does not support IPP/%d.%d, trying "
 
932
                  "IPP/1.0.\n", version / 10, version % 10);
 
933
          version = 10;
 
934
        }
 
935
 
 
936
        httpReconnect(http);
 
937
      }
 
938
      else if (ipp_status == IPP_NOT_FOUND)
 
939
      {
 
940
        _cupsLangPrintFilter(stderr, "ERROR",
 
941
                             _("The printer configuration is incorrect or the "
 
942
                               "printer no longer exists."));
 
943
 
 
944
        ippDelete(supported);
 
945
 
 
946
        return (CUPS_BACKEND_STOP);
 
947
      }
 
948
      else if (ipp_status == IPP_FORBIDDEN ||
 
949
               ipp_status == IPP_AUTHENTICATION_CANCELED)
 
950
      {
 
951
        const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
 
952
                                        /* WWW-Authenticate field value */
 
953
 
 
954
        if (!strncmp(www_auth, "Negotiate", 9))
 
955
          auth_info_required = "negotiate";
 
956
        else if (www_auth[0])
 
957
          auth_info_required = "username,password";
 
958
 
 
959
        fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
 
960
        return (CUPS_BACKEND_AUTH_REQUIRED);
 
961
      }
 
962
      else if (ipp_status != IPP_NOT_AUTHORIZED)
 
963
      {
 
964
        _cupsLangPrintFilter(stderr, "ERROR",
 
965
                             _("Unable to get printer status."));
 
966
        sleep(10);
 
967
 
 
968
        httpReconnect(http);
 
969
      }
 
970
 
 
971
      ippDelete(supported);
 
972
      supported = NULL;
 
973
      continue;
 
974
    }
 
975
 
 
976
    if (!getenv("CLASS"))
 
977
    {
 
978
     /*
 
979
      * Check printer-is-accepting-jobs = false and printer-state-reasons for the
 
980
      * "spool-area-full" keyword...
 
981
      */
 
982
 
 
983
      int busy = 0;
 
984
 
 
985
      if ((printer_accepting = ippFindAttribute(supported,
 
986
                                                "printer-is-accepting-jobs",
 
987
                                                IPP_TAG_BOOLEAN)) != NULL &&
 
988
          !printer_accepting->values[0].boolean)
 
989
        busy = 1;
 
990
      else if (!printer_accepting)
 
991
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
992
                             "cups-ipp-missing-printer-is-accepting-jobs");
 
993
 
 
994
      if ((printer_state = ippFindAttribute(supported,
 
995
                                            "printer-state-reasons",
 
996
                                            IPP_TAG_KEYWORD)) == NULL)
 
997
      {
 
998
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
999
                             "cups-ipp-missing-printer-state-reasons");
 
1000
      }
 
1001
      else if (!busy)
 
1002
      {
 
1003
        for (i = 0; i < printer_state->num_values; i ++)
 
1004
        {
 
1005
          if (!strcmp(printer_state->values[0].string.text,
 
1006
                      "spool-area-full") ||
 
1007
              !strncmp(printer_state->values[0].string.text, "spool-area-full-",
 
1008
                       16))
 
1009
          {
 
1010
            busy = 1;
 
1011
            break;
 
1012
          }
 
1013
        }
 
1014
      }
 
1015
 
 
1016
      if (busy)
 
1017
      {
 
1018
        _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
 
1019
 
 
1020
        report_printer_state(supported);
 
1021
 
 
1022
        sleep(delay);
 
1023
 
 
1024
        delay = _cupsNextDelay(delay, &prev_delay);
 
1025
 
 
1026
        ippDelete(supported);
 
1027
        supported = NULL;
 
1028
        continue;
 
1029
      }
 
1030
    }
 
1031
 
 
1032
   /*
 
1033
    * Check for supported attributes...
 
1034
    */
 
1035
 
 
1036
#ifdef HAVE_LIBZ
 
1037
    if ((compression_sup = ippFindAttribute(supported, "compression-supported",
 
1038
                                            IPP_TAG_KEYWORD)) != NULL)
 
1039
    {
 
1040
     /*
 
1041
      * Check whether the requested compression is supported and/or default to
 
1042
      * compression if supported...
 
1043
      */
 
1044
 
 
1045
      if (compression && !ippContainsString(compression_sup, compression))
 
1046
      {
 
1047
        fprintf(stderr, "DEBUG: Printer does not support the requested "
 
1048
                        "compression value \"%s\".\n", compression);
 
1049
        compression = NULL;
 
1050
      }
 
1051
      else if (!compression)
 
1052
      {
 
1053
        if (ippContainsString(compression_sup, "gzip"))
 
1054
          compression = "gzip";
 
1055
        else if (ippContainsString(compression_sup, "deflate"))
 
1056
          compression = "deflate";
 
1057
 
 
1058
        if (compression)
 
1059
          fprintf(stderr, "DEBUG: Automatically using \"%s\" compression.\n",
 
1060
                  compression);
 
1061
      }
 
1062
    }
 
1063
#endif /* HAVE_LIBZ */
 
1064
 
 
1065
    if ((copies_sup = ippFindAttribute(supported, "copies-supported",
 
1066
                                       IPP_TAG_RANGE)) != NULL)
 
1067
    {
 
1068
     /*
 
1069
      * Has the "copies-supported" attribute - does it have an upper
 
1070
      * bound > 1?
 
1071
      */
 
1072
 
 
1073
      fprintf(stderr, "DEBUG: copies-supported=%d-%d\n",
 
1074
              copies_sup->values[0].range.lower,
 
1075
              copies_sup->values[0].range.upper);
 
1076
 
 
1077
      if (copies_sup->values[0].range.upper <= 1)
 
1078
        copies_sup = NULL; /* No */
 
1079
    }
 
1080
 
 
1081
    cups_version = ippFindAttribute(supported, "cups-version", IPP_TAG_TEXT);
 
1082
 
 
1083
    if ((format_sup = ippFindAttribute(supported, "document-format-supported",
 
1084
                                       IPP_TAG_MIMETYPE)) != NULL)
 
1085
    {
 
1086
      fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
 
1087
              format_sup->num_values);
 
1088
      for (i = 0; i < format_sup->num_values; i ++)
 
1089
        fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
 
1090
                format_sup->values[i].string.text);
 
1091
    }
 
1092
 
 
1093
    if ((media_col_sup = ippFindAttribute(supported, "media-col-supported",
 
1094
                                          IPP_TAG_KEYWORD)) != NULL)
 
1095
    {
 
1096
      fprintf(stderr, "DEBUG: media-col-supported (%d values)\n",
 
1097
              media_col_sup->num_values);
 
1098
      for (i = 0; i < media_col_sup->num_values; i ++)
 
1099
        fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
 
1100
                media_col_sup->values[i].string.text);
 
1101
    }
 
1102
 
 
1103
    print_color_mode = ippFindAttribute(supported,
 
1104
                                        "print-color-mode-supported",
 
1105
                                        IPP_TAG_KEYWORD) != NULL;
 
1106
 
 
1107
    if ((operations_sup = ippFindAttribute(supported, "operations-supported",
 
1108
                                           IPP_TAG_ENUM)) != NULL)
 
1109
    {
 
1110
      fprintf(stderr, "DEBUG: operations-supported (%d values)\n",
 
1111
              operations_sup->num_values);
 
1112
      for (i = 0; i < operations_sup->num_values; i ++)
 
1113
        fprintf(stderr, "DEBUG: [%d] = %s\n", i,
 
1114
                ippOpString(operations_sup->values[i].integer));
 
1115
 
 
1116
      for (i = 0; i < operations_sup->num_values; i ++)
 
1117
        if (operations_sup->values[i].integer == IPP_PRINT_JOB)
 
1118
          break;
 
1119
 
 
1120
      if (i >= operations_sup->num_values)
 
1121
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1122
                             "cups-ipp-missing-print-job");
 
1123
 
 
1124
      for (i = 0; i < operations_sup->num_values; i ++)
 
1125
        if (operations_sup->values[i].integer == IPP_CANCEL_JOB)
 
1126
          break;
 
1127
 
 
1128
      if (i >= operations_sup->num_values)
 
1129
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1130
                             "cups-ipp-missing-cancel-job");
 
1131
 
 
1132
      for (i = 0; i < operations_sup->num_values; i ++)
 
1133
        if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
 
1134
          break;
 
1135
 
 
1136
      if (i >= operations_sup->num_values)
 
1137
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1138
                             "cups-ipp-missing-get-job-attributes");
 
1139
 
 
1140
      for (i = 0; i < operations_sup->num_values; i ++)
 
1141
        if (operations_sup->values[i].integer == IPP_GET_PRINTER_ATTRIBUTES)
 
1142
          break;
 
1143
 
 
1144
      if (i >= operations_sup->num_values)
 
1145
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1146
                             "cups-ipp-missing-get-printer-attributes");
 
1147
 
 
1148
      for (i = 0; i < operations_sup->num_values; i ++)
 
1149
      {
 
1150
        if (operations_sup->values[i].integer == IPP_VALIDATE_JOB)
 
1151
          validate_job = 1;
 
1152
        else if (operations_sup->values[i].integer == IPP_CREATE_JOB)
 
1153
          create_job = 1;
 
1154
        else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT)
 
1155
          send_document = 1;
 
1156
        else if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
 
1157
          get_job_attrs = 1;
 
1158
      }
 
1159
 
 
1160
      if (create_job && !send_document)
 
1161
      {
 
1162
        fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
 
1163
              stderr);
 
1164
        create_job = 0;
 
1165
 
 
1166
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1167
                             "cups-ipp-missing-send-document");
 
1168
      }
 
1169
 
 
1170
      if (!validate_job)
 
1171
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1172
                             "cups-ipp-missing-validate-job");
 
1173
    }
 
1174
    else
 
1175
      update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1176
                           "cups-ipp-missing-operations-supported");
 
1177
 
 
1178
    doc_handling_sup = ippFindAttribute(supported,
 
1179
                                        "multiple-document-handling-supported",
 
1180
                                        IPP_TAG_KEYWORD);
 
1181
 
 
1182
    report_printer_state(supported);
 
1183
  }
 
1184
  while (!job_canceled && ipp_status > IPP_OK_CONFLICT);
 
1185
 
 
1186
  if (job_canceled)
 
1187
    return (CUPS_BACKEND_OK);
 
1188
 
 
1189
 /*
 
1190
  * See if the printer is accepting jobs and is not stopped; if either
 
1191
  * condition is true and we are printing to a class, requeue the job...
 
1192
  */
 
1193
 
 
1194
  if (getenv("CLASS") != NULL)
 
1195
  {
 
1196
    printer_state     = ippFindAttribute(supported, "printer-state",
 
1197
                                         IPP_TAG_ENUM);
 
1198
    printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
 
1199
                                         IPP_TAG_BOOLEAN);
 
1200
 
 
1201
    if (printer_state == NULL ||
 
1202
        (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
 
1203
         waitprinter) ||
 
1204
        printer_accepting == NULL ||
 
1205
        !printer_accepting->values[0].boolean)
 
1206
    {
 
1207
     /*
 
1208
      * If the CLASS environment variable is set, the job was submitted
 
1209
      * to a class and not to a specific queue.  In this case, we want
 
1210
      * to abort immediately so that the job can be requeued on the next
 
1211
      * available printer in the class.
 
1212
      */
 
1213
 
 
1214
      _cupsLangPrintFilter(stderr, "INFO",
 
1215
                           _("Unable to contact printer, queuing on next "
 
1216
                             "printer in class."));
 
1217
 
 
1218
      ippDelete(supported);
 
1219
      httpClose(http);
 
1220
 
 
1221
     /*
 
1222
      * Sleep 5 seconds to keep the job from requeuing too rapidly...
 
1223
      */
 
1224
 
 
1225
      sleep(5);
 
1226
 
 
1227
      return (CUPS_BACKEND_FAILED);
 
1228
    }
 
1229
  }
 
1230
 
 
1231
 /*
 
1232
  * See if the printer supports multiple copies...
 
1233
  */
 
1234
 
 
1235
  copies = atoi(argv[4]);
 
1236
 
 
1237
  if (copies_sup || argc < 7)
 
1238
    copies_remaining = 1;
 
1239
  else
 
1240
    copies_remaining = copies;
 
1241
 
 
1242
 /*
 
1243
  * Prepare remaining printing options...
 
1244
  */
 
1245
 
 
1246
  options = NULL;
 
1247
 
 
1248
  if (send_options)
 
1249
  {
 
1250
    num_options = cupsParseOptions(argv[5], 0, &options);
 
1251
 
 
1252
    if (!cups_version && media_col_sup)
 
1253
    {
 
1254
     /*
 
1255
      * Load the PPD file and generate PWG attribute mapping information...
 
1256
      */
 
1257
 
 
1258
      ppd = ppdOpenFile(getenv("PPD"));
 
1259
      pc  = _ppdCacheCreateWithPPD(ppd);
 
1260
 
 
1261
      ppdMarkDefaults(ppd);
 
1262
      cupsMarkOptions(ppd, num_options, options);
 
1263
    }
 
1264
  }
 
1265
  else
 
1266
    num_options = 0;
 
1267
 
 
1268
  document_format = NULL;
 
1269
 
 
1270
  if (format_sup != NULL)
 
1271
  {
 
1272
    for (i = 0; i < format_sup->num_values; i ++)
 
1273
      if (!_cups_strcasecmp(final_content_type,
 
1274
                            format_sup->values[i].string.text))
 
1275
      {
 
1276
        document_format = final_content_type;
 
1277
        break;
 
1278
      }
 
1279
 
 
1280
    if (!document_format)
 
1281
    {
 
1282
      for (i = 0; i < format_sup->num_values; i ++)
 
1283
        if (!_cups_strcasecmp("application/octet-stream",
 
1284
                              format_sup->values[i].string.text))
 
1285
        {
 
1286
          document_format = "application/octet-stream";
 
1287
          break;
 
1288
        }
 
1289
    }
 
1290
  }
 
1291
 
 
1292
  fprintf(stderr, "DEBUG: final_content_type=\"%s\", document_format=\"%s\"\n",
 
1293
          final_content_type, document_format ? document_format : "(null)");
 
1294
 
 
1295
 /*
 
1296
  * If the printer does not support HTTP/1.1 (which IPP requires), copy stdin
 
1297
  * to a temporary file so that we can do a HTTP/1.0 submission...
 
1298
  *
 
1299
  * (I hate compatibility hacks!)
 
1300
  */
 
1301
 
 
1302
  if (http->version < HTTP_1_1 && num_files == 0)
 
1303
  {
 
1304
    if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
 
1305
    {
 
1306
      perror("DEBUG: Unable to create temporary file");
 
1307
      return (CUPS_BACKEND_FAILED);
 
1308
    }
 
1309
 
 
1310
    _cupsLangPrintFilter(stderr, "INFO", _("Copying print data."));
 
1311
 
 
1312
    if ((compatsize = write(fd, buffer, bytes)) < 0)
 
1313
    {
 
1314
      perror("DEBUG: Unable to write temporary file");
 
1315
      return (CUPS_BACKEND_FAILED);
 
1316
    }
 
1317
 
 
1318
    if ((bytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
 
1319
                                backendNetworkSideCB)) < 0)
 
1320
      return (CUPS_BACKEND_FAILED);
 
1321
 
 
1322
    compatsize += bytes;
 
1323
 
 
1324
    close(fd);
 
1325
 
 
1326
    compatfile = tmpfilename;
 
1327
    files      = &compatfile;
 
1328
    num_files  = 1;
 
1329
  }
 
1330
  else if (http->version < HTTP_1_1 && num_files == 1)
 
1331
  {
 
1332
    struct stat fileinfo;               /* File information */
 
1333
 
 
1334
    if (!stat(files[0], &fileinfo))
 
1335
      compatsize = fileinfo.st_size;
 
1336
  }
 
1337
 
 
1338
 /*
 
1339
  * If the printer only claims to support IPP/1.0, or if the user specifically
 
1340
  * included version=1.0 in the URI, then do not try to use Create-Job or
 
1341
  * Send-Document.  This is another dreaded compatibility hack, but
 
1342
  * unfortunately there are enough broken printers out there that we need
 
1343
  * this for now...
 
1344
  */
 
1345
 
 
1346
  if (version == 10)
 
1347
    create_job = send_document = 0;
 
1348
 
 
1349
 /*
 
1350
  * Start monitoring the printer in the background...
 
1351
  */
 
1352
 
 
1353
  monitor.uri           = uri;
 
1354
  monitor.hostname      = hostname;
 
1355
  monitor.user          = argv[2];
 
1356
  monitor.resource      = resource;
 
1357
  monitor.port          = port;
 
1358
  monitor.version       = version;
 
1359
  monitor.job_id        = 0;
 
1360
  monitor.get_job_attrs = get_job_attrs;
 
1361
  monitor.encryption    = cupsEncryption();
 
1362
  monitor.job_state     = IPP_JOB_PENDING;
 
1363
  monitor.printer_state = IPP_PRINTER_IDLE;
 
1364
 
 
1365
  if (create_job)
 
1366
  {
 
1367
    monitor.job_name = argv[3];
 
1368
  }
 
1369
  else
 
1370
  {
 
1371
    snprintf(print_job_name, sizeof(print_job_name), "%s - %s", argv[1],
 
1372
             argv[3]);
 
1373
    monitor.job_name = print_job_name;
 
1374
  }
 
1375
 
 
1376
  _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
 
1377
 
 
1378
 /*
 
1379
  * Validate access to the printer...
 
1380
  */
 
1381
 
 
1382
  while (!job_canceled && validate_job)
 
1383
  {
 
1384
    request = new_request(IPP_VALIDATE_JOB, version, uri, argv[2],
 
1385
                          monitor.job_name, num_options, options, compression,
 
1386
                          copies_sup ? copies : 1, document_format, pc, ppd,
 
1387
                          media_col_sup, doc_handling_sup, print_color_mode);
 
1388
 
 
1389
    response = cupsDoRequest(http, request, resource);
 
1390
 
 
1391
    ipp_status = cupsLastError();
 
1392
 
 
1393
    fprintf(stderr, "DEBUG: Validate-Job: %s (%s)\n",
 
1394
            ippErrorString(ipp_status), cupsLastErrorString());
 
1395
 
 
1396
    if ((job_auth = ippFindAttribute(response, "job-authorization-uri",
 
1397
                                     IPP_TAG_URI)) != NULL)
 
1398
      num_options = cupsAddOption("job-authorization-uri",
 
1399
                                  ippGetString(job_auth, 0, NULL), num_options,
 
1400
                                  &options);
 
1401
 
 
1402
    ippDelete(response);
 
1403
 
 
1404
    if (job_canceled)
 
1405
      break;
 
1406
 
 
1407
    if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
 
1408
        ipp_status == IPP_STATUS_ERROR_BUSY)
 
1409
    {
 
1410
      _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
 
1411
      sleep(10);
 
1412
    }
 
1413
    else if (ipp_status == IPP_STATUS_ERROR_DOCUMENT_FORMAT_NOT_SUPPORTED ||
 
1414
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
 
1415
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
 
1416
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
 
1417
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
 
1418
      goto cleanup;
 
1419
    else if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
 
1420
             ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
 
1421
    {
 
1422
      const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
 
1423
                                        /* WWW-Authenticate field value */
 
1424
 
 
1425
      if (!strncmp(www_auth, "Negotiate", 9))
 
1426
        auth_info_required = "negotiate";
 
1427
      else if (www_auth[0])
 
1428
        auth_info_required = "username,password";
 
1429
 
 
1430
      goto cleanup;
 
1431
    }
 
1432
    else if (ipp_status == IPP_STATUS_ERROR_OPERATION_NOT_SUPPORTED)
 
1433
    {
 
1434
     /*
 
1435
      * This is all too common...
 
1436
      */
 
1437
 
 
1438
      update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1439
                           "cups-ipp-missing-validate-job");
 
1440
      break;
 
1441
    }
 
1442
    else if (ipp_status < IPP_REDIRECTION_OTHER_SITE ||
 
1443
             ipp_status == IPP_BAD_REQUEST)
 
1444
      break;
 
1445
  }
 
1446
 
 
1447
 /*
 
1448
  * Then issue the print-job request...
 
1449
  */
 
1450
 
 
1451
  job_id = 0;
 
1452
 
 
1453
  while (!job_canceled && copies_remaining > 0)
 
1454
  {
 
1455
   /*
 
1456
    * Check for side-channel requests...
 
1457
    */
 
1458
 
 
1459
    backendCheckSideChannel(snmp_fd, http->hostaddr);
 
1460
 
 
1461
   /*
 
1462
    * Build the IPP job creation request...
 
1463
    */
 
1464
 
 
1465
    if (job_canceled)
 
1466
      break;
 
1467
 
 
1468
    request = new_request((num_files > 1 || create_job) ? IPP_CREATE_JOB :
 
1469
                                                          IPP_PRINT_JOB,
 
1470
                          version, uri, argv[2], monitor.job_name, num_options,
 
1471
                          options, compression, copies_sup ? copies : 1,
 
1472
                          document_format, pc, ppd, media_col_sup,
 
1473
                          doc_handling_sup, print_color_mode);
 
1474
 
 
1475
   /*
 
1476
    * Do the request...
 
1477
    */
 
1478
 
 
1479
    if (num_files > 1 || create_job)
 
1480
      response = cupsDoRequest(http, request, resource);
 
1481
    else
 
1482
    {
 
1483
      size_t    length = 0;             /* Length of request */
 
1484
 
 
1485
      if (compatsize > 0)
 
1486
      {
 
1487
        fputs("DEBUG: Sending file using HTTP/1.0 Content-Length...\n", stderr);
 
1488
        length = ippLength(request) + (size_t)compatsize;
 
1489
      }
 
1490
      else
 
1491
        fputs("DEBUG: Sending file using HTTP/1.1 chunking...\n", stderr);
 
1492
 
 
1493
      http_status = cupsSendRequest(http, request, resource, length);
 
1494
      if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
 
1495
      {
 
1496
        if (compression && strcmp(compression, "none"))
 
1497
          httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
 
1498
 
 
1499
        if (num_files == 1)
 
1500
        {
 
1501
          if ((fd = open(files[0], O_RDONLY)) < 0)
 
1502
          {
 
1503
            _cupsLangPrintError("ERROR", _("Unable to open print file"));
 
1504
            return (CUPS_BACKEND_FAILED);
 
1505
          }
 
1506
        }
 
1507
        else
 
1508
        {
 
1509
          fd          = 0;
 
1510
          http_status = cupsWriteRequestData(http, buffer, bytes);
 
1511
        }
 
1512
 
 
1513
        while (http_status == HTTP_CONTINUE &&
 
1514
               (!job_canceled || compatsize > 0))
 
1515
        {
 
1516
         /*
 
1517
          * Check for side-channel requests and more print data...
 
1518
          */
 
1519
 
 
1520
          FD_ZERO(&input);
 
1521
          FD_SET(fd, &input);
 
1522
          FD_SET(snmp_fd, &input);
 
1523
 
 
1524
          while (select(fd > snmp_fd ? fd + 1 : snmp_fd + 1, &input, NULL, NULL,
 
1525
                        NULL) <= 0 && !job_canceled);
 
1526
 
 
1527
          if (FD_ISSET(snmp_fd, &input))
 
1528
            backendCheckSideChannel(snmp_fd, http->hostaddr);
 
1529
 
 
1530
          if (FD_ISSET(fd, &input))
 
1531
          {
 
1532
            if ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
 
1533
            {
 
1534
              fprintf(stderr, "DEBUG: Read %d bytes...\n", (int)bytes);
 
1535
 
 
1536
              if ((http_status = cupsWriteRequestData(http, buffer, bytes))
 
1537
                      != HTTP_CONTINUE)
 
1538
                break;
 
1539
            }
 
1540
            else if (bytes == 0 || (errno != EINTR && errno != EAGAIN))
 
1541
              break;
 
1542
          }
 
1543
        }
 
1544
 
 
1545
        if (http_status == HTTP_ERROR)
 
1546
          fprintf(stderr, "DEBUG: Error writing document data for "
 
1547
                          "Print-Job: %s\n", strerror(httpError(http)));
 
1548
 
 
1549
        if (num_files == 1)
 
1550
          close(fd);
 
1551
      }
 
1552
 
 
1553
      response = cupsGetResponse(http, resource);
 
1554
      ippDelete(request);
 
1555
    }
 
1556
 
 
1557
    ipp_status = cupsLastError();
 
1558
 
 
1559
    fprintf(stderr, "DEBUG: %s: %s (%s)\n",
 
1560
            (num_files > 1 || create_job) ? "Create-Job" : "Print-Job",
 
1561
            ippErrorString(ipp_status), cupsLastErrorString());
 
1562
 
 
1563
    if (ipp_status > IPP_OK_CONFLICT)
 
1564
    {
 
1565
      job_id = 0;
 
1566
 
 
1567
      if (job_canceled)
 
1568
        break;
 
1569
 
 
1570
      if (ipp_status == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE ||
 
1571
          ipp_status == IPP_STATUS_ERROR_NOT_POSSIBLE ||
 
1572
          ipp_status == IPP_STATUS_ERROR_BUSY)
 
1573
      {
 
1574
        _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use."));
 
1575
        sleep(10);
 
1576
 
 
1577
        if (num_files == 0)
 
1578
        {
 
1579
         /*
 
1580
          * We can't re-submit when we have no files to print, so exit
 
1581
          * immediately with the right status code...
 
1582
          */
 
1583
 
 
1584
          goto cleanup;
 
1585
        }
 
1586
      }
 
1587
      else if (ipp_status == IPP_STATUS_ERROR_JOB_CANCELED ||
 
1588
               ipp_status == IPP_STATUS_ERROR_NOT_AUTHORIZED ||
 
1589
               ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
 
1590
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
 
1591
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
 
1592
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
 
1593
               ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
 
1594
        goto cleanup;
 
1595
      else
 
1596
      {
 
1597
       /*
 
1598
        * Update auth-info-required as needed...
 
1599
        */
 
1600
 
 
1601
        _cupsLangPrintFilter(stderr, "ERROR",
 
1602
                             _("Print job was not accepted."));
 
1603
 
 
1604
        if (ipp_status == IPP_STATUS_ERROR_FORBIDDEN ||
 
1605
            ipp_status == IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED)
 
1606
        {
 
1607
          const char *www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
 
1608
                                        /* WWW-Authenticate field value */
 
1609
 
 
1610
          if (!strncmp(www_auth, "Negotiate", 9))
 
1611
            auth_info_required = "negotiate";
 
1612
          else if (www_auth[0])
 
1613
            auth_info_required = "username,password";
 
1614
        }
 
1615
        else if (ipp_status == IPP_REQUEST_VALUE)
 
1616
        {
 
1617
         /*
 
1618
          * Print file is too large, abort this job...
 
1619
          */
 
1620
 
 
1621
          goto cleanup;
 
1622
        }
 
1623
        else
 
1624
          sleep(10);
 
1625
 
 
1626
        if (num_files == 0)
 
1627
        {
 
1628
         /*
 
1629
          * We can't re-submit when we have no files to print, so exit
 
1630
          * immediately with the right status code...
 
1631
          */
 
1632
 
 
1633
          goto cleanup;
 
1634
        }
 
1635
      }
 
1636
    }
 
1637
    else if ((job_id_attr = ippFindAttribute(response, "job-id",
 
1638
                                             IPP_TAG_INTEGER)) == NULL)
 
1639
    {
 
1640
      fputs("DEBUG: Print job accepted - job ID unknown.\n", stderr);
 
1641
      update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1642
                           "cups-ipp-missing-job-id");
 
1643
      job_id = 0;
 
1644
    }
 
1645
    else
 
1646
    {
 
1647
      password_tries = 0;
 
1648
      monitor.job_id = job_id = job_id_attr->values[0].integer;
 
1649
      fprintf(stderr, "DEBUG: Print job accepted - job ID %d.\n", job_id);
 
1650
    }
 
1651
 
 
1652
    ippDelete(response);
 
1653
 
 
1654
    if (job_canceled)
 
1655
      break;
 
1656
 
 
1657
    if (job_id && (num_files > 1 || create_job))
 
1658
    {
 
1659
      for (i = 0; num_files == 0 || i < num_files; i ++)
 
1660
      {
 
1661
       /*
 
1662
        * Check for side-channel requests...
 
1663
        */
 
1664
 
 
1665
        backendCheckSideChannel(snmp_fd, http->hostaddr);
 
1666
 
 
1667
       /*
 
1668
        * Send the next file in the job...
 
1669
        */
 
1670
 
 
1671
        request = ippNewRequest(IPP_SEND_DOCUMENT);
 
1672
        request->request.op.version[0] = version / 10;
 
1673
        request->request.op.version[1] = version % 10;
 
1674
 
 
1675
        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
1676
                     NULL, uri);
 
1677
 
 
1678
        ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
 
1679
                      job_id);
 
1680
 
 
1681
        if (argv[2][0])
 
1682
          ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
1683
                       "requesting-user-name", NULL, argv[2]);
 
1684
 
 
1685
        ippAddBoolean(request, IPP_TAG_OPERATION, "last-document",
 
1686
                      (i + 1) >= num_files);
 
1687
 
 
1688
        if (document_format)
 
1689
          ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
 
1690
                       "document-format", NULL, document_format);
 
1691
 
 
1692
        if (compression)
 
1693
          ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
1694
                       "compression", NULL, compression);
 
1695
 
 
1696
        fprintf(stderr, "DEBUG: Sending file %d using chunking...\n", i + 1);
 
1697
        http_status = cupsSendRequest(http, request, resource, 0);
 
1698
        if (http_status == HTTP_CONTINUE && request->state == IPP_DATA)
 
1699
        {
 
1700
          if (compression && strcmp(compression, "none"))
 
1701
            httpSetField(http, HTTP_FIELD_CONTENT_ENCODING, compression);
 
1702
 
 
1703
          if (num_files == 0)
 
1704
          {
 
1705
            fd          = 0;
 
1706
            http_status = cupsWriteRequestData(http, buffer, bytes);
 
1707
          }
 
1708
          else
 
1709
          {
 
1710
            if ((fd = open(files[i], O_RDONLY)) < 0)
 
1711
            {
 
1712
              _cupsLangPrintError("ERROR", _("Unable to open print file"));
 
1713
              return (CUPS_BACKEND_FAILED);
 
1714
            }
 
1715
          }
 
1716
        }
 
1717
        else
 
1718
          fd = -1;
 
1719
 
 
1720
        if (fd >= 0)
 
1721
        {
 
1722
          while (!job_canceled && http_status == HTTP_CONTINUE &&
 
1723
                 (bytes = read(fd, buffer, sizeof(buffer))) > 0)
 
1724
          {
 
1725
            if ((http_status = cupsWriteRequestData(http, buffer, bytes))
 
1726
                    != HTTP_CONTINUE)
 
1727
              break;
 
1728
            else
 
1729
            {
 
1730
             /*
 
1731
              * Check for side-channel requests...
 
1732
              */
 
1733
 
 
1734
              backendCheckSideChannel(snmp_fd, http->hostaddr);
 
1735
            }
 
1736
          }
 
1737
 
 
1738
          if (fd > 0)
 
1739
            close(fd);
 
1740
        }
 
1741
 
 
1742
        if (http_status == HTTP_ERROR)
 
1743
          fprintf(stderr, "DEBUG: Error writing document data for "
 
1744
                          "Send-Document: %s\n", strerror(httpError(http)));
 
1745
 
 
1746
        ippDelete(cupsGetResponse(http, resource));
 
1747
        ippDelete(request);
 
1748
 
 
1749
        fprintf(stderr, "DEBUG: Send-Document: %s (%s)\n",
 
1750
                ippErrorString(cupsLastError()), cupsLastErrorString());
 
1751
 
 
1752
        if (cupsLastError() > IPP_OK_CONFLICT)
 
1753
        {
 
1754
          ipp_status = cupsLastError();
 
1755
 
 
1756
          _cupsLangPrintFilter(stderr, "ERROR",
 
1757
                               _("Unable to add document to print job."));
 
1758
          break;
 
1759
        }
 
1760
        else
 
1761
        {
 
1762
          password_tries = 0;
 
1763
 
 
1764
          if (num_files == 0 || fd < 0)
 
1765
            break;
 
1766
        }
 
1767
      }
 
1768
    }
 
1769
 
 
1770
    if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
 
1771
    {
 
1772
      fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
 
1773
      copies_remaining --;
 
1774
    }
 
1775
    else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
 
1776
             ipp_status == IPP_NOT_POSSIBLE ||
 
1777
             ipp_status == IPP_PRINTER_BUSY)
 
1778
    {
 
1779
      if (argc == 6)
 
1780
      {
 
1781
       /*
 
1782
        * Need to reprocess the entire job; if we have a job ID, cancel the
 
1783
        * job first...
 
1784
        */
 
1785
 
 
1786
        if (job_id > 0)
 
1787
          cancel_job(http, uri, job_id, resource, argv[2], version);
 
1788
 
 
1789
        goto cleanup;
 
1790
      }
 
1791
      continue;
 
1792
    }
 
1793
    else if (ipp_status == IPP_REQUEST_VALUE ||
 
1794
             ipp_status == IPP_ERROR_JOB_CANCELED ||
 
1795
             ipp_status == IPP_NOT_AUTHORIZED ||
 
1796
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
 
1797
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
 
1798
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
 
1799
             ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED ||
 
1800
             ipp_status == IPP_INTERNAL_ERROR)
 
1801
    {
 
1802
     /*
 
1803
      * Print file is too large, job was canceled, we need new
 
1804
      * authentication data, or we had some sort of error...
 
1805
      */
 
1806
 
 
1807
      goto cleanup;
 
1808
    }
 
1809
    else if (ipp_status == IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED)
 
1810
    {
 
1811
     /*
 
1812
      * Server is configured incorrectly; the policy for Create-Job and
 
1813
      * Send-Document has to be the same (auth or no auth, encryption or
 
1814
      * no encryption).  Force the queue to stop since printing will never
 
1815
      * work.
 
1816
      */
 
1817
 
 
1818
      fputs("DEBUG: The server or printer is configured incorrectly.\n",
 
1819
            stderr);
 
1820
      fputs("DEBUG: The policy for Create-Job and Send-Document must have the "
 
1821
            "same authentication and encryption requirements.\n", stderr);
 
1822
 
 
1823
      ipp_status = IPP_STATUS_ERROR_INTERNAL;
 
1824
 
 
1825
      if (job_id > 0)
 
1826
        cancel_job(http, uri, job_id, resource, argv[2], version);
 
1827
 
 
1828
      goto cleanup;
 
1829
    }
 
1830
    else if (ipp_status == IPP_NOT_FOUND)
 
1831
    {
 
1832
     /*
 
1833
      * Printer does not actually implement support for Create-Job/
 
1834
      * Send-Document, so log the conformance issue and stop the printer.
 
1835
      */
 
1836
 
 
1837
      fputs("DEBUG: This printer claims to support Create-Job and "
 
1838
            "Send-Document, but those operations failed.\n", stderr);
 
1839
      fputs("DEBUG: Add '?version=1.0' to the device URI to use legacy "
 
1840
            "compatibility mode.\n", stderr);
 
1841
      update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1842
                           "cups-ipp-missing-send-document");
 
1843
 
 
1844
      ipp_status = IPP_INTERNAL_ERROR;  /* Force queue to stop */
 
1845
 
 
1846
      goto cleanup;
 
1847
    }
 
1848
    else
 
1849
      copies_remaining --;
 
1850
 
 
1851
   /*
 
1852
    * Wait for the job to complete...
 
1853
    */
 
1854
 
 
1855
    if (!job_id || !waitjob || !get_job_attrs)
 
1856
      continue;
 
1857
 
 
1858
    _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
 
1859
 
 
1860
    for (delay = _cupsNextDelay(0, &prev_delay); !job_canceled;)
 
1861
    {
 
1862
     /*
 
1863
      * Check for side-channel requests...
 
1864
      */
 
1865
 
 
1866
      backendCheckSideChannel(snmp_fd, http->hostaddr);
 
1867
 
 
1868
     /*
 
1869
      * Check printer state...
 
1870
      */
 
1871
 
 
1872
      check_printer_state(http, uri, resource, argv[2], version);
 
1873
 
 
1874
      if (cupsLastError() <= IPP_OK_CONFLICT)
 
1875
        password_tries = 0;
 
1876
 
 
1877
     /*
 
1878
      * Build an IPP_GET_JOB_ATTRIBUTES request...
 
1879
      */
 
1880
 
 
1881
      request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
 
1882
      request->request.op.version[0] = version / 10;
 
1883
      request->request.op.version[1] = version % 10;
 
1884
 
 
1885
      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
1886
                   NULL, uri);
 
1887
 
 
1888
      ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
 
1889
                    job_id);
 
1890
 
 
1891
      if (argv[2][0])
 
1892
        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
1893
                     "requesting-user-name", NULL, argv[2]);
 
1894
 
 
1895
      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
1896
                    "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
 
1897
                    NULL, jattrs);
 
1898
 
 
1899
     /*
 
1900
      * Do the request...
 
1901
      */
 
1902
 
 
1903
      httpReconnect(http);
 
1904
      response   = cupsDoRequest(http, request, resource);
 
1905
      ipp_status = cupsLastError();
 
1906
 
 
1907
      if (ipp_status == IPP_NOT_FOUND || ipp_status == IPP_NOT_POSSIBLE)
 
1908
      {
 
1909
       /*
 
1910
        * Job has gone away and/or the server has no job history...
 
1911
        */
 
1912
 
 
1913
        update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1914
                             "cups-ipp-missing-job-history");
 
1915
        ippDelete(response);
 
1916
 
 
1917
        ipp_status = IPP_OK;
 
1918
        break;
 
1919
      }
 
1920
 
 
1921
      fprintf(stderr, "DEBUG: Get-Job-Attributes: %s (%s)\n",
 
1922
              ippErrorString(ipp_status), cupsLastErrorString());
 
1923
 
 
1924
      if (ipp_status <= IPP_OK_CONFLICT)
 
1925
        password_tries = 0;
 
1926
      else
 
1927
      {
 
1928
        if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
 
1929
            ipp_status != IPP_PRINTER_BUSY)
 
1930
        {
 
1931
          ippDelete(response);
 
1932
          ipp_status = IPP_OK;
 
1933
          break;
 
1934
        }
 
1935
        else if (ipp_status == IPP_INTERNAL_ERROR)
 
1936
        {
 
1937
          waitjob_tries ++;
 
1938
 
 
1939
          if (waitjob_tries > 4)
 
1940
          {
 
1941
            ippDelete(response);
 
1942
            ipp_status = IPP_OK;
 
1943
            break;
 
1944
          }
 
1945
        }
 
1946
      }
 
1947
 
 
1948
      if (response)
 
1949
      {
 
1950
        if ((job_state = ippFindAttribute(response, "job-state",
 
1951
                                          IPP_TAG_ENUM)) != NULL)
 
1952
        {
 
1953
         /*
 
1954
          * Reflect the remote job state in the local queue...
 
1955
          */
 
1956
 
 
1957
          if (cups_version &&
 
1958
              job_state->values[0].integer >= IPP_JOB_PENDING &&
 
1959
              job_state->values[0].integer <= IPP_JOB_COMPLETED)
 
1960
            update_reasons(NULL,
 
1961
                           remote_job_states[job_state->values[0].integer -
 
1962
                                             IPP_JOB_PENDING]);
 
1963
 
 
1964
          if ((job_sheets = ippFindAttribute(response,
 
1965
                                             "job-media-sheets-completed",
 
1966
                                             IPP_TAG_INTEGER)) == NULL)
 
1967
            job_sheets = ippFindAttribute(response,
 
1968
                                          "job-impressions-completed",
 
1969
                                          IPP_TAG_INTEGER);
 
1970
 
 
1971
          if (job_sheets)
 
1972
            fprintf(stderr, "PAGE: total %d\n",
 
1973
                    job_sheets->values[0].integer);
 
1974
 
 
1975
         /*
 
1976
          * Stop polling if the job is finished or pending-held...
 
1977
          */
 
1978
 
 
1979
          if (job_state->values[0].integer > IPP_JOB_STOPPED)
 
1980
          {
 
1981
            ippDelete(response);
 
1982
            break;
 
1983
          }
 
1984
        }
 
1985
        else if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
 
1986
                 ipp_status != IPP_NOT_POSSIBLE &&
 
1987
                 ipp_status != IPP_PRINTER_BUSY)
 
1988
        {
 
1989
         /*
 
1990
          * If the printer does not return a job-state attribute, it does not
 
1991
          * conform to the IPP specification - break out immediately and fail
 
1992
          * the job...
 
1993
          */
 
1994
 
 
1995
          update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
 
1996
                               "cups-ipp-missing-job-state");
 
1997
          ipp_status = IPP_INTERNAL_ERROR;
 
1998
          break;
 
1999
        }
 
2000
      }
 
2001
 
 
2002
      ippDelete(response);
 
2003
 
 
2004
     /*
 
2005
      * Wait before polling again...
 
2006
      */
 
2007
 
 
2008
      sleep(delay);
 
2009
 
 
2010
      delay = _cupsNextDelay(delay, &prev_delay);
 
2011
    }
 
2012
  }
 
2013
 
 
2014
 /*
 
2015
  * Cancel the job as needed...
 
2016
  */
 
2017
 
 
2018
  if (job_canceled > 0 && job_id > 0)
 
2019
    cancel_job(http, uri, job_id, resource, argv[2], version);
 
2020
 
 
2021
 /*
 
2022
  * Check the printer state and report it if necessary...
 
2023
  */
 
2024
 
 
2025
  check_printer_state(http, uri, resource, argv[2], version);
 
2026
 
 
2027
  if (cupsLastError() <= IPP_OK_CONFLICT)
 
2028
    password_tries = 0;
 
2029
 
 
2030
 /*
 
2031
  * Collect the final page count as needed...
 
2032
  */
 
2033
 
 
2034
  if (have_supplies &&
 
2035
      !backendSNMPSupplies(snmp_fd, &(http->addrlist->addr), &page_count,
 
2036
                           NULL) &&
 
2037
      page_count > start_count)
 
2038
    fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
 
2039
 
 
2040
#ifdef HAVE_GSSAPI
 
2041
 /*
 
2042
  * See if we used Kerberos at all...
 
2043
  */
 
2044
 
 
2045
  if (http->gssctx)
 
2046
    auth_info_required = "negotiate";
 
2047
#endif /* HAVE_GSSAPI */
 
2048
 
 
2049
 /*
 
2050
  * Free memory...
 
2051
  */
 
2052
 
 
2053
  cleanup:
 
2054
 
 
2055
  cupsFreeOptions(num_options, options);
 
2056
  _ppdCacheDestroy(pc);
 
2057
  ppdClose(ppd);
 
2058
 
 
2059
  httpClose(http);
 
2060
 
 
2061
  ippDelete(supported);
 
2062
 
 
2063
 /*
 
2064
  * Remove the temporary file(s) if necessary...
 
2065
  */
 
2066
 
 
2067
  if (tmpfilename[0])
 
2068
    unlink(tmpfilename);
 
2069
 
 
2070
 /*
 
2071
  * Return the queue status...
 
2072
  */
 
2073
 
 
2074
  if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
 
2075
      ipp_status == IPP_AUTHENTICATION_CANCELED ||
 
2076
      ipp_status <= IPP_OK_CONFLICT)
 
2077
    fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
 
2078
 
 
2079
  if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED)
 
2080
    fputs("JOBSTATE: account-info-needed\n", stderr);
 
2081
  else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED)
 
2082
    fputs("JOBSTATE: account-closed\n", stderr);
 
2083
  else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED)
 
2084
    fputs("JOBSTATE: account-limit-reached\n", stderr);
 
2085
  else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
 
2086
    fputs("JOBSTATE: account-authorization-failed\n", stderr);
 
2087
 
 
2088
  if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN ||
 
2089
      ipp_status == IPP_AUTHENTICATION_CANCELED)
 
2090
    return (CUPS_BACKEND_AUTH_REQUIRED);
 
2091
  else if (ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_LIMIT_REACHED ||
 
2092
           ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_INFO_NEEDED ||
 
2093
           ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_CLOSED ||
 
2094
           ipp_status == IPP_STATUS_ERROR_CUPS_ACCOUNT_AUTHORIZATION_FAILED)
 
2095
    return (CUPS_BACKEND_HOLD);
 
2096
  else if (ipp_status == IPP_INTERNAL_ERROR)
 
2097
    return (CUPS_BACKEND_STOP);
 
2098
  else if (ipp_status == IPP_CONFLICT)
 
2099
    return (CUPS_BACKEND_FAILED);
 
2100
  else if (ipp_status == IPP_REQUEST_VALUE ||
 
2101
           ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES ||
 
2102
           ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
 
2103
  {
 
2104
    if (ipp_status == IPP_REQUEST_VALUE)
 
2105
      _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
 
2106
    else if (ipp_status == IPP_DOCUMENT_FORMAT)
 
2107
      _cupsLangPrintFilter(stderr, "ERROR",
 
2108
                           _("Printer cannot print supplied content."));
 
2109
    else if (ipp_status == IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES)
 
2110
      _cupsLangPrintFilter(stderr, "ERROR",
 
2111
                           _("Printer cannot print with supplied options."));
 
2112
    else
 
2113
      _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
 
2114
 
 
2115
    return (CUPS_BACKEND_CANCEL);
 
2116
  }
 
2117
  else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
 
2118
    return (CUPS_BACKEND_RETRY_CURRENT);
 
2119
  else
 
2120
    return (CUPS_BACKEND_OK);
 
2121
}
 
2122
 
 
2123
 
 
2124
/*
 
2125
 * 'cancel_job()' - Cancel a print job.
 
2126
 */
 
2127
 
 
2128
static void
 
2129
cancel_job(http_t     *http,            /* I - HTTP connection */
 
2130
           const char *uri,             /* I - printer-uri */
 
2131
           int        id,               /* I - job-id */
 
2132
           const char *resource,        /* I - Resource path */
 
2133
           const char *user,            /* I - requesting-user-name */
 
2134
           int        version)          /* I - IPP version */
 
2135
{
 
2136
  ipp_t *request;                       /* Cancel-Job request */
 
2137
 
 
2138
 
 
2139
  _cupsLangPrintFilter(stderr, "INFO", _("Canceling print job."));
 
2140
 
 
2141
  request = ippNewRequest(IPP_CANCEL_JOB);
 
2142
  request->request.op.version[0] = version / 10;
 
2143
  request->request.op.version[1] = version % 10;
 
2144
 
 
2145
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
2146
               NULL, uri);
 
2147
  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
 
2148
 
 
2149
  if (user && user[0])
 
2150
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
2151
                 "requesting-user-name", NULL, user);
 
2152
 
 
2153
 /*
 
2154
  * Do the request...
 
2155
  */
 
2156
 
 
2157
  ippDelete(cupsDoRequest(http, request, resource));
 
2158
 
 
2159
  if (cupsLastError() > IPP_OK_CONFLICT)
 
2160
    _cupsLangPrintFilter(stderr, "ERROR", _("Unable to cancel print job."));
 
2161
}
 
2162
 
 
2163
 
 
2164
/*
 
2165
 * 'check_printer_state()' - Check the printer state.
 
2166
 */
 
2167
 
 
2168
static ipp_pstate_t                     /* O - Current printer-state */
 
2169
check_printer_state(
 
2170
    http_t      *http,                  /* I - HTTP connection */
 
2171
    const char  *uri,                   /* I - Printer URI */
 
2172
    const char  *resource,              /* I - Resource path */
 
2173
    const char  *user,                  /* I - Username, if any */
 
2174
    int         version)                /* I - IPP version */
 
2175
 {
 
2176
  ipp_t         *request,               /* IPP request */
 
2177
                *response;              /* IPP response */
 
2178
  ipp_attribute_t *attr;                /* Attribute in response */
 
2179
  ipp_pstate_t  printer_state = IPP_PRINTER_STOPPED;
 
2180
                                        /* Current printer-state */
 
2181
 
 
2182
 
 
2183
 /*
 
2184
  * Send a Get-Printer-Attributes request and log the results...
 
2185
  */
 
2186
 
 
2187
  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
 
2188
  request->request.op.version[0] = version / 10;
 
2189
  request->request.op.version[1] = version % 10;
 
2190
 
 
2191
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
2192
               NULL, uri);
 
2193
 
 
2194
  if (user && user[0])
 
2195
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
2196
                 "requesting-user-name", NULL, user);
 
2197
 
 
2198
  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
2199
                "requested-attributes",
 
2200
                (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
 
2201
 
 
2202
  if ((response = cupsDoRequest(http, request, resource)) != NULL)
 
2203
  {
 
2204
    report_printer_state(response);
 
2205
 
 
2206
    if ((attr = ippFindAttribute(response, "printer-state",
 
2207
                                 IPP_TAG_ENUM)) != NULL)
 
2208
      printer_state = (ipp_pstate_t)attr->values[0].integer;
 
2209
 
 
2210
    ippDelete(response);
 
2211
  }
 
2212
 
 
2213
  fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
 
2214
          ippErrorString(cupsLastError()), cupsLastErrorString());
 
2215
 
 
2216
 /*
 
2217
  * Return the printer-state value...
 
2218
  */
 
2219
 
 
2220
  return (printer_state);
 
2221
}
 
2222
 
 
2223
 
 
2224
/*
 
2225
 * 'monitor_printer()' - Monitor the printer state.
 
2226
 */
 
2227
 
 
2228
static void *                           /* O - Thread exit code */
 
2229
monitor_printer(
 
2230
    _cups_monitor_t *monitor)           /* I - Monitoring data */
 
2231
{
 
2232
  http_t        *http;                  /* Connection to printer */
 
2233
  ipp_t         *request,               /* IPP request */
 
2234
                *response;              /* IPP response */
 
2235
  ipp_attribute_t *attr;                /* Attribute in response */
 
2236
  int           delay,                  /* Current delay */
 
2237
                prev_delay;             /* Previous delay */
 
2238
  ipp_op_t      job_op;                 /* Operation to use */
 
2239
  int           job_id;                 /* Job ID */
 
2240
  const char    *job_name;              /* Job name */
 
2241
  ipp_jstate_t  job_state;              /* Job state */
 
2242
  const char    *job_user;              /* Job originating user name */
 
2243
  int           password_tries = 0;     /* Password tries */
 
2244
 
 
2245
 
 
2246
 /*
 
2247
  * Make a copy of the printer connection...
 
2248
  */
 
2249
 
 
2250
  http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC,
 
2251
                      monitor->encryption, 1, 0, NULL);
 
2252
  httpSetTimeout(http, 30.0, timeout_cb, NULL);
 
2253
  if (username[0])
 
2254
    cupsSetUser(username);
 
2255
 
 
2256
  cupsSetPasswordCB2((cups_password_cb2_t)password_cb, &password_tries);
 
2257
 
 
2258
 /*
 
2259
  * Loop until the job is canceled, aborted, or completed.
 
2260
  */
 
2261
 
 
2262
  delay = _cupsNextDelay(0, &prev_delay);
 
2263
 
 
2264
  monitor->job_reasons = 0;
 
2265
 
 
2266
  while (monitor->job_state < IPP_JOB_CANCELED && !job_canceled)
 
2267
  {
 
2268
   /*
 
2269
    * Reconnect to the printer...
 
2270
    */
 
2271
 
 
2272
    if (!httpReconnect(http))
 
2273
    {
 
2274
     /*
 
2275
      * Connected, so check on the printer state...
 
2276
      */
 
2277
 
 
2278
      monitor->printer_state = check_printer_state(http, monitor->uri,
 
2279
                                                   monitor->resource,
 
2280
                                                   monitor->user,
 
2281
                                                   monitor->version);
 
2282
      if (cupsLastError() <= IPP_OK_CONFLICT)
 
2283
        password_tries = 0;
 
2284
 
 
2285
     /*
 
2286
      * Check the status of the job itself...
 
2287
      */
 
2288
 
 
2289
      job_op  = (monitor->job_id > 0 && monitor->get_job_attrs) ?
 
2290
                    IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
 
2291
      request = ippNewRequest(job_op);
 
2292
      request->request.op.version[0] = monitor->version / 10;
 
2293
      request->request.op.version[1] = monitor->version % 10;
 
2294
 
 
2295
      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
2296
                   NULL, monitor->uri);
 
2297
      if (job_op == IPP_GET_JOB_ATTRIBUTES)
 
2298
        ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
 
2299
                      monitor->job_id);
 
2300
 
 
2301
      if (monitor->user && monitor->user[0])
 
2302
        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
2303
                     "requesting-user-name", NULL, monitor->user);
 
2304
 
 
2305
      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
2306
                    "requested-attributes",
 
2307
                    (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
 
2308
 
 
2309
     /*
 
2310
      * Do the request...
 
2311
      */
 
2312
 
 
2313
      response = cupsDoRequest(http, request, monitor->resource);
 
2314
 
 
2315
      fprintf(stderr, "DEBUG: (monitor) %s: %s (%s)\n", ippOpString(job_op),
 
2316
              ippErrorString(cupsLastError()), cupsLastErrorString());
 
2317
 
 
2318
      if (cupsLastError() <= IPP_OK_CONFLICT)
 
2319
        password_tries = 0;
 
2320
 
 
2321
      if (job_op == IPP_GET_JOB_ATTRIBUTES)
 
2322
      {
 
2323
        if ((attr = ippFindAttribute(response, "job-state",
 
2324
                                     IPP_TAG_ENUM)) != NULL)
 
2325
          monitor->job_state = (ipp_jstate_t)attr->values[0].integer;
 
2326
        else
 
2327
          monitor->job_state = IPP_JOB_COMPLETED;
 
2328
      }
 
2329
      else if (response)
 
2330
      {
 
2331
        for (attr = response->attrs; attr; attr = attr->next)
 
2332
        {
 
2333
          job_id    = 0;
 
2334
          job_name  = NULL;
 
2335
          job_state = IPP_JOB_PENDING;
 
2336
          job_user  = NULL;
 
2337
 
 
2338
          while (attr && attr->group_tag != IPP_TAG_JOB)
 
2339
            attr = attr->next;
 
2340
 
 
2341
          if (!attr)
 
2342
            break;
 
2343
 
 
2344
          while (attr && attr->group_tag == IPP_TAG_JOB)
 
2345
          {
 
2346
            if (!strcmp(attr->name, "job-id") &&
 
2347
                attr->value_tag == IPP_TAG_INTEGER)
 
2348
              job_id = attr->values[0].integer;
 
2349
            else if (!strcmp(attr->name, "job-name") &&
 
2350
                     (attr->value_tag == IPP_TAG_NAME ||
 
2351
                      attr->value_tag == IPP_TAG_NAMELANG))
 
2352
              job_name = attr->values[0].string.text;
 
2353
            else if (!strcmp(attr->name, "job-state") &&
 
2354
                     attr->value_tag == IPP_TAG_ENUM)
 
2355
              job_state = attr->values[0].integer;
 
2356
            else if (!strcmp(attr->name, "job-originating-user-name") &&
 
2357
                     (attr->value_tag == IPP_TAG_NAME ||
 
2358
                      attr->value_tag == IPP_TAG_NAMELANG))
 
2359
              job_user = attr->values[0].string.text;
 
2360
 
 
2361
            attr = attr->next;
 
2362
          }
 
2363
 
 
2364
          if (job_id > 0 && job_name && !strcmp(job_name, monitor->job_name) &&
 
2365
              job_user && monitor->user && !strcmp(job_user, monitor->user))
 
2366
          {
 
2367
            monitor->job_id    = job_id;
 
2368
            monitor->job_state = job_state;
 
2369
            break;
 
2370
          }
 
2371
 
 
2372
          if (!attr)
 
2373
            break;
 
2374
        }
 
2375
      }
 
2376
 
 
2377
      if ((attr = ippFindAttribute(response, "job-state-reasons",
 
2378
                                   IPP_TAG_KEYWORD)) != NULL)
 
2379
      {
 
2380
        int     i, new_reasons = 0;     /* Looping var, new reasons */
 
2381
 
 
2382
        for (i = 0; i < attr->num_values; i ++)
 
2383
        {
 
2384
          if (!strcmp(attr->values[i].string.text,
 
2385
                      "account-authorization-failed"))
 
2386
            new_reasons |= _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED;
 
2387
          else if (!strcmp(attr->values[i].string.text, "account-closed"))
 
2388
            new_reasons |= _CUPS_JSR_ACCOUNT_CLOSED;
 
2389
          else if (!strcmp(attr->values[i].string.text, "account-info-needed"))
 
2390
            new_reasons |= _CUPS_JSR_ACCOUNT_INFO_NEEDED;
 
2391
          else if (!strcmp(attr->values[i].string.text,
 
2392
                           "account-limit-reached"))
 
2393
            new_reasons |= _CUPS_JSR_ACCOUNT_LIMIT_REACHED;
 
2394
          else if (!strcmp(attr->values[i].string.text, "job-password-wait"))
 
2395
            new_reasons |= _CUPS_JSR_JOB_PASSWORD_WAIT;
 
2396
          else if (!strcmp(attr->values[i].string.text, "job-release-wait"))
 
2397
            new_reasons |= _CUPS_JSR_JOB_RELEASE_WAIT;
 
2398
        }
 
2399
 
 
2400
        if (new_reasons != monitor->job_reasons)
 
2401
        {
 
2402
          if (new_reasons & _CUPS_JSR_ACCOUNT_AUTHORIZATION_FAILED)
 
2403
            fputs("JOBSTATE: account-authorization-failed\n", stderr);
 
2404
          else if (new_reasons & _CUPS_JSR_ACCOUNT_CLOSED)
 
2405
            fputs("JOBSTATE: account-closed\n", stderr);
 
2406
          else if (new_reasons & _CUPS_JSR_ACCOUNT_INFO_NEEDED)
 
2407
            fputs("JOBSTATE: account-info-needed\n", stderr);
 
2408
          else if (new_reasons & _CUPS_JSR_ACCOUNT_LIMIT_REACHED)
 
2409
            fputs("JOBSTATE: account-limit-reached\n", stderr);
 
2410
          else if (new_reasons & _CUPS_JSR_JOB_PASSWORD_WAIT)
 
2411
            fputs("JOBSTATE: job-password-wait\n", stderr);
 
2412
          else if (new_reasons & _CUPS_JSR_JOB_RELEASE_WAIT)
 
2413
            fputs("JOBSTATE: job-release-wait\n", stderr);
 
2414
          else
 
2415
            fputs("JOBSTATE: job-printing\n", stderr);
 
2416
 
 
2417
          monitor->job_reasons = new_reasons;
 
2418
        }
 
2419
      }
 
2420
 
 
2421
      ippDelete(response);
 
2422
 
 
2423
      fprintf(stderr, "DEBUG: (monitor) job-state=%s\n",
 
2424
              ippEnumString("job-state", monitor->job_state));
 
2425
 
 
2426
      if (!job_canceled &&
 
2427
          (monitor->job_state == IPP_JOB_CANCELED ||
 
2428
           monitor->job_state == IPP_JOB_ABORTED))
 
2429
        job_canceled = -1;
 
2430
 
 
2431
     /*
 
2432
      * Disconnect from the printer - we'll reconnect on the next poll...
 
2433
      */
 
2434
 
 
2435
      _httpDisconnect(http);
 
2436
    }
 
2437
 
 
2438
   /*
 
2439
    * Sleep for N seconds...
 
2440
    */
 
2441
 
 
2442
    sleep(delay);
 
2443
 
 
2444
    delay = _cupsNextDelay(delay, &prev_delay);
 
2445
  }
 
2446
 
 
2447
 /*
 
2448
  * Cancel the job if necessary...
 
2449
  */
 
2450
 
 
2451
  if (job_canceled > 0 && monitor->job_id > 0)
 
2452
    if (!httpReconnect(http))
 
2453
      cancel_job(http, monitor->uri, monitor->job_id, monitor->resource,
 
2454
                 monitor->user, monitor->version);
 
2455
 
 
2456
 /*
 
2457
  * Cleanup and return...
 
2458
  */
 
2459
 
 
2460
  httpClose(http);
 
2461
 
 
2462
  return (NULL);
 
2463
}
 
2464
 
 
2465
 
 
2466
/*
 
2467
 * 'new_request()' - Create a new print creation or validation request.
 
2468
 */
 
2469
 
 
2470
static ipp_t *                          /* O - Request data */
 
2471
new_request(
 
2472
    ipp_op_t        op,                 /* I - IPP operation code */
 
2473
    int             version,            /* I - IPP version number */
 
2474
    const char      *uri,               /* I - printer-uri value */
 
2475
    const char      *user,              /* I - requesting-user-name value */
 
2476
    const char      *title,             /* I - job-name value */
 
2477
    int             num_options,        /* I - Number of options to send */
 
2478
    cups_option_t   *options,           /* I - Options to send */
 
2479
    const char      *compression,       /* I - compression value or NULL */
 
2480
    int             copies,             /* I - copies value or 0 */
 
2481
    const char      *format,            /* I - document-format value or NULL */
 
2482
    _ppd_cache_t    *pc,                /* I - PPD cache and mapping data */
 
2483
    ppd_file_t      *ppd,               /* I - PPD file data */
 
2484
    ipp_attribute_t *media_col_sup,     /* I - media-col-supported values */
 
2485
    ipp_attribute_t *doc_handling_sup,  /* I - multiple-document-handling-supported values */
 
2486
    int             print_color_mode)   /* I - Printer supports print-color-mode */
 
2487
{
 
2488
  int           i;                      /* Looping var */
 
2489
  ipp_t         *request;               /* Request data */
 
2490
  const char    *keyword;               /* PWG keyword */
 
2491
  _pwg_size_t   *size;                  /* PWG media size */
 
2492
  ipp_t         *media_col,             /* media-col value */
 
2493
                *media_size;            /* media-size value */
 
2494
  const char    *media_source,          /* media-source value */
 
2495
                *media_type,            /* media-type value */
 
2496
                *collate_str,           /* multiple-document-handling value */
 
2497
                *mandatory;             /* Mandatory attributes */
 
2498
  ipp_tag_t     group;                  /* Current group */
 
2499
  ipp_attribute_t *attr;                /* Current attribute */
 
2500
  const char    *color_attr_name;       /* Supported color attribute */
 
2501
  char          buffer[1024];           /* Value buffer */
 
2502
 
 
2503
 
 
2504
 /*
 
2505
  * Create the IPP request...
 
2506
  */
 
2507
 
 
2508
  request                        = ippNewRequest(op);
 
2509
  request->request.op.version[0] = version / 10;
 
2510
  request->request.op.version[1] = version % 10;
 
2511
 
 
2512
  fprintf(stderr, "DEBUG: %s IPP/%d.%d\n",
 
2513
          ippOpString(request->request.op.operation_id),
 
2514
          request->request.op.version[0],
 
2515
          request->request.op.version[1]);
 
2516
 
 
2517
 /*
 
2518
  * Add standard attributes...
 
2519
  */
 
2520
 
 
2521
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
 
2522
               NULL, uri);
 
2523
  fprintf(stderr, "DEBUG: printer-uri=\"%s\"\n", uri);
 
2524
 
 
2525
  if (user && *user)
 
2526
  {
 
2527
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
 
2528
                 "requesting-user-name", NULL, user);
 
2529
    fprintf(stderr, "DEBUG: requesting-user-name=\"%s\"\n", user);
 
2530
  }
 
2531
 
 
2532
  if (title && *title)
 
2533
  {
 
2534
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
 
2535
                 title);
 
2536
    fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
 
2537
  }
 
2538
 
 
2539
  if (format && op != IPP_CREATE_JOB)
 
2540
  {
 
2541
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
 
2542
                 "document-format", NULL, format);
 
2543
    fprintf(stderr, "DEBUG: document-format=\"%s\"\n", format);
 
2544
  }
 
2545
 
 
2546
#ifdef HAVE_LIBZ
 
2547
  if (compression && op != IPP_OP_CREATE_JOB && op != IPP_OP_VALIDATE_JOB)
 
2548
  {
 
2549
    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
2550
                 "compression", NULL, compression);
 
2551
    fprintf(stderr, "DEBUG: compression=\"%s\"\n", compression);
 
2552
  }
 
2553
#endif /* HAVE_LIBZ */
 
2554
 
 
2555
 /*
 
2556
  * Handle options on the command-line...
 
2557
  */
 
2558
 
 
2559
  if (num_options > 0)
 
2560
  {
 
2561
    if (pc)
 
2562
    {
 
2563
      int       num_finishings = 0,     /* Number of finishing values */
 
2564
                finishings[10];         /* Finishing enum values */
 
2565
      ppd_choice_t *choice;             /* Marked choice */
 
2566
 
 
2567
     /*
 
2568
      * Send standard IPP attributes...
 
2569
      */
 
2570
 
 
2571
      fputs("DEBUG: Adding standard IPP operation/job attributes.\n", stderr);
 
2572
 
 
2573
      if (pc->password &&
 
2574
          (keyword = cupsGetOption("job-password", num_options,
 
2575
                                   options)) != NULL)
 
2576
      {
 
2577
        ippAddOctetString(request, IPP_TAG_OPERATION, "job-password",
 
2578
                          keyword, strlen(keyword));
 
2579
 
 
2580
        if ((keyword = cupsGetOption("job-password-encryption", num_options,
 
2581
                                     options)) == NULL)
 
2582
          keyword = "none";
 
2583
 
 
2584
        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
 
2585
                     "job-password-encryption", NULL, keyword);
 
2586
      }
 
2587
 
 
2588
      if (pc->account_id)
 
2589
      {
 
2590
        if ((keyword = cupsGetOption("job-account-id", num_options,
 
2591
                                     options)) == NULL)
 
2592
          keyword = cupsGetOption("job-billing", num_options, options);
 
2593
 
 
2594
        if (keyword)
 
2595
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME, "job-account-id",
 
2596
                       NULL, keyword);
 
2597
      }
 
2598
 
 
2599
      if (pc->accounting_user_id)
 
2600
      {
 
2601
        if ((keyword = cupsGetOption("job-accounting-user-id", num_options,
 
2602
                                     options)) == NULL)
 
2603
          keyword = user;
 
2604
 
 
2605
        if (keyword)
 
2606
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_NAME,
 
2607
                       "job-accounting-user-id", NULL, keyword);
 
2608
      }
 
2609
 
 
2610
      for (mandatory = (char *)cupsArrayFirst(pc->mandatory);
 
2611
           mandatory;
 
2612
           mandatory = (char *)cupsArrayNext(pc->mandatory))
 
2613
      {
 
2614
        if (strcmp(mandatory, "copies") &&
 
2615
            strcmp(mandatory, "destination-uris") &&
 
2616
            strcmp(mandatory, "finishings") &&
 
2617
            strcmp(mandatory, "job-account-id") &&
 
2618
            strcmp(mandatory, "job-accounting-user-id") &&
 
2619
            strcmp(mandatory, "job-password") &&
 
2620
            strcmp(mandatory, "job-password-encryption") &&
 
2621
            strcmp(mandatory, "media") &&
 
2622
            strncmp(mandatory, "media-col", 9) &&
 
2623
            strcmp(mandatory, "multiple-document-handling") &&
 
2624
            strcmp(mandatory, "output-bin") &&
 
2625
            strcmp(mandatory, "print-color-mode") &&
 
2626
            strcmp(mandatory, "print-quality") &&
 
2627
            strcmp(mandatory, "sides") &&
 
2628
            (keyword = cupsGetOption(mandatory, num_options, options)) != NULL)
 
2629
        {
 
2630
          _ipp_option_t *opt = _ippFindOption(mandatory);
 
2631
                                        /* Option type */
 
2632
          ipp_tag_t     value_tag = opt ? opt->value_tag : IPP_TAG_NAME;
 
2633
                                        /* Value type */
 
2634
 
 
2635
          switch (value_tag)
 
2636
          {
 
2637
            case IPP_TAG_INTEGER :
 
2638
            case IPP_TAG_ENUM :
 
2639
                ippAddInteger(request, IPP_TAG_JOB, value_tag, mandatory,
 
2640
                              atoi(keyword));
 
2641
                break;
 
2642
            case IPP_TAG_BOOLEAN :
 
2643
                ippAddBoolean(request, IPP_TAG_JOB, mandatory,
 
2644
                              !_cups_strcasecmp(keyword, "true"));
 
2645
                break;
 
2646
            case IPP_TAG_RANGE :
 
2647
                {
 
2648
                  int lower, upper;     /* Range */
 
2649
 
 
2650
                  if (sscanf(keyword, "%d-%d", &lower, &upper) != 2)
 
2651
                    lower = upper = atoi(keyword);
 
2652
 
 
2653
                  ippAddRange(request, IPP_TAG_JOB, mandatory, lower, upper);
 
2654
                }
 
2655
                break;
 
2656
            case IPP_TAG_STRING :
 
2657
                ippAddOctetString(request, IPP_TAG_JOB, mandatory, keyword,
 
2658
                                  strlen(keyword));
 
2659
                break;
 
2660
            default :
 
2661
                ippAddString(request, IPP_TAG_JOB, value_tag, mandatory,
 
2662
                             NULL, keyword);
 
2663
                break;
 
2664
          }
 
2665
        }
 
2666
      }
 
2667
 
 
2668
      if ((keyword = cupsGetOption("PageSize", num_options, options)) == NULL)
 
2669
        keyword = cupsGetOption("media", num_options, options);
 
2670
 
 
2671
      if ((size = _ppdCacheGetSize(pc, keyword)) != NULL)
 
2672
      {
 
2673
       /*
 
2674
        * Add a media-col value...
 
2675
        */
 
2676
 
 
2677
        media_size = ippNew();
 
2678
        ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
 
2679
                      "x-dimension", size->width);
 
2680
        ippAddInteger(media_size, IPP_TAG_ZERO, IPP_TAG_INTEGER,
 
2681
                      "y-dimension", size->length);
 
2682
 
 
2683
        media_col = ippNew();
 
2684
        ippAddCollection(media_col, IPP_TAG_ZERO, "media-size", media_size);
 
2685
 
 
2686
        media_source = _ppdCacheGetSource(pc, cupsGetOption("InputSlot",
 
2687
                                                            num_options,
 
2688
                                                            options));
 
2689
        media_type   = _ppdCacheGetType(pc, cupsGetOption("MediaType",
 
2690
                                                          num_options,
 
2691
                                                          options));
 
2692
 
 
2693
        for (i = 0; i < media_col_sup->num_values; i ++)
 
2694
        {
 
2695
          if (!strcmp(media_col_sup->values[i].string.text,
 
2696
                      "media-left-margin"))
 
2697
            ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
 
2698
                          "media-left-margin", size->left);
 
2699
          else if (!strcmp(media_col_sup->values[i].string.text,
 
2700
                           "media-bottom-margin"))
 
2701
            ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
 
2702
                          "media-bottom-margin", size->bottom);
 
2703
          else if (!strcmp(media_col_sup->values[i].string.text,
 
2704
                           "media-right-margin"))
 
2705
            ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
 
2706
                          "media-right-margin", size->right);
 
2707
          else if (!strcmp(media_col_sup->values[i].string.text,
 
2708
                           "media-top-margin"))
 
2709
            ippAddInteger(media_col, IPP_TAG_ZERO, IPP_TAG_INTEGER,
 
2710
                          "media-top-margin", size->top);
 
2711
          else if (!strcmp(media_col_sup->values[i].string.text,
 
2712
                           "media-source") && media_source)
 
2713
            ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
 
2714
                         "media-source", NULL, media_source);
 
2715
          else if (!strcmp(media_col_sup->values[i].string.text,
 
2716
                           "media-type") && media_type)
 
2717
            ippAddString(media_col, IPP_TAG_ZERO, IPP_TAG_KEYWORD,
 
2718
                         "media-type", NULL, media_type);
 
2719
        }
 
2720
 
 
2721
        ippAddCollection(request, IPP_TAG_JOB, "media-col", media_col);
 
2722
      }
 
2723
 
 
2724
      if ((keyword = cupsGetOption("output-bin", num_options,
 
2725
                                   options)) == NULL)
 
2726
      {
 
2727
        if ((choice = ppdFindMarkedChoice(ppd, "OutputBin")) != NULL)
 
2728
          keyword = _ppdCacheGetBin(pc, choice->choice);
 
2729
      }
 
2730
 
 
2731
      if (keyword)
 
2732
        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "output-bin",
 
2733
                     NULL, keyword);
 
2734
 
 
2735
      color_attr_name = print_color_mode ? "print-color-mode" : "output-mode";
 
2736
 
 
2737
      if ((keyword = cupsGetOption("print-color-mode", num_options,
 
2738
                                   options)) != NULL)
 
2739
        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, color_attr_name,
 
2740
                     NULL, keyword);
 
2741
      else if ((choice = ppdFindMarkedChoice(ppd, "ColorModel")) != NULL)
 
2742
      {
 
2743
        if (!_cups_strcasecmp(choice->choice, "Gray"))
 
2744
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
 
2745
                       color_attr_name, NULL, "monochrome");
 
2746
        else
 
2747
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
 
2748
                       color_attr_name, NULL, "color");
 
2749
      }
 
2750
 
 
2751
      if ((keyword = cupsGetOption("print-quality", num_options,
 
2752
                                   options)) != NULL)
 
2753
        ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
 
2754
                      atoi(keyword));
 
2755
      else if ((choice = ppdFindMarkedChoice(ppd, "cupsPrintQuality")) != NULL)
 
2756
      {
 
2757
        if (!_cups_strcasecmp(choice->choice, "draft"))
 
2758
          ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
 
2759
                        IPP_QUALITY_DRAFT);
 
2760
        else if (!_cups_strcasecmp(choice->choice, "normal"))
 
2761
          ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
 
2762
                        IPP_QUALITY_NORMAL);
 
2763
        else if (!_cups_strcasecmp(choice->choice, "high"))
 
2764
          ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_ENUM, "print-quality",
 
2765
                        IPP_QUALITY_HIGH);
 
2766
      }
 
2767
 
 
2768
      if ((keyword = cupsGetOption("sides", num_options, options)) != NULL)
 
2769
        ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
 
2770
                     NULL, keyword);
 
2771
      else if (pc->sides_option &&
 
2772
               (choice = ppdFindMarkedChoice(ppd, pc->sides_option)) != NULL)
 
2773
      {
 
2774
        if (!_cups_strcasecmp(choice->choice, pc->sides_1sided))
 
2775
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
 
2776
                       NULL, "one-sided");
 
2777
        else if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_long))
 
2778
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
 
2779
                       NULL, "two-sided-long-edge");
 
2780
        if (!_cups_strcasecmp(choice->choice, pc->sides_2sided_short))
 
2781
          ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD, "sides",
 
2782
                       NULL, "two-sided-short-edge");
 
2783
      }
 
2784
 
 
2785
      if ((keyword = cupsGetOption("multiple-document-handling",
 
2786
                                   num_options, options)) != NULL)
 
2787
      {
 
2788
        if (strstr(keyword, "uncollated"))
 
2789
          keyword = "false";
 
2790
        else
 
2791
          keyword = "true";
 
2792
      }
 
2793
      else if ((keyword = cupsGetOption("collate", num_options,
 
2794
                                        options)) == NULL)
 
2795
        keyword = "true";
 
2796
 
 
2797
      if (format)
 
2798
      {
 
2799
        if (!_cups_strcasecmp(format, "image/gif") ||
 
2800
            !_cups_strcasecmp(format, "image/jp2") ||
 
2801
            !_cups_strcasecmp(format, "image/jpeg") ||
 
2802
            !_cups_strcasecmp(format, "image/png") ||
 
2803
            !_cups_strcasecmp(format, "image/tiff") ||
 
2804
            !_cups_strncasecmp(format, "image/x-", 8))
 
2805
        {
 
2806
         /*
 
2807
          * Collation makes no sense for single page image formats...
 
2808
          */
 
2809
 
 
2810
          keyword = "false";
 
2811
        }
 
2812
        else if (!_cups_strncasecmp(format, "image/", 6) ||
 
2813
                 !_cups_strcasecmp(format, "application/vnd.cups-raster"))
 
2814
        {
 
2815
         /*
 
2816
          * Multi-page image formats will have copies applied by the upstream
 
2817
          * filters...
 
2818
          */
 
2819
 
 
2820
          copies = 1;
 
2821
        }
 
2822
      }
 
2823
 
 
2824
      if (doc_handling_sup)
 
2825
      {
 
2826
        if (!_cups_strcasecmp(keyword, "true"))
 
2827
          collate_str = "separate-documents-collated-copies";
 
2828
        else
 
2829
          collate_str = "separate-documents-uncollated-copies";
 
2830
 
 
2831
        for (i = 0; i < doc_handling_sup->num_values; i ++)
 
2832
          if (!strcmp(doc_handling_sup->values[i].string.text, collate_str))
 
2833
          {
 
2834
            ippAddString(request, IPP_TAG_JOB, IPP_TAG_KEYWORD,
 
2835
                         "multiple-document-handling", NULL, collate_str);
 
2836
            break;
 
2837
          }
 
2838
 
 
2839
        if (i >= doc_handling_sup->num_values)
 
2840
          copies = 1;
 
2841
      }
 
2842
 
 
2843
     /*
 
2844
      * Map finishing options...
 
2845
      */
 
2846
 
 
2847
      num_finishings = _ppdCacheGetFinishingValues(pc, num_options, options,
 
2848
                                                   (int)(sizeof(finishings) /
 
2849
                                                         sizeof(finishings[0])),
 
2850
                                                   finishings);
 
2851
      if (num_finishings > 0)
 
2852
        ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings",
 
2853
                       num_finishings, finishings);
 
2854
 
 
2855
     /*
 
2856
      * Map FaxOut options...
 
2857
      */
 
2858
 
 
2859
      if ((keyword = cupsGetOption("phone", num_options, options)) != NULL)
 
2860
      {
 
2861
        ipp_t   *destination;           /* destination collection */
 
2862
        char    tel_uri[1024];          /* tel: URI */
 
2863
 
 
2864
        destination = ippNew();
 
2865
 
 
2866
        httpAssembleURI(HTTP_URI_CODING_ALL, tel_uri, sizeof(tel_uri), "tel",
 
2867
                        NULL, NULL, 0, keyword);
 
2868
        ippAddString(destination, IPP_TAG_JOB, IPP_TAG_URI, "destination-uri",
 
2869
                     NULL, tel_uri);
 
2870
 
 
2871
        if ((keyword = cupsGetOption("faxPrefix", num_options,
 
2872
                                     options)) != NULL && *keyword)
 
2873
          ippAddString(destination, IPP_TAG_JOB, IPP_TAG_TEXT,
 
2874
                       "pre-dial-string", NULL, keyword);
 
2875
 
 
2876
        ippAddCollection(request, IPP_TAG_JOB, "destination-uris", destination);
 
2877
        ippDelete(destination);
 
2878
      }
 
2879
    }
 
2880
    else
 
2881
    {
 
2882
     /*
 
2883
      * When talking to another CUPS server, send all options...
 
2884
      */
 
2885
 
 
2886
      fputs("DEBUG: Adding all operation/job attributes.\n", stderr);
 
2887
      cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
 
2888
      cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
 
2889
    }
 
2890
 
 
2891
    if (copies > 1 && (!pc || copies <= pc->max_copies))
 
2892
      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", copies);
 
2893
  }
 
2894
 
 
2895
  fprintf(stderr, "DEBUG: IPP/%d.%d %s #%d\n", version / 10, version % 10,
 
2896
          ippOpString(ippGetOperation(request)), ippGetRequestId(request));
 
2897
  for (group = IPP_TAG_ZERO, attr = ippFirstAttribute(request);
 
2898
       attr;
 
2899
       attr = ippNextAttribute(request))
 
2900
  {
 
2901
    const char *name = ippGetName(attr);
 
2902
 
 
2903
    if (!name)
 
2904
    {
 
2905
      group = IPP_TAG_ZERO;
 
2906
      continue;
 
2907
    }
 
2908
 
 
2909
    if (group != ippGetGroupTag(attr))
 
2910
    {
 
2911
      group = ippGetGroupTag(attr);
 
2912
      fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(group));
 
2913
    }
 
2914
 
 
2915
    ippAttributeString(attr, buffer, sizeof(buffer));
 
2916
    fprintf(stderr, "DEBUG: %s %s%s %s\n", name,
 
2917
            ippGetCount(attr) > 1 ? "1setOf " : "",
 
2918
            ippTagString(ippGetValueTag(attr)), buffer);
 
2919
  }
 
2920
 
 
2921
  fprintf(stderr, "DEBUG: ---- %s ----\n", ippTagString(IPP_TAG_END));
 
2922
 
 
2923
  return (request);
 
2924
}
 
2925
 
 
2926
 
 
2927
/*
 
2928
 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
 
2929
 */
 
2930
 
 
2931
static const char *                     /* O - Password  */
 
2932
password_cb(const char *prompt,         /* I - Prompt (not used) */
 
2933
            http_t     *http,           /* I - Connection */
 
2934
            const char *method,         /* I - Request method (not used) */
 
2935
            const char *resource,       /* I - Resource path (not used) */
 
2936
            int        *password_tries) /* I - Password tries */
 
2937
{
 
2938
  char  def_username[HTTP_MAX_VALUE];   /* Default username */
 
2939
 
 
2940
 
 
2941
  fprintf(stderr, "DEBUG: password_cb(prompt=\"%s\", http=%p, method=\"%s\", "
 
2942
                  "resource=\"%s\", password_tries=%p(%d)), password=%p\n",
 
2943
          prompt, http, method, resource, password_tries, *password_tries,
 
2944
          password);
 
2945
 
 
2946
  (void)prompt;
 
2947
  (void)method;
 
2948
  (void)resource;
 
2949
 
 
2950
 /*
 
2951
  * Remember that we need to authenticate...
 
2952
  */
 
2953
 
 
2954
  auth_info_required = "username,password";
 
2955
 
 
2956
  if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
 
2957
                      def_username))
 
2958
  {
 
2959
    char        quoted[HTTP_MAX_VALUE * 2 + 4];
 
2960
                                        /* Quoted string */
 
2961
 
 
2962
    fprintf(stderr, "ATTR: auth-info-default=%s,\n",
 
2963
            quote_string(def_username, quoted, sizeof(quoted)));
 
2964
  }
 
2965
 
 
2966
  if (password && *password && *password_tries < 3)
 
2967
  {
 
2968
    (*password_tries) ++;
 
2969
 
 
2970
    return (password);
 
2971
  }
 
2972
  else
 
2973
  {
 
2974
   /*
 
2975
    * Give up after 3 tries or if we don't have a password to begin with...
 
2976
    */
 
2977
 
 
2978
    return (NULL);
 
2979
  }
 
2980
}
 
2981
 
 
2982
 
 
2983
/*
 
2984
 * 'quote_string()' - Quote a string value.
 
2985
 */
 
2986
 
 
2987
static const char *                     /* O - Quoted string */
 
2988
quote_string(const char *s,             /* I - String */
 
2989
             char       *q,             /* I - Quoted string buffer */
 
2990
             size_t     qsize)          /* I - Size of quoted string buffer */
 
2991
{
 
2992
  char  *qptr,                          /* Pointer into string buffer */
 
2993
        *qend;                          /* End of string buffer */
 
2994
 
 
2995
 
 
2996
  qptr = q;
 
2997
  qend = q + qsize - 5;
 
2998
 
 
2999
  if (qend < q)
 
3000
  {
 
3001
    *q = '\0';
 
3002
    return (q);
 
3003
  }
 
3004
 
 
3005
  *qptr++ = '\'';
 
3006
  *qptr++ = '\"';
 
3007
 
 
3008
  while (*s && qptr < qend)
 
3009
  {
 
3010
    if (*s == '\\' || *s == '\"' || *s == '\'')
 
3011
    {
 
3012
      if (q < (qend - 3))
 
3013
      {
 
3014
        *qptr++ = '\\';
 
3015
        *qptr++ = '\\';
 
3016
        *qptr++ = '\\';
 
3017
      }
 
3018
      else
 
3019
        break;
 
3020
    }
 
3021
 
 
3022
    *qptr++ = *s++;
 
3023
  }
 
3024
 
 
3025
  *qptr++ = '\"';
 
3026
  *qptr++ = '\'';
 
3027
  *qptr   = '\0';
 
3028
 
 
3029
  return (q);
 
3030
}
 
3031
 
 
3032
 
 
3033
/*
 
3034
 * 'report_attr()' - Report an IPP attribute value.
 
3035
 */
 
3036
 
 
3037
static void
 
3038
report_attr(ipp_attribute_t *attr)      /* I - Attribute */
 
3039
{
 
3040
  int           i;                      /* Looping var */
 
3041
  char          value[1024],            /* Value string */
 
3042
                *valptr;                /* Pointer into value string */
 
3043
  const char    *cached;                /* Cached attribute */
 
3044
 
 
3045
 
 
3046
 /*
 
3047
  * Convert the attribute values into quoted strings...
 
3048
  */
 
3049
 
 
3050
  for (i = 0, valptr = value;
 
3051
       i < attr->num_values && valptr < (value + sizeof(value) - 10);
 
3052
       i ++)
 
3053
  {
 
3054
    if (i > 0)
 
3055
      *valptr++ = ',';
 
3056
 
 
3057
    switch (attr->value_tag)
 
3058
    {
 
3059
      case IPP_TAG_INTEGER :
 
3060
      case IPP_TAG_ENUM :
 
3061
          snprintf(valptr, sizeof(value) - (valptr - value), "%d",
 
3062
                   attr->values[i].integer);
 
3063
          valptr += strlen(valptr);
 
3064
          break;
 
3065
 
 
3066
      case IPP_TAG_TEXT :
 
3067
      case IPP_TAG_NAME :
 
3068
      case IPP_TAG_KEYWORD :
 
3069
          quote_string(attr->values[i].string.text, valptr,
 
3070
                       value + sizeof(value) - valptr);
 
3071
          valptr += strlen(valptr);
 
3072
          break;
 
3073
 
 
3074
      default :
 
3075
         /*
 
3076
          * Unsupported value type...
 
3077
          */
 
3078
 
 
3079
          return;
 
3080
    }
 
3081
  }
 
3082
 
 
3083
  *valptr = '\0';
 
3084
 
 
3085
  _cupsMutexLock(&report_mutex);
 
3086
 
 
3087
  if ((cached = cupsGetOption(attr->name, num_attr_cache,
 
3088
                              attr_cache)) == NULL || strcmp(cached, value))
 
3089
  {
 
3090
   /*
 
3091
    * Tell the scheduler about the new values...
 
3092
    */
 
3093
 
 
3094
    num_attr_cache = cupsAddOption(attr->name, value, num_attr_cache,
 
3095
                                   &attr_cache);
 
3096
    fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
 
3097
  }
 
3098
 
 
3099
  _cupsMutexUnlock(&report_mutex);
 
3100
}
 
3101
 
 
3102
 
 
3103
/*
 
3104
 * 'report_printer_state()' - Report the printer state.
 
3105
 */
 
3106
 
 
3107
static void
 
3108
report_printer_state(ipp_t *ipp)        /* I - IPP response */
 
3109
{
 
3110
  ipp_attribute_t       *pa,            /* printer-alert */
 
3111
                        *pam,           /* printer-alert-message */
 
3112
                        *psm,           /* printer-state-message */
 
3113
                        *reasons,       /* printer-state-reasons */
 
3114
                        *marker;        /* marker-* attributes */
 
3115
  char                  value[1024],    /* State/message string */
 
3116
                        *valptr;        /* Pointer into string */
 
3117
  static int            ipp_supplies = -1;
 
3118
                                        /* Report supply levels? */
 
3119
 
 
3120
 
 
3121
 /*
 
3122
  * Report alerts and messages...
 
3123
  */
 
3124
 
 
3125
  if ((pa = ippFindAttribute(ipp, "printer-alert", IPP_TAG_TEXT)) != NULL)
 
3126
    report_attr(pa);
 
3127
 
 
3128
  if ((pam = ippFindAttribute(ipp, "printer-alert-message",
 
3129
                              IPP_TAG_TEXT)) != NULL)
 
3130
    report_attr(pam);
 
3131
 
 
3132
  if ((psm = ippFindAttribute(ipp, "printer-state-message",
 
3133
                              IPP_TAG_TEXT)) != NULL)
 
3134
  {
 
3135
    char        *ptr;                   /* Pointer into message */
 
3136
 
 
3137
 
 
3138
    strlcpy(value, "INFO: ", sizeof(value));
 
3139
    for (ptr = psm->values[0].string.text, valptr = value + 6;
 
3140
         *ptr && valptr < (value + sizeof(value) - 6);
 
3141
         ptr ++)
 
3142
    {
 
3143
      if (*ptr < ' ' && *ptr > 0 && *ptr != '\t')
 
3144
      {
 
3145
       /*
 
3146
        * Substitute "<XX>" for the control character; sprintf is safe because
 
3147
        * we always leave 6 chars free at the end...
 
3148
        */
 
3149
 
 
3150
        sprintf(valptr, "<%02X>", *ptr);
 
3151
        valptr += 4;
 
3152
      }
 
3153
      else
 
3154
        *valptr++ = *ptr;
 
3155
    }
 
3156
 
 
3157
    *valptr++ = '\n';
 
3158
    *valptr   = '\0';
 
3159
 
 
3160
    fputs(value, stderr);
 
3161
  }
 
3162
 
 
3163
 /*
 
3164
  * Now report printer-state-reasons, filtering out some of the reasons we never
 
3165
  * want to set...
 
3166
  */
 
3167
 
 
3168
  if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
 
3169
                                  IPP_TAG_KEYWORD)) == NULL)
 
3170
    return;
 
3171
 
 
3172
  update_reasons(reasons, NULL);
 
3173
 
 
3174
 /*
 
3175
  * Relay the current marker-* attribute values...
 
3176
  */
 
3177
 
 
3178
  if (ipp_supplies < 0)
 
3179
  {
 
3180
    ppd_file_t  *ppd;                   /* PPD file */
 
3181
    ppd_attr_t  *ppdattr;               /* Attribute in PPD file */
 
3182
 
 
3183
    if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
 
3184
        (ppdattr = ppdFindAttr(ppd, "cupsIPPSupplies", NULL)) != NULL &&
 
3185
        ppdattr->value && _cups_strcasecmp(ppdattr->value, "true"))
 
3186
      ipp_supplies = 0;
 
3187
    else
 
3188
      ipp_supplies = 1;
 
3189
 
 
3190
    ppdClose(ppd);
 
3191
  }
 
3192
 
 
3193
  if (ipp_supplies > 0)
 
3194
  {
 
3195
    if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
 
3196
      report_attr(marker);
 
3197
    if ((marker = ippFindAttribute(ipp, "marker-high-levels",
 
3198
                                   IPP_TAG_INTEGER)) != NULL)
 
3199
      report_attr(marker);
 
3200
    if ((marker = ippFindAttribute(ipp, "marker-levels",
 
3201
                                   IPP_TAG_INTEGER)) != NULL)
 
3202
      report_attr(marker);
 
3203
    if ((marker = ippFindAttribute(ipp, "marker-low-levels",
 
3204
                                   IPP_TAG_INTEGER)) != NULL)
 
3205
      report_attr(marker);
 
3206
    if ((marker = ippFindAttribute(ipp, "marker-message",
 
3207
                                   IPP_TAG_TEXT)) != NULL)
 
3208
      report_attr(marker);
 
3209
    if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
 
3210
      report_attr(marker);
 
3211
    if ((marker = ippFindAttribute(ipp, "marker-types",
 
3212
                                   IPP_TAG_KEYWORD)) != NULL)
 
3213
      report_attr(marker);
 
3214
  }
 
3215
}
 
3216
 
 
3217
 
 
3218
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
 
3219
/*
 
3220
 * 'run_as_user()' - Run the IPP backend as the printing user.
 
3221
 *
 
3222
 * This function uses an XPC-based user agent to run the backend as the printing
 
3223
 * user. We need to do this in order to have access to the user's Kerberos
 
3224
 * credentials.
 
3225
 */
 
3226
 
 
3227
static int                              /* O - Exit status */
 
3228
run_as_user(char       *argv[],         /* I - Command-line arguments */
 
3229
            uid_t      uid,             /* I - User ID */
 
3230
            const char *device_uri,     /* I - Device URI */
 
3231
            int        fd)              /* I - File to print */
 
3232
{
 
3233
  const char            *auth_negotiate;/* AUTH_NEGOTIATE env var */
 
3234
  xpc_connection_t      conn;           /* Connection to XPC service */
 
3235
  xpc_object_t          request;        /* Request message dictionary */
 
3236
  __block xpc_object_t  response;       /* Response message dictionary */
 
3237
  dispatch_semaphore_t  sem;            /* Semaphore for waiting for response */
 
3238
  int                   status = CUPS_BACKEND_FAILED;
 
3239
                                        /* Status of request */
 
3240
 
 
3241
 
 
3242
  fprintf(stderr, "DEBUG: Running IPP backend as UID %d.\n", (int)uid);
 
3243
 
 
3244
 /*
 
3245
  * Connect to the user agent for the specified UID...
 
3246
  */
 
3247
 
 
3248
  conn = xpc_connection_create_mach_service(kPMPrintUIToolAgent,
 
3249
                                            dispatch_get_global_queue(0, 0), 0);
 
3250
  if (!conn)
 
3251
  {
 
3252
    _cupsLangPrintFilter(stderr, "ERROR",
 
3253
                         _("Unable to start backend process."));
 
3254
    fputs("DEBUG: Unable to create connection to agent.\n", stderr);
 
3255
    goto cleanup;
 
3256
  }
 
3257
 
 
3258
  xpc_connection_set_event_handler(conn,
 
3259
                                   ^(xpc_object_t event)
 
3260
                                   {
 
3261
                                     xpc_type_t messageType = xpc_get_type(event);
 
3262
 
 
3263
                                     if (messageType == XPC_TYPE_ERROR)
 
3264
                                     {
 
3265
                                       if (event == XPC_ERROR_CONNECTION_INTERRUPTED)
 
3266
                                         fprintf(stderr, "DEBUG: Interrupted connection to service %s.\n",
 
3267
                                                 xpc_connection_get_name(conn));
 
3268
                                       else if (event == XPC_ERROR_CONNECTION_INVALID)
 
3269
                                         fprintf(stderr, "DEBUG: Connection invalid for service %s.\n",
 
3270
                                                 xpc_connection_get_name(conn));
 
3271
                                       else
 
3272
                                         fprintf(stderr, "DEBUG: Unxpected error for service %s: %s\n",
 
3273
                                                 xpc_connection_get_name(conn),
 
3274
                                                 xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
 
3275
                                     }
 
3276
                                   });
 
3277
  xpc_connection_set_target_uid(conn, uid);
 
3278
  xpc_connection_resume(conn);
 
3279
 
 
3280
 /*
 
3281
  * Try starting the backend...
 
3282
  */
 
3283
 
 
3284
  request = xpc_dictionary_create(NULL, NULL, 0);
 
3285
  xpc_dictionary_set_int64(request, "command", kPMStartJob);
 
3286
  xpc_dictionary_set_string(request, "device-uri", device_uri);
 
3287
  xpc_dictionary_set_string(request, "job-id", argv[1]);
 
3288
  xpc_dictionary_set_string(request, "user", argv[2]);
 
3289
  xpc_dictionary_set_string(request, "title", argv[3]);
 
3290
  xpc_dictionary_set_string(request, "copies", argv[4]);
 
3291
  xpc_dictionary_set_string(request, "options", argv[5]);
 
3292
  xpc_dictionary_set_string(request, "auth-info-required",
 
3293
                            getenv("AUTH_INFO_REQUIRED"));
 
3294
  if ((auth_negotiate = getenv("AUTH_NEGOTIATE")) != NULL)
 
3295
    xpc_dictionary_set_string(request, "auth-negotiate", auth_negotiate);
 
3296
  xpc_dictionary_set_fd(request, "stdin", fd);
 
3297
  xpc_dictionary_set_fd(request, "stderr", 2);
 
3298
  xpc_dictionary_set_fd(request, "side-channel", CUPS_SC_FD);
 
3299
 
 
3300
  sem      = dispatch_semaphore_create(0);
 
3301
  response = NULL;
 
3302
 
 
3303
  xpc_connection_send_message_with_reply(conn, request,
 
3304
                                         dispatch_get_global_queue(0,0),
 
3305
                                         ^(xpc_object_t reply)
 
3306
                                         {
 
3307
                                           /* Save the response and wake up */
 
3308
                                           if (xpc_get_type(reply)
 
3309
                                                   == XPC_TYPE_DICTIONARY)
 
3310
                                             response = xpc_retain(reply);
 
3311
 
 
3312
                                           dispatch_semaphore_signal(sem);
 
3313
                                         });
 
3314
 
 
3315
  dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
 
3316
  xpc_release(request);
 
3317
  dispatch_release(sem);
 
3318
 
 
3319
  if (response)
 
3320
  {
 
3321
    child_pid = xpc_dictionary_get_int64(response, "child-pid");
 
3322
 
 
3323
    xpc_release(response);
 
3324
 
 
3325
    if (child_pid)
 
3326
      fprintf(stderr, "DEBUG: Child PID=%d.\n", child_pid);
 
3327
    else
 
3328
    {
 
3329
      _cupsLangPrintFilter(stderr, "ERROR",
 
3330
                           _("Unable to start backend process."));
 
3331
      fputs("DEBUG: No child PID.\n", stderr);
 
3332
      goto cleanup;
 
3333
    }
 
3334
  }
 
3335
  else
 
3336
  {
 
3337
    _cupsLangPrintFilter(stderr, "ERROR",
 
3338
                         _("Unable to start backend process."));
 
3339
    fputs("DEBUG: No reply from agent.\n", stderr);
 
3340
    goto cleanup;
 
3341
  }
 
3342
 
 
3343
 /*
 
3344
  * Then wait for the backend to finish...
 
3345
  */
 
3346
 
 
3347
  request = xpc_dictionary_create(NULL, NULL, 0);
 
3348
  xpc_dictionary_set_int64(request, "command", kPMWaitForJob);
 
3349
  xpc_dictionary_set_fd(request, "stderr", 2);
 
3350
 
 
3351
  sem      = dispatch_semaphore_create(0);
 
3352
  response = NULL;
 
3353
 
 
3354
  xpc_connection_send_message_with_reply(conn, request,
 
3355
                                         dispatch_get_global_queue(0,0),
 
3356
                                         ^(xpc_object_t reply)
 
3357
                                         {
 
3358
                                           /* Save the response and wake up */
 
3359
                                           if (xpc_get_type(reply)
 
3360
                                                   == XPC_TYPE_DICTIONARY)
 
3361
                                             response = xpc_retain(reply);
 
3362
 
 
3363
                                           dispatch_semaphore_signal(sem);
 
3364
                                         });
 
3365
 
 
3366
  dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
 
3367
  xpc_release(request);
 
3368
  dispatch_release(sem);
 
3369
 
 
3370
  if (response)
 
3371
  {
 
3372
    status = xpc_dictionary_get_int64(response, "status");
 
3373
 
 
3374
    if (status == SIGTERM || status == SIGKILL || status == SIGPIPE)
 
3375
    {
 
3376
      fprintf(stderr, "DEBUG: Child terminated on signal %d.\n", status);
 
3377
      status = CUPS_BACKEND_FAILED;
 
3378
    }
 
3379
    else if (WIFSIGNALED(status))
 
3380
    {
 
3381
      fprintf(stderr, "DEBUG: Child crashed on signal %d.\n", status);
 
3382
      status = CUPS_BACKEND_STOP;
 
3383
    }
 
3384
    else if (WIFEXITED(status))
 
3385
    {
 
3386
      status = WEXITSTATUS(status);
 
3387
      fprintf(stderr, "DEBUG: Child exited with status %d.\n", status);
 
3388
    }
 
3389
 
 
3390
    xpc_release(response);
 
3391
  }
 
3392
  else
 
3393
    _cupsLangPrintFilter(stderr, "ERROR",
 
3394
                         _("Unable to get backend exit status."));
 
3395
 
 
3396
  cleanup:
 
3397
 
 
3398
  if (conn)
 
3399
  {
 
3400
    xpc_connection_cancel(conn);
 
3401
    xpc_release(conn);
 
3402
  }
 
3403
 
 
3404
  return (status);
 
3405
}
 
3406
#endif /* HAVE_GSSAPI && HAVE_XPC */
 
3407
 
 
3408
 
 
3409
/*
 
3410
 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
 
3411
 */
 
3412
 
 
3413
static void
 
3414
sigterm_handler(int sig)                /* I - Signal */
 
3415
{
 
3416
  (void)sig;    /* remove compiler warnings... */
 
3417
 
 
3418
  write(2, "DEBUG: Got SIGTERM.\n", 20);
 
3419
 
 
3420
#if defined(HAVE_GSSAPI) && defined(HAVE_XPC)
 
3421
  if (child_pid)
 
3422
  {
 
3423
    kill(child_pid, sig);
 
3424
    child_pid = 0;
 
3425
  }
 
3426
#endif /* HAVE_GSSAPI && HAVE_XPC */
 
3427
 
 
3428
  if (!job_canceled)
 
3429
  {
 
3430
   /*
 
3431
    * Flag that the job should be canceled...
 
3432
    */
 
3433
 
 
3434
    write(2, "DEBUG: job_canceled = 1.\n", 25);
 
3435
 
 
3436
    job_canceled = 1;
 
3437
    return;
 
3438
  }
 
3439
 
 
3440
 /*
 
3441
  * The scheduler already tried to cancel us once, now just terminate
 
3442
  * after removing our temp file!
 
3443
  */
 
3444
 
 
3445
  if (tmpfilename[0])
 
3446
    unlink(tmpfilename);
 
3447
 
 
3448
  exit(1);
 
3449
}
 
3450
 
 
3451
 
 
3452
/*
 
3453
 * 'timeout_cb()' - Handle HTTP timeouts.
 
3454
 */
 
3455
 
 
3456
static int                              /* O - 1 to continue, 0 to cancel */
 
3457
timeout_cb(http_t *http,                /* I - Connection to server (unused) */
 
3458
           void   *user_data)           /* I - User data (unused) */
 
3459
{
 
3460
  (void)http;
 
3461
  (void)user_data;
 
3462
 
 
3463
  return (!job_canceled);
 
3464
}
 
3465
 
 
3466
 
 
3467
/*
 
3468
 * 'update_reasons()' - Update the printer-state-reasons values.
 
3469
 */
 
3470
 
 
3471
static void
 
3472
update_reasons(ipp_attribute_t *attr,   /* I - printer-state-reasons or NULL */
 
3473
               const char      *s)      /* I - STATE: string or NULL */
 
3474
{
 
3475
  char          op;                     /* Add (+), remove (-), replace (\0) */
 
3476
  cups_array_t  *new_reasons;           /* New reasons array */
 
3477
  char          *reason,                /* Current reason */
 
3478
                add[2048],              /* Reasons added string */
 
3479
                *addptr,                /* Pointer into add string */
 
3480
                rem[2048],              /* Reasons removed string */
 
3481
                *remptr;                /* Pointer into remove string */
 
3482
  const char    *addprefix,             /* Current add string prefix */
 
3483
                *remprefix;             /* Current remove string prefix */
 
3484
 
 
3485
 
 
3486
  fprintf(stderr, "DEBUG: update_reasons(attr=%d(%s%s), s=\"%s\")\n",
 
3487
          attr ? attr->num_values : 0, attr ? attr->values[0].string.text : "",
 
3488
          attr && attr->num_values > 1 ? ",..." : "", s ? s : "(null)");
 
3489
 
 
3490
 /*
 
3491
  * Create an array of new reason keyword strings...
 
3492
  */
 
3493
 
 
3494
  if (attr)
 
3495
  {
 
3496
    int i;                              /* Looping var */
 
3497
 
 
3498
    new_reasons = cupsArrayNew((cups_array_func_t)strcmp, NULL);
 
3499
    op          = '\0';
 
3500
 
 
3501
    for (i = 0; i < attr->num_values; i ++)
 
3502
    {
 
3503
      reason = attr->values[i].string.text;
 
3504
 
 
3505
      if (strcmp(reason, "none") &&
 
3506
          strcmp(reason, "none-report") &&
 
3507
          strcmp(reason, "paused") &&
 
3508
          strncmp(reason, "spool-area-full", 15) &&
 
3509
          strcmp(reason, "com.apple.print.recoverable-warning") &&
 
3510
          strncmp(reason, "cups-", 5))
 
3511
        cupsArrayAdd(new_reasons, reason);
 
3512
    }
 
3513
  }
 
3514
  else if (s)
 
3515
  {
 
3516
    if (*s == '+' || *s == '-')
 
3517
      op = *s++;
 
3518
    else
 
3519
      op = '\0';
 
3520
 
 
3521
    new_reasons = _cupsArrayNewStrings(s, ',');
 
3522
  }
 
3523
  else
 
3524
    return;
 
3525
 
 
3526
 /*
 
3527
  * Compute the changes...
 
3528
  */
 
3529
 
 
3530
  add[0]    = '\0';
 
3531
  addprefix = "STATE: +";
 
3532
  addptr    = add;
 
3533
  rem[0]    = '\0';
 
3534
  remprefix = "STATE: -";
 
3535
  remptr    = rem;
 
3536
 
 
3537
  fprintf(stderr, "DEBUG2: op='%c', new_reasons=%d, state_reasons=%d\n",
 
3538
          op ? op : ' ', cupsArrayCount(new_reasons),
 
3539
          cupsArrayCount(state_reasons));
 
3540
 
 
3541
  _cupsMutexLock(&report_mutex);
 
3542
 
 
3543
  if (op == '+')
 
3544
  {
 
3545
   /*
 
3546
    * Add reasons...
 
3547
    */
 
3548
 
 
3549
    for (reason = (char *)cupsArrayFirst(new_reasons);
 
3550
         reason;
 
3551
         reason = (char *)cupsArrayNext(new_reasons))
 
3552
    {
 
3553
      if (!cupsArrayFind(state_reasons, reason))
 
3554
      {
 
3555
        if (!strncmp(reason, "cups-remote-", 12))
 
3556
        {
 
3557
         /*
 
3558
          * If we are setting cups-remote-xxx, remove all other cups-remote-xxx
 
3559
          * keywords...
 
3560
          */
 
3561
 
 
3562
          char  *temp;          /* Current reason in state_reasons */
 
3563
 
 
3564
          cupsArraySave(state_reasons);
 
3565
 
 
3566
          for (temp = (char *)cupsArrayFirst(state_reasons);
 
3567
               temp;
 
3568
               temp = (char *)cupsArrayNext(state_reasons))
 
3569
            if (!strncmp(temp, "cups-remote-", 12))
 
3570
            {
 
3571
              snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
 
3572
                       temp);
 
3573
              remptr    += strlen(remptr);
 
3574
              remprefix = ",";
 
3575
 
 
3576
              cupsArrayRemove(state_reasons, temp);
 
3577
              break;
 
3578
            }
 
3579
 
 
3580
          cupsArrayRestore(state_reasons);
 
3581
        }
 
3582
 
 
3583
        cupsArrayAdd(state_reasons, reason);
 
3584
 
 
3585
        snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
 
3586
                 reason);
 
3587
        addptr    += strlen(addptr);
 
3588
        addprefix = ",";
 
3589
      }
 
3590
    }
 
3591
  }
 
3592
  else if (op == '-')
 
3593
  {
 
3594
   /*
 
3595
    * Remove reasons...
 
3596
    */
 
3597
 
 
3598
    for (reason = (char *)cupsArrayFirst(new_reasons);
 
3599
         reason;
 
3600
         reason = (char *)cupsArrayNext(new_reasons))
 
3601
    {
 
3602
      if (cupsArrayFind(state_reasons, reason))
 
3603
      {
 
3604
        snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
 
3605
                 reason);
 
3606
        remptr    += strlen(remptr);
 
3607
        remprefix = ",";
 
3608
 
 
3609
        cupsArrayRemove(state_reasons, reason);
 
3610
      }
 
3611
    }
 
3612
  }
 
3613
  else
 
3614
  {
 
3615
   /*
 
3616
    * Replace reasons...
 
3617
    */
 
3618
 
 
3619
    for (reason = (char *)cupsArrayFirst(state_reasons);
 
3620
         reason;
 
3621
         reason = (char *)cupsArrayNext(state_reasons))
 
3622
    {
 
3623
      if (strncmp(reason, "cups-", 5) && !cupsArrayFind(new_reasons, reason))
 
3624
      {
 
3625
        snprintf(remptr, sizeof(rem) - (remptr - rem), "%s%s", remprefix,
 
3626
                 reason);
 
3627
        remptr    += strlen(remptr);
 
3628
        remprefix = ",";
 
3629
 
 
3630
        cupsArrayRemove(state_reasons, reason);
 
3631
      }
 
3632
    }
 
3633
 
 
3634
    for (reason = (char *)cupsArrayFirst(new_reasons);
 
3635
         reason;
 
3636
         reason = (char *)cupsArrayNext(new_reasons))
 
3637
    {
 
3638
      if (!cupsArrayFind(state_reasons, reason))
 
3639
      {
 
3640
        cupsArrayAdd(state_reasons, reason);
 
3641
 
 
3642
        snprintf(addptr, sizeof(add) - (addptr - add), "%s%s", addprefix,
 
3643
                 reason);
 
3644
        addptr    += strlen(addptr);
 
3645
        addprefix = ",";
 
3646
      }
 
3647
    }
 
3648
  }
 
3649
 
 
3650
  _cupsMutexUnlock(&report_mutex);
 
3651
 
 
3652
 /*
 
3653
  * Report changes and return...
 
3654
  */
 
3655
 
 
3656
  if (add[0] && rem[0])
 
3657
    fprintf(stderr, "%s\n%s\n", add, rem);
 
3658
  else if (add[0])
 
3659
    fprintf(stderr, "%s\n", add);
 
3660
  else if (rem[0])
 
3661
    fprintf(stderr, "%s\n", rem);
 
3662
}
 
3663
 
 
3664
/*
 
3665
 * End of "$Id: ipp.c 11353 2013-10-23 19:53:08Z msweet $".
 
3666
 */