]> git.pld-linux.org Git - packages/cups.git/blob - add-ipp-backend-of-cups-1.4.patch
Release 4 (by relup.sh)
[packages/cups.git] / add-ipp-backend-of-cups-1.4.patch
1 --- a/backend/Makefile
2 +++ b/backend/Makefile
3 @@ -21,6 +21,7 @@
4  # See http://www.cups.org/documentation.php/api-filter.html for more info...
5  RBACKENDS =    \
6                 ipp \
7 +               ipp14 \
8                 lpd \
9                 $(DNSSD_BACKEND)
10  UBACKENDS =    \
11 @@ -51,6 +51,7 @@
12                 snmp-supplies.o
13  OBJS   =       \
14                 ipp.o \
15 +               ipp14.o \
16                 lpd.o \
17                 dnssd.o \
18                 snmp.o \
19 @@ -218,6 +218,17 @@
20  
21  
22  #
23 +# ipp14
24 +#
25 +
26 +ipp14: ipp14.o ../cups/$(LIBCUPS) libbackend.a
27 +       echo Linking $@...
28 +       $(CC) $(LDFLAGS) -o ipp14 ipp14.o libbackend.a $(LIBS)
29 +       #$(RM) http
30 +       #$(LN) ipp14 http
31 +
32 +
33 +#
34  # lpd
35  #
36  
37 --- /dev/null
38 +++ b/backend/ipp14.c
39 @@ -0,0 +1,1953 @@
40 +/*
41 + * "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $"
42 + *
43 + *   IPP backend for the Common UNIX Printing System (CUPS).
44 + *
45 + *   Copyright 2007-2010 by Apple Inc.
46 + *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
47 + *
48 + *   These coded instructions, statements, and computer programs are the
49 + *   property of Apple Inc. and are protected by Federal copyright
50 + *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
51 + *   "LICENSE" which should have been included with this file.  If this
52 + *   file is missing or damaged, see the license at "http://www.cups.org/".
53 + *
54 + *   This file is subject to the Apple OS-Developed Software exception.
55 + *
56 + * Contents:
57 + *
58 + *   main()                 - Send a file to the printer or server.
59 + *   cancel_job()           - Cancel a print job.
60 + *   check_printer_state()  - Check the printer state...
61 + *   compress_files()       - Compress print files...
62 + *   password_cb()          - Disable the password prompt for
63 + *                            cupsDoFileRequest().
64 + *   report_attr()          - Report an IPP attribute value.
65 + *   report_printer_state() - Report the printer state.
66 + *   run_pictwps_filter()   - Convert PICT files to PostScript when printing
67 + *                            remotely.
68 + *   sigterm_handler()      - Handle 'terminate' signals that stop the backend.
69 + */
70 +
71 +/*
72 + * Include necessary headers.
73 + */
74 +
75 +#include <cups/http-private.h>
76 +#include "backend-private.h"
77 +#include <sys/types.h>
78 +#include <sys/stat.h>
79 +#include <sys/wait.h>
80 +
81 +/*
82 + * Globals...
83 + */
84 +
85 +static char    *password = NULL;       /* Password for device URI */
86 +static int     password_tries = 0;     /* Password tries */
87 +static const char *auth_info_required = "none";
88 +                                       /* New auth-info-required value */
89 +#ifdef __APPLE__
90 +static char    pstmpname[1024] = "";   /* Temporary PostScript file name */
91 +#endif /* __APPLE__ */
92 +static char    tmpfilename[1024] = ""; /* Temporary spool file name */
93 +static int     job_cancelled = 0;      /* Job cancelled? */
94 +
95 +
96 +/*
97 + * Local functions...
98 + */
99 +
100 +static void    cancel_job(http_t *http, const char *uri, int id,
101 +                          const char *resource, const char *user, int version);
102 +static void    check_printer_state(http_t *http, const char *uri,
103 +                                   const char *resource, const char *user,
104 +                                   int version, int job_id);
105 +#ifdef HAVE_LIBZ
106 +static void    compress_files(int num_files, char **files);
107 +#endif /* HAVE_LIBZ */
108 +static const char *password_cb(const char *);
109 +static void    report_attr(ipp_attribute_t *attr);
110 +static int     report_printer_state(ipp_t *ipp, int job_id);
111 +
112 +#ifdef __APPLE__
113 +static int     run_pictwps_filter(char **argv, const char *filename);
114 +#endif /* __APPLE__ */
115 +static void    sigterm_handler(int sig);
116 +
117 +
118 +/*
119 + * 'main()' - Send a file to the printer or server.
120 + *
121 + * Usage:
122 + *
123 + *    printer-uri job-id user title copies options [file]
124 + */
125 +
126 +int                                    /* O - Exit status */
127 +main(int  argc,                                /* I - Number of command-line args */
128 +     char *argv[])                     /* I - Command-line arguments */
129 +{
130 +  int          i;                      /* Looping var */
131 +  int          send_options;           /* Send job options? */
132 +  int          num_options;            /* Number of printer options */
133 +  cups_option_t        *options;               /* Printer options */
134 +  const char   *device_uri;            /* Device URI */
135 +  char         scheme[255],            /* Scheme in URI */
136 +               hostname[1024],         /* Hostname */
137 +               username[255],          /* Username info */
138 +               resource[1024],         /* Resource info (printer name) */
139 +               addrname[256],          /* Address name */
140 +               *optptr,                /* Pointer to URI options */
141 +               *name,                  /* Name of option */
142 +               *value,                 /* Value of option */
143 +               sep;                    /* Separator character */
144 +  int          snmp_fd,                /* SNMP socket */
145 +               start_count,            /* Page count via SNMP at start */
146 +               page_count,             /* Page count via SNMP */
147 +               have_supplies;          /* Printer supports supply levels? */
148 +  int          num_files;              /* Number of files to print */
149 +  char         **files,                /* Files to print */
150 +               *filename;              /* Pointer to single filename */
151 +  int          port;                   /* Port number (not used) */
152 +  char         uri[HTTP_MAX_URI];      /* Updated URI without user/pass */
153 +  ipp_status_t ipp_status;             /* Status of IPP request */
154 +  http_t       *http;                  /* HTTP connection */
155 +  ipp_t                *request,               /* IPP request */
156 +               *response,              /* IPP response */
157 +               *supported;             /* get-printer-attributes response */
158 +  time_t       start_time;             /* Time of first connect */
159 +  int          recoverable;            /* Recoverable error shown? */
160 +  int          contimeout;             /* Connection timeout */
161 +  int          delay;                  /* Delay for retries... */
162 +  int          compression,            /* Do compression of the job data? */
163 +               waitjob,                /* Wait for job complete? */
164 +               waitprinter;            /* Wait for printer ready? */
165 +  ipp_attribute_t *job_id_attr;                /* job-id attribute */
166 +  int          job_id;                 /* job-id value */
167 +  ipp_attribute_t *job_sheets;         /* job-media-sheets-completed */
168 +  ipp_attribute_t *job_state;          /* job-state */
169 +  ipp_attribute_t *copies_sup;         /* copies-supported */
170 +  ipp_attribute_t *format_sup;         /* document-format-supported */
171 +  ipp_attribute_t *printer_state;      /* printer-state attribute */
172 +  ipp_attribute_t *printer_accepting;  /* printer-is-accepting-jobs */
173 +  int          copies,                 /* Number of copies for job */
174 +               copies_remaining;       /* Number of copies remaining */
175 +  const char   *content_type,          /* CONTENT_TYPE environment variable */
176 +               *final_content_type;    /* FINAL_CONTENT_TYPE environment var */
177 +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
178 +  struct sigaction action;             /* Actions for POSIX signals */
179 +#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
180 +  int          version;                /* IPP version */
181 +  static const char * const pattrs[] =
182 +               {                       /* Printer attributes we want */
183 +                  "com.apple.print.recoverable-message",
184 +                 "copies-supported",
185 +                 "document-format-supported",
186 +                 "marker-colors",
187 +                 "marker-high-levels",
188 +                 "marker-levels",
189 +                 "marker-low-levels",
190 +                 "marker-message",
191 +                 "marker-names",
192 +                 "marker-types",
193 +                 "printer-is-accepting-jobs",
194 +                 "printer-state",
195 +                 "printer-state-message",
196 +                 "printer-state-reasons",
197 +               };
198 +  static const char * const jattrs[] =
199 +               {                       /* Job attributes we want */
200 +                 "job-media-sheets-completed",
201 +                 "job-state"
202 +               };
203 +
204 +
205 + /*
206 +  * Make sure status messages are not buffered...
207 +  */
208 +
209 +  setbuf(stderr, NULL);
210 +
211 + /*
212 +  * Ignore SIGPIPE and catch SIGTERM signals...
213 +  */
214 +
215 +#ifdef HAVE_SIGSET
216 +  sigset(SIGPIPE, SIG_IGN);
217 +  sigset(SIGTERM, sigterm_handler);
218 +#elif defined(HAVE_SIGACTION)
219 +  memset(&action, 0, sizeof(action));
220 +  action.sa_handler = SIG_IGN;
221 +  sigaction(SIGPIPE, &action, NULL);
222 +
223 +  sigemptyset(&action.sa_mask);
224 +  sigaddset(&action.sa_mask, SIGTERM);
225 +  action.sa_handler = sigterm_handler;
226 +  sigaction(SIGTERM, &action, NULL);
227 +#else
228 +  signal(SIGPIPE, SIG_IGN);
229 +  signal(SIGTERM, sigterm_handler);
230 +#endif /* HAVE_SIGSET */
231 +
232 + /*
233 +  * Check command-line...
234 +  */
235 +
236 +  if (argc == 1)
237 +  {
238 +    char *s;
239 +
240 +    if ((s = strrchr(argv[0], '/')) != NULL)
241 +      s ++;
242 +    else
243 +      s = argv[0];
244 +
245 +    printf("network %s \"Unknown\" \"%s (%s)\"\n",
246 +           s, _cupsLangString(cupsLangDefault(),
247 +                             _("Internet Printing Protocol")), s);
248 +    return (CUPS_BACKEND_OK);
249 +  }
250 +  else if (argc < 6)
251 +  {
252 +    _cupsLangPrintf(stderr,
253 +                    _("Usage: %s job-id user title copies options [file]\n"),
254 +                   argv[0]);
255 +    return (CUPS_BACKEND_STOP);
256 +  }
257 +
258 + /*
259 +  * Get the (final) content type...
260 +  */
261 +
262 +  if ((content_type = getenv("CONTENT_TYPE")) == NULL)
263 +    content_type = "application/octet-stream";
264 +
265 +  if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
266 +  {
267 +    final_content_type = content_type;
268 +
269 +    if (!strncmp(final_content_type, "printer/", 8))
270 +      final_content_type = "application/vnd.cups-raw";
271 +  }
272 +
273 + /*
274 +  * Extract the hostname and printer name from the URI...
275 +  */
276 +
277 +  if ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
278 +    return (CUPS_BACKEND_FAILED);
279 +
280 +  httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
281 +                  username, sizeof(username), hostname, sizeof(hostname), &port,
282 +                 resource, sizeof(resource));
283 +
284 +  if (!port)
285 +    port = IPP_PORT;                   /* Default to port 631 */
286 +
287 +  if (!strcmp(scheme, "https"))
288 +    cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
289 +  else
290 +    cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
291 +
292 + /*
293 +  * See if there are any options...
294 +  */
295 +
296 +  compression = 0;
297 +  version     = 11;
298 +  waitjob     = 1;
299 +  waitprinter = 1;
300 +  contimeout  = 7 * 24 * 60 * 60;
301 +
302 +  if ((optptr = strchr(resource, '?')) != NULL)
303 +  {
304 +   /*
305 +    * Yup, terminate the device name string and move to the first
306 +    * character of the optptr...
307 +    */
308 +
309 +    *optptr++ = '\0';
310 +
311 +   /*
312 +    * Then parse the optptr...
313 +    */
314 +
315 +    while (*optptr)
316 +    {
317 +     /*
318 +      * Get the name...
319 +      */
320 +
321 +      name = optptr;
322 +
323 +      while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
324 +        optptr ++;
325 +
326 +      if ((sep = *optptr) != '\0')
327 +        *optptr++ = '\0';
328 +
329 +      if (sep == '=')
330 +      {
331 +       /*
332 +        * Get the value...
333 +       */
334 +
335 +        value = optptr;
336 +
337 +       while (*optptr && *optptr != '+' && *optptr != '&')
338 +         optptr ++;
339 +
340 +        if (*optptr)
341 +         *optptr++ = '\0';
342 +      }
343 +      else
344 +        value = (char *)"";
345 +
346 +     /*
347 +      * Process the option...
348 +      */
349 +
350 +      if (!strcasecmp(name, "waitjob"))
351 +      {
352 +       /*
353 +        * Wait for job completion?
354 +       */
355 +
356 +        waitjob = !strcasecmp(value, "on") ||
357 +                 !strcasecmp(value, "yes") ||
358 +                 !strcasecmp(value, "true");
359 +      }
360 +      else if (!strcasecmp(name, "waitprinter"))
361 +      {
362 +       /*
363 +        * Wait for printer idle?
364 +       */
365 +
366 +        waitprinter = !strcasecmp(value, "on") ||
367 +                     !strcasecmp(value, "yes") ||
368 +                     !strcasecmp(value, "true");
369 +      }
370 +      else if (!strcasecmp(name, "encryption"))
371 +      {
372 +       /*
373 +        * Enable/disable encryption?
374 +       */
375 +
376 +        if (!strcasecmp(value, "always"))
377 +         cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
378 +        else if (!strcasecmp(value, "required"))
379 +         cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
380 +        else if (!strcasecmp(value, "never"))
381 +         cupsSetEncryption(HTTP_ENCRYPT_NEVER);
382 +        else if (!strcasecmp(value, "ifrequested"))
383 +         cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
384 +       else
385 +       {
386 +         _cupsLangPrintf(stderr,
387 +                         _("ERROR: Unknown encryption option value \"%s\"!\n"),
388 +                         value);
389 +        }
390 +      }
391 +      else if (!strcasecmp(name, "version"))
392 +      {
393 +        if (!strcmp(value, "1.0"))
394 +         version = 10;
395 +       else if (!strcmp(value, "1.1"))
396 +         version = 11;
397 +       else if (!strcmp(value, "2.0"))
398 +         version = 20;
399 +       else if (!strcmp(value, "2.1"))
400 +         version = 21;
401 +       else
402 +       {
403 +         _cupsLangPrintf(stderr,
404 +                         _("ERROR: Unknown version option value \"%s\"!\n"),
405 +                         value);
406 +       }
407 +      }
408 +#ifdef HAVE_LIBZ
409 +      else if (!strcasecmp(name, "compression"))
410 +      {
411 +        compression = !strcasecmp(value, "true") ||
412 +                     !strcasecmp(value, "yes") ||
413 +                     !strcasecmp(value, "on") ||
414 +                     !strcasecmp(value, "gzip");
415 +      }
416 +#endif /* HAVE_LIBZ */
417 +      else if (!strcasecmp(name, "contimeout"))
418 +      {
419 +       /*
420 +        * Set the connection timeout...
421 +       */
422 +
423 +       if (atoi(value) > 0)
424 +         contimeout = atoi(value);
425 +      }
426 +      else
427 +      {
428 +       /*
429 +        * Unknown option...
430 +       */
431 +
432 +       _cupsLangPrintf(stderr,
433 +                       _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
434 +                       name, value);
435 +      }
436 +    }
437 +  }
438 +
439 + /*
440 +  * If we have 7 arguments, print the file named on the command-line.
441 +  * Otherwise, copy stdin to a temporary file and print the temporary
442 +  * file.
443 +  */
444 +
445 +  if (argc == 6)
446 +  {
447 +   /*
448 +    * Copy stdin to a temporary file...
449 +    */
450 +
451 +    int                        fd;             /* File descriptor */
452 +    http_addrlist_t    *addrlist;      /* Address list */
453 +    off_t              tbytes;         /* Total bytes copied */
454 +
455 +
456 +    fputs("STATE: +connecting-to-device\n", stderr);
457 +    fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
458 +
459 +    if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, "1")) == NULL)
460 +    {
461 +      _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
462 +                     hostname);
463 +      return (CUPS_BACKEND_STOP);
464 +    }
465 +
466 +    snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
467 +
468 +    if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
469 +    {
470 +      _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
471 +      return (CUPS_BACKEND_FAILED);
472 +    }
473 +
474 +    _cupsLangPuts(stderr, _("INFO: Copying print data...\n"));
475 +
476 +    tbytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
477 +                            backendNetworkSideCB);
478 +
479 +    if (snmp_fd >= 0)
480 +      _cupsSNMPClose(snmp_fd);
481 +
482 +    httpAddrFreeList(addrlist);
483 +
484 +    close(fd);
485 +
486 +   /*
487 +    * Don't try printing files less than 2 bytes...
488 +    */
489 +
490 +    if (tbytes <= 1)
491 +    {
492 +      _cupsLangPuts(stderr, _("ERROR: Empty print file!\n"));
493 +      unlink(tmpfilename);
494 +      return (CUPS_BACKEND_FAILED);
495 +    }
496 +
497 +   /*
498 +    * Point to the single file from stdin...
499 +    */
500 +
501 +    filename     = tmpfilename;
502 +    num_files    = 1;
503 +    files        = &filename;
504 +    send_options = 0;
505 +  }
506 +  else
507 +  {
508 +   /*
509 +    * Point to the files on the command-line...
510 +    */
511 +
512 +    num_files    = argc - 6;
513 +    files        = argv + 6;
514 +    send_options = 1;
515 +
516 +#ifdef HAVE_LIBZ
517 +    if (compression)
518 +      compress_files(num_files, files);
519 +#endif /* HAVE_LIBZ */
520 +  }
521 +
522 +  fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
523 +
524 + /*
525 +  * Set the authentication info, if any...
526 +  */
527 +
528 +  cupsSetPasswordCB(password_cb);
529 +
530 +  if (username[0])
531 +  {
532 +   /*
533 +    * Use authenticaion information in the device URI...
534 +    */
535 +
536 +    if ((password = strchr(username, ':')) != NULL)
537 +      *password++ = '\0';
538 +
539 +    cupsSetUser(username);
540 +  }
541 +  else if (!getuid())
542 +  {
543 +   /*
544 +    * Try loading authentication information from the environment.
545 +    */
546 +
547 +    const char *ptr = getenv("AUTH_USERNAME");
548 +
549 +    if (ptr)
550 +      cupsSetUser(ptr);
551 +
552 +    password = getenv("AUTH_PASSWORD");
553 +  }
554 +
555 + /*
556 +  * Try connecting to the remote server...
557 +  */
558 +
559 +  delay       = 5;
560 +  recoverable = 0;
561 +  start_time  = time(NULL);
562 +
563 +  fputs("STATE: +connecting-to-device\n", stderr);
564 +
565 +  do
566 +  {
567 +    fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
568 +    _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
569 +
570 +    if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
571 +    {
572 +      if (job_cancelled)
573 +       break;
574 +
575 +      if (getenv("CLASS") != NULL)
576 +      {
577 +       /*
578 +        * If the CLASS environment variable is set, the job was submitted
579 +       * to a class and not to a specific queue.  In this case, we want
580 +       * to abort immediately so that the job can be requeued on the next
581 +       * available printer in the class.
582 +       */
583 +
584 +        _cupsLangPuts(stderr,
585 +                     _("INFO: Unable to contact printer, queuing on next "
586 +                       "printer in class...\n"));
587 +
588 +        if (tmpfilename[0])
589 +         unlink(tmpfilename);
590 +
591 +       /*
592 +        * Sleep 5 seconds to keep the job from requeuing too rapidly...
593 +       */
594 +
595 +       sleep(5);
596 +
597 +        return (CUPS_BACKEND_FAILED);
598 +      }
599 +
600 +      if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
601 +          errno == EHOSTUNREACH)
602 +      {
603 +        if (contimeout && (time(NULL) - start_time) > contimeout)
604 +       {
605 +         _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
606 +         return (CUPS_BACKEND_FAILED);
607 +       }
608 +
609 +        recoverable = 1;
610 +
611 +       _cupsLangPrintf(stderr,
612 +                       _("WARNING: recoverable: Network host \'%s\' is busy; "
613 +                         "will retry in %d seconds...\n"),
614 +                       hostname, delay);
615 +
616 +       sleep(delay);
617 +
618 +       if (delay < 30)
619 +         delay += 5;
620 +      }
621 +      else if (h_errno)
622 +      {
623 +       _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
624 +                       hostname);
625 +       return (CUPS_BACKEND_STOP);
626 +      }
627 +      else
628 +      {
629 +        recoverable = 1;
630 +
631 +        fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
632 +       _cupsLangPuts(stderr,
633 +                     _("ERROR: recoverable: Unable to connect to printer; will "
634 +                       "retry in 30 seconds...\n"));
635 +       sleep(30);
636 +      }
637 +
638 +      if (job_cancelled)
639 +       break;
640 +    }
641 +  }
642 +  while (http == NULL);
643 +
644 +  if (job_cancelled || !http)
645 +  {
646 +    if (tmpfilename[0])
647 +      unlink(tmpfilename);
648 +
649 +    return (CUPS_BACKEND_FAILED);
650 +  }
651 +
652 +  fputs("STATE: -connecting-to-device\n", stderr);
653 +  _cupsLangPuts(stderr, _("INFO: Connected to printer...\n"));
654 +
655 +#ifdef AF_INET6
656 +  if (http->hostaddr->addr.sa_family == AF_INET6)
657 +    fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
658 +           httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
659 +           ntohs(http->hostaddr->ipv6.sin6_port));
660 +  else
661 +#endif /* AF_INET6 */
662 +    if (http->hostaddr->addr.sa_family == AF_INET)
663 +      fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
664 +             httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
665 +             ntohs(http->hostaddr->ipv4.sin_port));
666 +
667 + /*
668 +  * See if the printer supports SNMP...
669 +  */
670 +
671 +  if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0)
672 +    have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count,
673 +                                         NULL);
674 +  else
675 +    have_supplies = start_count = 0;
676 +
677 + /*
678 +  * Build a URI for the printer and fill the standard IPP attributes for
679 +  * an IPP_PRINT_FILE request.  We can't use the URI in argv[0] because it
680 +  * might contain username:password information...
681 +  */
682 +
683 +  httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
684 +                 port, resource);
685 +
686 + /*
687 +  * First validate the destination and see if the device supports multiple
688 +  * copies.  We have to do this because some IPP servers (e.g. HP JetDirect)
689 +  * don't support the copies attribute...
690 +  */
691 +
692 +  copies_sup = NULL;
693 +  format_sup = NULL;
694 +  supported  = NULL;
695 +
696 +  do
697 +  {
698 +   /*
699 +    * Check for side-channel requests...
700 +    */
701 +
702 +    backendCheckSideChannel(snmp_fd, http->hostaddr);
703 +
704 +   /*
705 +    * Build the IPP request...
706 +    */
707 +
708 +    request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
709 +    request->request.op.version[0] = version / 10;
710 +    request->request.op.version[1] = version % 10;
711 +
712 +    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
713 +                NULL, uri);
714 +
715 +    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
716 +                  "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
717 +                 NULL, pattrs);
718 +
719 +   /*
720 +    * Do the request...
721 +    */
722 +
723 +    fputs("DEBUG: Getting supported attributes...\n", stderr);
724 +
725 +    if (http->version < HTTP_1_1)
726 +      httpReconnect(http);
727 +
728 +    if ((supported = cupsDoRequest(http, request, resource)) == NULL)
729 +      ipp_status = cupsLastError();
730 +    else
731 +      ipp_status = supported->request.status.status_code;
732 +
733 +    if (ipp_status > IPP_OK_CONFLICT)
734 +    {
735 +      if (ipp_status == IPP_PRINTER_BUSY ||
736 +         ipp_status == IPP_SERVICE_UNAVAILABLE)
737 +      {
738 +        if (contimeout && (time(NULL) - start_time) > contimeout)
739 +       {
740 +         _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
741 +         return (CUPS_BACKEND_FAILED);
742 +       }
743 +
744 +        recoverable = 1;
745 +
746 +       _cupsLangPrintf(stderr,
747 +                       _("WARNING: recoverable: Network host \'%s\' is busy; "
748 +                         "will retry in %d seconds...\n"),
749 +                       hostname, delay);
750 +
751 +        report_printer_state(supported, 0);
752 +
753 +       sleep(delay);
754 +
755 +       if (delay < 30)
756 +         delay += 5;
757 +      }
758 +      else if ((ipp_status == IPP_BAD_REQUEST ||
759 +               ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
760 +      {
761 +       /*
762 +       * Switch to IPP/1.0...
763 +       */
764 +
765 +       _cupsLangPrintf(stderr,
766 +                       _("INFO: Printer does not support IPP/%d.%d, trying "
767 +                         "IPP/1.0...\n"), version / 10, version % 10);
768 +       version = 10;
769 +       httpReconnect(http);
770 +      }
771 +      else if (ipp_status == IPP_NOT_FOUND)
772 +      {
773 +        _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n"));
774 +
775 +       if (supported)
776 +          ippDelete(supported);
777 +
778 +       return (CUPS_BACKEND_STOP);
779 +      }
780 +      else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
781 +      {
782 +       if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
783 +                    "Negotiate", 9))
784 +         auth_info_required = "negotiate";
785 +
786 +       fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
787 +       return (CUPS_BACKEND_AUTH_REQUIRED);
788 +      }
789 +      else
790 +      {
791 +       _cupsLangPrintf(stderr,
792 +                       _("ERROR: Unable to get printer status (%s)!\n"),
793 +                       cupsLastErrorString());
794 +        sleep(10);
795 +      }
796 +
797 +      if (supported)
798 +        ippDelete(supported);
799 +
800 +      continue;
801 +    }
802 +    else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
803 +                                           IPP_TAG_RANGE)) != NULL)
804 +    {
805 +     /*
806 +      * Has the "copies-supported" attribute - does it have an upper
807 +      * bound > 1?
808 +      */
809 +
810 +      if (copies_sup->values[0].range.upper <= 1)
811 +       copies_sup = NULL; /* No */
812 +    }
813 +
814 +    format_sup = ippFindAttribute(supported, "document-format-supported",
815 +                                 IPP_TAG_MIMETYPE);
816 +
817 +    if (format_sup)
818 +    {
819 +      fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
820 +             format_sup->num_values);
821 +      for (i = 0; i < format_sup->num_values; i ++)
822 +       fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
823 +               format_sup->values[i].string.text);
824 +    }
825 +
826 +    report_printer_state(supported, 0);
827 +  }
828 +  while (ipp_status > IPP_OK_CONFLICT);
829 +
830 + /*
831 +  * See if the printer is accepting jobs and is not stopped; if either
832 +  * condition is true and we are printing to a class, requeue the job...
833 +  */
834 +
835 +  if (getenv("CLASS") != NULL)
836 +  {
837 +    printer_state     = ippFindAttribute(supported, "printer-state",
838 +                                        IPP_TAG_ENUM);
839 +    printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
840 +                                        IPP_TAG_BOOLEAN);
841 +
842 +    if (printer_state == NULL ||
843 +       (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
844 +        waitprinter) ||
845 +       printer_accepting == NULL ||
846 +       !printer_accepting->values[0].boolean)
847 +    {
848 +     /*
849 +      * If the CLASS environment variable is set, the job was submitted
850 +      * to a class and not to a specific queue.  In this case, we want
851 +      * to abort immediately so that the job can be requeued on the next
852 +      * available printer in the class.
853 +      */
854 +
855 +      _cupsLangPuts(stderr,
856 +                    _("INFO: Unable to contact printer, queuing on next "
857 +                     "printer in class...\n"));
858 +
859 +      ippDelete(supported);
860 +      httpClose(http);
861 +
862 +      if (tmpfilename[0])
863 +       unlink(tmpfilename);
864 +
865 +     /*
866 +      * Sleep 5 seconds to keep the job from requeuing too rapidly...
867 +      */
868 +
869 +      sleep(5);
870 +
871 +      return (CUPS_BACKEND_FAILED);
872 +    }
873 +  }
874 +
875 +  if (recoverable)
876 +  {
877 +   /*
878 +    * If we've shown a recoverable error make sure the printer proxies
879 +    * have a chance to see the recovered message. Not pretty but
880 +    * necessary for now...
881 +    */
882 +
883 +    fputs("INFO: recovered: \n", stderr);
884 +    sleep(5);
885 +  }
886 +
887 + /*
888 +  * See if the printer supports multiple copies...
889 +  */
890 +
891 +  copies = atoi(argv[4]);
892 +
893 +  if (copies_sup || argc < 7)
894 +  {
895 +    copies_remaining = 1;
896 +
897 +    if (argc < 7)
898 +      copies = 1;
899 +  }
900 +  else
901 +    copies_remaining = copies;
902 +
903 + /*
904 +  * Then issue the print-job request...
905 +  */
906 +
907 +  job_id  = 0;
908 +
909 +  while (copies_remaining > 0)
910 +  {
911 +   /*
912 +    * Check for side-channel requests...
913 +    */
914 +
915 +    backendCheckSideChannel(snmp_fd, http->hostaddr);
916 +
917 +   /*
918 +    * Build the IPP request...
919 +    */
920 +
921 +    if (job_cancelled)
922 +      break;
923 +
924 +    if (num_files > 1)
925 +      request = ippNewRequest(IPP_CREATE_JOB);
926 +    else
927 +      request = ippNewRequest(IPP_PRINT_JOB);
928 +
929 +    request->request.op.version[0] = version / 10;
930 +    request->request.op.version[1] = version % 10;
931 +
932 +    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
933 +                NULL, uri);
934 +
935 +    fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
936 +
937 +    if (argv[2][0])
938 +      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
939 +                   "requesting-user-name", NULL, argv[2]);
940 +
941 +    fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
942 +
943 +   /*
944 +    * Only add a "job-name" attribute if the remote server supports
945 +    * copy generation - some IPP implementations like HP's don't seem
946 +    * to like UTF-8 job names (STR #1837)...
947 +    */
948 +
949 +    if (argv[3][0] && copies_sup)
950 +      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
951 +                  argv[3]);
952 +
953 +    fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
954 +
955 +#ifdef HAVE_LIBZ
956 +    if (compression)
957 +      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
958 +                   "compression", NULL, "gzip");
959 +#endif /* HAVE_LIBZ */
960 +
961 +   /*
962 +    * Handle options on the command-line...
963 +    */
964 +
965 +    options     = NULL;
966 +    num_options = cupsParseOptions(argv[5], 0, &options);
967 +
968 +#ifdef __APPLE__
969 +    if (!strcasecmp(final_content_type, "application/pictwps") &&
970 +        num_files == 1)
971 +    {
972 +      if (format_sup != NULL)
973 +      {
974 +       for (i = 0; i < format_sup->num_values; i ++)
975 +         if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
976 +           break;
977 +      }
978 +
979 +      if (format_sup == NULL || i >= format_sup->num_values)
980 +      {
981 +       /*
982 +       * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
983 +       * so convert the document to PostScript...
984 +       */
985 +
986 +       if (run_pictwps_filter(argv, files[0]))
987 +       {
988 +         if (pstmpname[0])
989 +           unlink(pstmpname);
990 +
991 +         if (tmpfilename[0])
992 +           unlink(tmpfilename);
993 +
994 +         return (CUPS_BACKEND_FAILED);
995 +        }
996 +
997 +        files[0] = pstmpname;
998 +
999 +       /*
1000 +       * Change the MIME type to application/postscript and change the
1001 +       * number of copies to 1...
1002 +       */
1003 +
1004 +       final_content_type = "application/postscript";
1005 +       copies             = 1;
1006 +       copies_remaining   = 1;
1007 +        send_options       = 0;
1008 +      }
1009 +    }
1010 +#endif /* __APPLE__ */
1011 +
1012 +    if (format_sup != NULL)
1013 +    {
1014 +      for (i = 0; i < format_sup->num_values; i ++)
1015 +        if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
1016 +          break;
1017 +
1018 +      if (i < format_sup->num_values)
1019 +        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1020 +                    "document-format", NULL, final_content_type);
1021 +    }
1022 +
1023 +    if (copies_sup && version > 10 && send_options)
1024 +    {
1025 +     /*
1026 +      * Only send options if the destination printer supports the copies
1027 +      * attribute and IPP/1.1.  This is a hack for the HP and Lexmark
1028 +      * implementations of IPP, which do not accept extension attributes
1029 +      * and incorrectly report a client-error-bad-request error instead of
1030 +      * the successful-ok-unsupported-attributes status.  In short, at least
1031 +      * some HP and Lexmark implementations of IPP are non-compliant.
1032 +      */
1033 +
1034 +      cupsEncodeOptions(request, num_options, options);
1035 +
1036 +      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
1037 +                    copies);
1038 +    }
1039 +
1040 +    cupsFreeOptions(num_options, options);
1041 +
1042 +   /*
1043 +    * If copies aren't supported, then we are likely dealing with an HP
1044 +    * JetDirect.  The HP IPP implementation seems to close the connection
1045 +    * after every request - that is, it does *not* implement HTTP Keep-
1046 +    * Alive, which is REQUIRED by HTTP/1.1...
1047 +    */
1048 +
1049 +    if (!copies_sup)
1050 +      httpReconnect(http);
1051 +
1052 +   /*
1053 +    * Do the request...
1054 +    */
1055 +
1056 +    if (http->version < HTTP_1_1)
1057 +      httpReconnect(http);
1058 +
1059 +    if (num_files > 1)
1060 +      response = cupsDoRequest(http, request, resource);
1061 +    else
1062 +      response = cupsDoFileRequest(http, request, resource, files[0]);
1063 +
1064 +    ipp_status = cupsLastError();
1065 +
1066 +    if (ipp_status > IPP_OK_CONFLICT)
1067 +    {
1068 +      job_id = 0;
1069 +
1070 +      if (job_cancelled)
1071 +        break;
1072 +
1073 +      if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1074 +         ipp_status == IPP_PRINTER_BUSY)
1075 +      {
1076 +        _cupsLangPuts(stderr,
1077 +                     _("INFO: Printer busy; will retry in 10 seconds...\n"));
1078 +       sleep(10);
1079 +      }
1080 +      else if ((ipp_status == IPP_BAD_REQUEST ||
1081 +               ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
1082 +      {
1083 +       /*
1084 +       * Switch to IPP/1.0...
1085 +       */
1086 +
1087 +       _cupsLangPrintf(stderr,
1088 +                       _("INFO: Printer does not support IPP/%d.%d, trying "
1089 +                         "IPP/1.0...\n"), version / 10, version % 10);
1090 +       version = 10;
1091 +       httpReconnect(http);
1092 +      }
1093 +      else
1094 +      {
1095 +       /*
1096 +       * Update auth-info-required as needed...
1097 +       */
1098 +
1099 +        _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
1100 +                       cupsLastErrorString());
1101 +
1102 +       if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1103 +       {
1104 +         fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1105 +                 httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1106 +
1107 +         /*
1108 +         * Normal authentication goes through the password callback, which sets
1109 +         * auth_info_required to "username,password".  Kerberos goes directly
1110 +         * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1111 +         * here and set auth_info_required as needed...
1112 +         */
1113 +
1114 +         if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1115 +                      "Negotiate", 9))
1116 +           auth_info_required = "negotiate";
1117 +       }
1118 +      }
1119 +    }
1120 +    else if ((job_id_attr = ippFindAttribute(response, "job-id",
1121 +                                             IPP_TAG_INTEGER)) == NULL)
1122 +    {
1123 +      _cupsLangPuts(stderr,
1124 +                    _("NOTICE: Print file accepted - job ID unknown.\n"));
1125 +      job_id = 0;
1126 +    }
1127 +    else
1128 +    {
1129 +      job_id = job_id_attr->values[0].integer;
1130 +      _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
1131 +                      job_id);
1132 +    }
1133 +
1134 +    ippDelete(response);
1135 +
1136 +    if (job_cancelled)
1137 +      break;
1138 +
1139 +    if (job_id && num_files > 1)
1140 +    {
1141 +      for (i = 0; i < num_files; i ++)
1142 +      {
1143 +       /*
1144 +       * Check for side-channel requests...
1145 +       */
1146 +
1147 +       backendCheckSideChannel(snmp_fd, http->hostaddr);
1148 +
1149 +       /*
1150 +        * Send the next file in the job...
1151 +       */
1152 +
1153 +       request = ippNewRequest(IPP_SEND_DOCUMENT);
1154 +       request->request.op.version[0] = version / 10;
1155 +       request->request.op.version[1] = version % 10;
1156 +
1157 +       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1158 +                    NULL, uri);
1159 +
1160 +        ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1161 +                     job_id);
1162 +
1163 +       if (argv[2][0])
1164 +         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1165 +                       "requesting-user-name", NULL, argv[2]);
1166 +
1167 +        if ((i + 1) == num_files)
1168 +         ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1169 +
1170 +        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1171 +                    "document-format", NULL, content_type);
1172 +
1173 +       if (http->version < HTTP_1_1)
1174 +         httpReconnect(http);
1175 +
1176 +        ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
1177 +
1178 +       if (cupsLastError() > IPP_OK_CONFLICT)
1179 +       {
1180 +         ipp_status = cupsLastError();
1181 +
1182 +         _cupsLangPrintf(stderr,
1183 +                         _("ERROR: Unable to add file %d to job: %s\n"),
1184 +                         job_id, cupsLastErrorString());
1185 +         break;
1186 +       }
1187 +      }
1188 +    }
1189 +
1190 +    if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1191 +    {
1192 +      fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1193 +      copies_remaining --;
1194 +    }
1195 +    else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1196 +            ipp_status == IPP_PRINTER_BUSY)
1197 +      continue;
1198 +    else
1199 +      copies_remaining --;
1200 +
1201 +   /*
1202 +    * Wait for the job to complete...
1203 +    */
1204 +
1205 +    if (!job_id || !waitjob)
1206 +      continue;
1207 +
1208 +    _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
1209 +
1210 +    for (delay = 1; !job_cancelled;)
1211 +    {
1212 +     /*
1213 +      * Check for side-channel requests...
1214 +      */
1215 +
1216 +      backendCheckSideChannel(snmp_fd, http->hostaddr);
1217 +
1218 +     /*
1219 +      * Build an IPP_GET_JOB_ATTRIBUTES request...
1220 +      */
1221 +
1222 +      request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1223 +      request->request.op.version[0] = version / 10;
1224 +      request->request.op.version[1] = version % 10;
1225 +
1226 +      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1227 +                  NULL, uri);
1228 +
1229 +      ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1230 +                   job_id);
1231 +
1232 +      if (argv[2][0])
1233 +       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1234 +                    "requesting-user-name", NULL, argv[2]);
1235 +
1236 +      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1237 +                    "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1238 +                   NULL, jattrs);
1239 +
1240 +     /*
1241 +      * Do the request...
1242 +      */
1243 +
1244 +      if (!copies_sup || http->version < HTTP_1_1)
1245 +       httpReconnect(http);
1246 +
1247 +      response   = cupsDoRequest(http, request, resource);
1248 +      ipp_status = cupsLastError();
1249 +
1250 +      if (ipp_status == IPP_NOT_FOUND)
1251 +      {
1252 +       /*
1253 +        * Job has gone away and/or the server has no job history...
1254 +       */
1255 +
1256 +        ippDelete(response);
1257 +
1258 +       ipp_status = IPP_OK;
1259 +        break;
1260 +      }
1261 +
1262 +      if (ipp_status > IPP_OK_CONFLICT)
1263 +      {
1264 +       if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1265 +           ipp_status != IPP_PRINTER_BUSY)
1266 +       {
1267 +         ippDelete(response);
1268 +
1269 +          _cupsLangPrintf(stderr,
1270 +                         _("ERROR: Unable to get job %d attributes (%s)!\n"),
1271 +                         job_id, cupsLastErrorString());
1272 +          break;
1273 +       }
1274 +      }
1275 +
1276 +      if (response)
1277 +      {
1278 +       if ((job_state = ippFindAttribute(response, "job-state",
1279 +                                         IPP_TAG_ENUM)) != NULL)
1280 +       {
1281 +        /*
1282 +          * Stop polling if the job is finished or pending-held...
1283 +         */
1284 +
1285 +          if (job_state->values[0].integer > IPP_JOB_STOPPED)
1286 +         {
1287 +           if ((job_sheets = ippFindAttribute(response, 
1288 +                                              "job-media-sheets-completed",
1289 +                                              IPP_TAG_INTEGER)) != NULL)
1290 +             fprintf(stderr, "PAGE: total %d\n",
1291 +                     job_sheets->values[0].integer);
1292 +
1293 +           ippDelete(response);
1294 +           break;
1295 +         }
1296 +       }
1297 +       else
1298 +       {
1299 +        /*
1300 +         * If the printer does not return a job-state attribute, it does not
1301 +         * conform to the IPP specification - break out immediately and fail
1302 +         * the job...
1303 +         */
1304 +
1305 +          fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1306 +               stderr);
1307 +         ipp_status = IPP_INTERNAL_ERROR;
1308 +         break;
1309 +       }
1310 +      }
1311 +
1312 +      ippDelete(response);
1313 +
1314 +     /*
1315 +      * Check the printer state and report it if necessary...
1316 +      */
1317 +
1318 +      check_printer_state(http, uri, resource, argv[2], version, job_id);
1319 +
1320 +     /*
1321 +      * Wait 1-10 seconds before polling again...
1322 +      */
1323 +
1324 +      sleep(delay);
1325 +
1326 +      delay ++;
1327 +      if (delay > 10)
1328 +        delay = 1;
1329 +    }
1330 +  }
1331 +
1332 + /*
1333 +  * Cancel the job as needed...
1334 +  */
1335 +
1336 +  if (job_cancelled && job_id)
1337 +    cancel_job(http, uri, job_id, resource, argv[2], version);
1338 +
1339 + /*
1340 +  * Check the printer state and report it if necessary...
1341 +  */
1342 +
1343 +  check_printer_state(http, uri, resource, argv[2], version, job_id);
1344 +
1345 + /*
1346 +  * Collect the final page count as needed...
1347 +  */
1348 +
1349 +  if (have_supplies && 
1350 +      !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1351 +      page_count > start_count)
1352 +    fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1353 +
1354 +#ifdef HAVE_GSSAPI
1355 + /*
1356 +  * See if we used Kerberos at all...
1357 +  */
1358 +
1359 +  if (http->gssctx)
1360 +    auth_info_required = "negotiate";
1361 +#endif /* HAVE_GSSAPI */
1362 +
1363 + /*
1364 +  * Free memory...
1365 +  */
1366 +
1367 +  httpClose(http);
1368 +
1369 +  ippDelete(supported);
1370 +
1371 + /*
1372 +  * Remove the temporary file(s) if necessary...
1373 +  */
1374 +
1375 +  if (tmpfilename[0])
1376 +    unlink(tmpfilename);
1377 +
1378 +#ifdef HAVE_LIBZ
1379 +  if (compression)
1380 +  {
1381 +    for (i = 0; i < num_files; i ++)
1382 +      unlink(files[i]);
1383 +  }
1384 +#endif /* HAVE_LIBZ */
1385 +
1386 +#ifdef __APPLE__
1387 +  if (pstmpname[0])
1388 +    unlink(pstmpname);
1389 +#endif /* __APPLE__ */
1390 +
1391 + /*
1392 +  * Return the queue status...
1393 +  */
1394 +
1395 +  fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1396 +
1397 +  if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1398 +    return (CUPS_BACKEND_AUTH_REQUIRED);
1399 +  else if (ipp_status == IPP_INTERNAL_ERROR)
1400 +    return (CUPS_BACKEND_STOP);
1401 +  else if (ipp_status > IPP_OK_CONFLICT)
1402 +    return (CUPS_BACKEND_FAILED);
1403 +  else
1404 +  {
1405 +    _cupsLangPuts(stderr, _("INFO: Ready to print.\n"));
1406 +    return (CUPS_BACKEND_OK);
1407 +  }
1408 +}
1409 +
1410 +
1411 +/*
1412 + * 'cancel_job()' - Cancel a print job.
1413 + */
1414 +
1415 +static void
1416 +cancel_job(http_t     *http,           /* I - HTTP connection */
1417 +           const char *uri,            /* I - printer-uri */
1418 +          int        id,               /* I - job-id */
1419 +          const char *resource,        /* I - Resource path */
1420 +          const char *user,            /* I - requesting-user-name */
1421 +          int        version)          /* I - IPP version */
1422 +{
1423 +  ipp_t        *request;                       /* Cancel-Job request */
1424 +
1425 +
1426 +  _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
1427 +
1428 +  request = ippNewRequest(IPP_CANCEL_JOB);
1429 +  request->request.op.version[0] = version / 10;
1430 +  request->request.op.version[1] = version % 10;
1431 +
1432 +  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1433 +               NULL, uri);
1434 +  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1435 +
1436 +  if (user && user[0])
1437 +    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1438 +                 "requesting-user-name", NULL, user);
1439 +
1440 + /*
1441 +  * Do the request...
1442 +  */
1443 +
1444 +  if (http->version < HTTP_1_1)
1445 +    httpReconnect(http);
1446 +
1447 +  ippDelete(cupsDoRequest(http, request, resource));
1448 +
1449 +  if (cupsLastError() > IPP_OK_CONFLICT)
1450 +    _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1451 +                   cupsLastErrorString());
1452 +}
1453 +
1454 +
1455 +/*
1456 + * 'check_printer_state()' - Check the printer state...
1457 + */
1458 +
1459 +static void
1460 +check_printer_state(
1461 +    http_t      *http,                 /* I - HTTP connection */
1462 +    const char  *uri,                  /* I - Printer URI */
1463 +    const char  *resource,             /* I - Resource path */
1464 +    const char  *user,                 /* I - Username, if any */
1465 +    int         version,               /* I - IPP version */
1466 +    int         job_id)                        /* I - Current job ID */
1467 +{
1468 +  ipp_t        *request,                       /* IPP request */
1469 +       *response;                      /* IPP response */
1470 +  static const char * const attrs[] =  /* Attributes we want */
1471 +  {
1472 +    "com.apple.print.recoverable-message",
1473 +    "marker-colors",
1474 +    "marker-levels",
1475 +    "marker-message",
1476 +    "marker-names",
1477 +    "marker-types",
1478 +    "printer-state-message",
1479 +    "printer-state-reasons"
1480 +  };
1481 +
1482 +
1483 + /*
1484 +  * Check on the printer state...
1485 +  */
1486 +
1487 +  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1488 +  request->request.op.version[0] = version / 10;
1489 +  request->request.op.version[1] = version % 10;
1490 +
1491 +  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1492 +               NULL, uri);
1493 +
1494 +  if (user && user[0])
1495 +    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1496 +                 "requesting-user-name", NULL, user);
1497 +
1498 +  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1499 +                "requested-attributes",
1500 +               (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
1501 +
1502 + /*
1503 +  * Do the request...
1504 +  */
1505 +
1506 +  if (http->version < HTTP_1_1)
1507 +    httpReconnect(http);
1508 +
1509 +  if ((response = cupsDoRequest(http, request, resource)) != NULL)
1510 +  {
1511 +    report_printer_state(response, job_id);
1512 +    ippDelete(response);
1513 +  }
1514 +}
1515 +
1516 +
1517 +#ifdef HAVE_LIBZ
1518 +/*
1519 + * 'compress_files()' - Compress print files...
1520 + */
1521 +
1522 +static void
1523 +compress_files(int  num_files,         /* I - Number of files */
1524 +               char **files)           /* I - Files */
1525 +{
1526 +  int          i,                      /* Looping var */
1527 +               fd;                     /* Temporary file descriptor */
1528 +  ssize_t      bytes;                  /* Bytes read/written */
1529 +  size_t       total;                  /* Total bytes read */
1530 +  cups_file_t  *in,                    /* Input file */
1531 +               *out;                   /* Output file */
1532 +  struct stat  outinfo;                /* Output file information */
1533 +  char         filename[1024],         /* Temporary filename */
1534 +               buffer[32768];          /* Copy buffer */
1535 +
1536 +
1537 +  fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1538 +  for (i = 0; i < num_files; i ++)
1539 +  {
1540 +    if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1541 +    {
1542 +      _cupsLangPrintf(stderr,
1543 +                     _("ERROR: Unable to create temporary compressed print "
1544 +                       "file: %s\n"), strerror(errno));
1545 +      exit(CUPS_BACKEND_FAILED);
1546 +    }
1547 +
1548 +    if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1549 +    {
1550 +      _cupsLangPrintf(stderr,
1551 +                     _("ERROR: Unable to open temporary compressed print "
1552 +                       "file: %s\n"), strerror(errno));
1553 +      exit(CUPS_BACKEND_FAILED);
1554 +    }
1555 +
1556 +    if ((in = cupsFileOpen(files[i], "r")) == NULL)
1557 +    {
1558 +      _cupsLangPrintf(stderr,
1559 +                      _("ERROR: Unable to open print file \"%s\": %s\n"),
1560 +                     files[i], strerror(errno));
1561 +      cupsFileClose(out);
1562 +      exit(CUPS_BACKEND_FAILED);
1563 +    }
1564 +
1565 +    total = 0;
1566 +    while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1567 +      if (cupsFileWrite(out, buffer, bytes) < bytes)
1568 +      {
1569 +        _cupsLangPrintf(stderr,
1570 +                       _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1571 +                       (int)bytes, filename, strerror(errno));
1572 +        cupsFileClose(in);
1573 +        cupsFileClose(out);
1574 +       exit(CUPS_BACKEND_FAILED);
1575 +      }
1576 +      else
1577 +        total += bytes;
1578 +
1579 +    cupsFileClose(out);
1580 +    cupsFileClose(in);
1581 +
1582 +    files[i] = strdup(filename);
1583 +
1584 +    if (!stat(filename, &outinfo))
1585 +      fprintf(stderr,
1586 +              "DEBUG: File %d compressed to %.1f%% of original size, "
1587 +             CUPS_LLFMT " bytes...\n",
1588 +              i + 1, 100.0 * outinfo.st_size / total,
1589 +             CUPS_LLCAST outinfo.st_size);
1590 +  }
1591 +}
1592 +#endif /* HAVE_LIBZ */
1593 +
1594 +
1595 +/*
1596 + * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1597 + */
1598 +
1599 +static const char *                    /* O - Password  */
1600 +password_cb(const char *prompt)                /* I - Prompt (not used) */
1601 +{
1602 +  (void)prompt;
1603 +
1604 + /*
1605 +  * Remember that we need to authenticate...
1606 +  */
1607 +
1608 +  auth_info_required = "username,password";
1609 +
1610 +  if (password && *password && password_tries < 3)
1611 +  {
1612 +    password_tries ++;
1613 +
1614 +    return (password);
1615 +  }
1616 +  else
1617 +  {
1618 +   /*
1619 +    * Give up after 3 tries or if we don't have a password to begin with...
1620 +    */
1621 +
1622 +    return (NULL);
1623 +  }
1624 +}
1625 +
1626 +
1627 +/*
1628 + * 'report_attr()' - Report an IPP attribute value.
1629 + */
1630 +
1631 +static void
1632 +report_attr(ipp_attribute_t *attr)     /* I - Attribute */
1633 +{
1634 +  int  i;                              /* Looping var */
1635 +  char value[1024],                    /* Value string */
1636 +       *valptr,                        /* Pointer into value string */
1637 +       *attrptr;                       /* Pointer into attribute value */
1638 +
1639 +
1640 + /*
1641 +  * Convert the attribute values into quoted strings...
1642 +  */
1643 +
1644 +  for (i = 0, valptr = value;
1645 +       i < attr->num_values && valptr < (value + sizeof(value) - 10);
1646 +       i ++)
1647 +  {
1648 +    if (i > 0)
1649 +      *valptr++ = ',';
1650 +
1651 +    switch (attr->value_tag)
1652 +    {
1653 +      case IPP_TAG_INTEGER :
1654 +      case IPP_TAG_ENUM :
1655 +          snprintf(valptr, sizeof(value) - (valptr - value), "%d",
1656 +                  attr->values[i].integer);
1657 +         valptr += strlen(valptr);
1658 +         break;
1659 +
1660 +      case IPP_TAG_TEXT :
1661 +      case IPP_TAG_NAME :
1662 +      case IPP_TAG_KEYWORD :
1663 +          *valptr++ = '\"';
1664 +         for (attrptr = attr->values[i].string.text;
1665 +              *attrptr && valptr < (value + sizeof(value) - 10);
1666 +              attrptr ++)
1667 +         {
1668 +           if (*attrptr == '\\' || *attrptr == '\"')
1669 +             *valptr++ = '\\';
1670 +
1671 +           *valptr++ = *attrptr;
1672 +         }
1673 +          *valptr++ = '\"';
1674 +          break;
1675 +
1676 +      default :
1677 +         /*
1678 +         * Unsupported value type...
1679 +         */
1680 +
1681 +          return;
1682 +    }
1683 +  }
1684 +
1685 +  *valptr = '\0';
1686 +
1687 + /*
1688 +  * Tell the scheduler about the new values...
1689 +  */
1690 +
1691 +  fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
1692 +}
1693 +
1694 +
1695 +/*
1696 + * 'report_printer_state()' - Report the printer state.
1697 + */
1698 +
1699 +static int                             /* O - Number of reasons shown */
1700 +report_printer_state(ipp_t *ipp,       /* I - IPP response */
1701 +                     int   job_id)     /* I - Current job ID */
1702 +{
1703 +  int                  i;              /* Looping var */
1704 +  int                  count;          /* Count of reasons shown... */
1705 +  ipp_attribute_t      *caprm,         /* com.apple.print.recoverable-message */
1706 +                       *psm,           /* printer-state-message */
1707 +                       *reasons,       /* printer-state-reasons */
1708 +                       *marker;        /* marker-* attributes */
1709 +  const char           *reason;        /* Current reason */
1710 +  const char           *prefix;        /* Prefix for STATE: line */
1711 +  char                 state[1024];    /* State string */
1712 +  int                  saw_caprw;      /* Saw com.apple.print.recoverable-warning state */
1713 +
1714 +
1715 +  if ((psm = ippFindAttribute(ipp, "printer-state-message",
1716 +                              IPP_TAG_TEXT)) != NULL)
1717 +    fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1718 +
1719 +  if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1720 +                                  IPP_TAG_KEYWORD)) == NULL)
1721 +    return (0);
1722 +
1723 +  saw_caprw = 0;
1724 +  state[0]  = '\0';
1725 +  prefix    = "STATE: ";
1726 +
1727 +  for (i = 0, count = 0; i < reasons->num_values; i ++)
1728 +  {
1729 +    reason = reasons->values[i].string.text;
1730 +
1731 +    if (!strcmp(reason, "com.apple.print.recoverable-warning"))
1732 +      saw_caprw = 1;
1733 +    else if (strcmp(reason, "paused"))
1734 +    {
1735 +      strlcat(state, prefix, sizeof(state));
1736 +      strlcat(state, reason, sizeof(state));
1737 +
1738 +      prefix  = ",";
1739 +    }
1740 +  }
1741 +
1742 +  if (state[0])
1743 +    fprintf(stderr, "%s\n", state);
1744 +
1745 + /*
1746 +  * Relay com.apple.print.recoverable-message...
1747 +  */
1748 +
1749 +  if ((caprm = ippFindAttribute(ipp, "com.apple.print.recoverable-message",
1750 +                                IPP_TAG_TEXT)) != NULL)
1751 +    fprintf(stderr, "WARNING: %s: %s\n",
1752 +            saw_caprw ? "recoverable" : "recovered",
1753 +           caprm->values[0].string.text);
1754 +
1755 + /*
1756 +  * Relay the current marker-* attribute values...
1757 +  */
1758 +
1759 +  if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
1760 +    report_attr(marker);
1761 +  if ((marker = ippFindAttribute(ipp, "marker-high-levels",
1762 +                                 IPP_TAG_INTEGER)) != NULL)
1763 +    report_attr(marker);
1764 +  if ((marker = ippFindAttribute(ipp, "marker-levels",
1765 +                                 IPP_TAG_INTEGER)) != NULL)
1766 +    report_attr(marker);
1767 +  if ((marker = ippFindAttribute(ipp, "marker-low-levels",
1768 +                                 IPP_TAG_INTEGER)) != NULL)
1769 +    report_attr(marker);
1770 +  if ((marker = ippFindAttribute(ipp, "marker-message", IPP_TAG_TEXT)) != NULL)
1771 +    report_attr(marker);
1772 +  if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
1773 +    report_attr(marker);
1774 +  if ((marker = ippFindAttribute(ipp, "marker-types", IPP_TAG_KEYWORD)) != NULL)
1775 +    report_attr(marker);
1776 +
1777 +  return (count);
1778 +}
1779 +
1780 +
1781 +#ifdef __APPLE__
1782 +/*
1783 + * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1784 + *                          remotely.
1785 + *
1786 + * This step is required because the PICT format is not documented and
1787 + * subject to change, so developing a filter for other OS's is infeasible.
1788 + * Also, fonts required by the PICT file need to be embedded on the
1789 + * client side (which has the fonts), so we run the filter to get a
1790 + * PostScript file for printing...
1791 + */
1792 +
1793 +static int                             /* O - Exit status of filter */
1794 +run_pictwps_filter(char       **argv,  /* I - Command-line arguments */
1795 +                   const char *filename)/* I - Filename */
1796 +{
1797 +  struct stat  fileinfo;               /* Print file information */
1798 +  const char   *ppdfile;               /* PPD file for destination printer */
1799 +  int          pid;                    /* Child process ID */
1800 +  int          fd;                     /* Temporary file descriptor */
1801 +  int          status;                 /* Exit status of filter */
1802 +  const char   *printer;               /* PRINTER env var */
1803 +  static char  ppdenv[1024];           /* PPD environment variable */
1804 +
1805 +
1806 + /*
1807 +  * First get the PPD file for the printer...
1808 +  */
1809 +
1810 +  printer = getenv("PRINTER");
1811 +  if (!printer)
1812 +  {
1813 +    _cupsLangPuts(stderr,
1814 +                  _("ERROR: PRINTER environment variable not defined!\n"));
1815 +    return (-1);
1816 +  }
1817 +
1818 +  if ((ppdfile = cupsGetPPD(printer)) == NULL)
1819 +  {
1820 +    _cupsLangPrintf(stderr,
1821 +                   _("ERROR: Unable to get PPD file for printer \"%s\" - "
1822 +                     "%s.\n"), printer, cupsLastErrorString());
1823 +  }
1824 +  else
1825 +  {
1826 +    snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1827 +    putenv(ppdenv);
1828 +  }
1829 +
1830 + /*
1831 +  * Then create a temporary file for printing...
1832 +  */
1833 +
1834 +  if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
1835 +  {
1836 +    _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
1837 +    if (ppdfile)
1838 +      unlink(ppdfile);
1839 +    return (-1);
1840 +  }
1841 +
1842 + /*
1843 +  * Get the owner of the spool file - it is owned by the user we want to run
1844 +  * as...
1845 +  */
1846 +
1847 +  if (argv[6])
1848 +    stat(argv[6], &fileinfo);
1849 +  else
1850 +  {
1851 +   /*
1852 +    * Use the OSX defaults, as an up-stream filter created the PICT
1853 +    * file...
1854 +    */
1855 +
1856 +    fileinfo.st_uid = 1;
1857 +    fileinfo.st_gid = 80;
1858 +  }
1859 +
1860 +  if (ppdfile)
1861 +    chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1862 +
1863 +  fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1864 +
1865 + /*
1866 +  * Finally, run the filter to convert the file...
1867 +  */
1868 +
1869 +  if ((pid = fork()) == 0)
1870 +  {
1871 +   /*
1872 +    * Child process for pictwpstops...  Redirect output of pictwpstops to a
1873 +    * file...
1874 +    */
1875 +
1876 +    dup2(fd, 1);
1877 +    close(fd);
1878 +
1879 +    if (!getuid())
1880 +    {
1881 +     /*
1882 +      * Change to an unpriviledged user...
1883 +      */
1884 +
1885 +      if (setgid(fileinfo.st_gid))
1886 +        return (errno);
1887 +
1888 +      if (setuid(fileinfo.st_uid))
1889 +        return (errno);
1890 +    }
1891 +
1892 +    execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1893 +           filename, NULL);
1894 +    _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1895 +                   strerror(errno));
1896 +    return (errno);
1897 +  }
1898 +
1899 +  close(fd);
1900 +
1901 +  if (pid < 0)
1902 +  {
1903 +   /*
1904 +    * Error!
1905 +    */
1906 +
1907 +    _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1908 +                   strerror(errno));
1909 +    if (ppdfile)
1910 +      unlink(ppdfile);
1911 +    return (-1);
1912 +  }
1913 +
1914 + /*
1915 +  * Now wait for the filter to complete...
1916 +  */
1917 +
1918 +  if (wait(&status) < 0)
1919 +  {
1920 +    _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1921 +                   strerror(errno));
1922 +    close(fd);
1923 +    if (ppdfile)
1924 +      unlink(ppdfile);
1925 +    return (-1);
1926 +  }
1927 +
1928 +  if (ppdfile)
1929 +    unlink(ppdfile);
1930 +
1931 +  close(fd);
1932 +
1933 +  if (status)
1934 +  {
1935 +    if (status >= 256)
1936 +      _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1937 +                     status / 256);
1938 +    else
1939 +      _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1940 +                     status);
1941 +
1942 +    return (status);
1943 +  }
1944 +
1945 + /*
1946 +  * Return with no errors..
1947 +  */
1948 +
1949 +  return (0);
1950 +}
1951 +#endif /* __APPLE__ */
1952 +
1953 +
1954 +/*
1955 + * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1956 + */
1957 +
1958 +static void
1959 +sigterm_handler(int sig)               /* I - Signal */
1960 +{
1961 +  (void)sig;   /* remove compiler warnings... */
1962 +
1963 +  if (!job_cancelled)
1964 +  {
1965 +   /*
1966 +    * Flag that the job should be cancelled...
1967 +    */
1968 +
1969 +    job_cancelled = 1;
1970 +    return;
1971 +  }
1972 +
1973 + /*
1974 +  * The scheduler already tried to cancel us once, now just terminate
1975 +  * after removing our temp files!
1976 +  */
1977 +
1978 +  if (tmpfilename[0])
1979 +    unlink(tmpfilename);
1980 +
1981 +#ifdef __APPLE__
1982 +  if (pstmpname[0])
1983 +    unlink(pstmpname);
1984 +#endif /* __APPLE__ */
1985 +
1986 +  exit(1);
1987 +}
1988 +
1989 +
1990 +/*
1991 + * End of "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $".
1992 + */
This page took 0.36461 seconds and 3 git commands to generate.