~ubuntu-branches/ubuntu/precise/cups/precise-security

« back to all changes in this revision

Viewing changes to .pc/ipp-backend-http-1.0-fallback-fix.patch/backend/ipp.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt, Till Kamppeter, Martin Pitt
  • Date: 2012-03-14 14:40:23 UTC
  • Revision ID: package-import@ubuntu.com-20120314144023-jqdb0v722119nnmi
Tags: 1.5.2-8
[ Till Kamppeter ]
* debian/patches/ipp-fixes-1.5.3.patch: Updated IPP backend to the state of
  the upcoming CUPS 1.5.3, fixing most of the known problems with printing
  to IPP printers or servers.
* debian/patches/ipp-backend-fails-to-trigger-authentication-prompts.patch,
  debian/patches/ipp-backend-http-1.0-fallback-fix.patch,
  debian/patches/ipp-backend-missing-document-type.patch: Removed old
  patches for the IPP backend.

[ Martin Pitt ]
* test-i18n-nonlinux.patch: Fix typo in #if which broke the patch. Hopefully
  Closes: #662996 for good now.

Show diffs side-by-side

added added

removed removed

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