[packages/cups] - rel 2; add ipp14 backend; fixes for ipp15 backend; other fixes - everything from debian
arekm
arekm at pld-linux.org
Mon Jul 9 18:54:55 CEST 2012
commit ba7599c972fac2d950e2236db7e13b5d4ecc1b45
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Mon Jul 9 18:54:36 2012 +0200
- rel 2; add ipp14 backend; fixes for ipp15 backend; other fixes - everything from debian
add-ipp-backend-of-cups-1.4.patch | 1991 ++++++++++++++++++++
cups.spec | 14 +-
ipp-backend-cups-1.5.4-fixes.patch | 187 ++
reactivate_recommended_driver.patch | 29 +
...-incoming-postscript-and-add-to-ipp-attrs.patch | 102 +
5 files changed, 2318 insertions(+), 5 deletions(-)
---
diff --git a/add-ipp-backend-of-cups-1.4.patch b/add-ipp-backend-of-cups-1.4.patch
new file mode 100644
index 0000000..2ffe20e
--- /dev/null
+++ b/add-ipp-backend-of-cups-1.4.patch
@@ -0,0 +1,1991 @@
+--- a/backend/Makefile
++++ b/backend/Makefile
+@@ -21,12 +21,12 @@
+ # Object files...
+ #
+
+-RBACKENDS = ipp lpd $(DNSSD_BACKEND)
++RBACKENDS = ipp ipp14 lpd $(DNSSD_BACKEND)
+ UBACKENDS = $(LEGACY_BACKENDS) serial snmp socket usb
+ UNITTESTS = test1284 testbackend testsupplies
+ TARGETS = libbackend.a $(RBACKENDS) $(UBACKENDS)
+ LIBOBJS = ieee1284.o network.o runloop.o snmp-supplies.o
+-OBJS = ipp.o lpd.o dnssd.o parallel.o serial.o snmp.o \
++OBJS = ipp.o ipp14.o lpd.o dnssd.o parallel.o serial.o snmp.o \
+ socket.o test1284.o testbackend.o testsupplies.o usb.o
+
+
+@@ -218,6 +218,17 @@
+
+
+ #
++# ipp14
++#
++
++ipp14: ipp14.o ../cups/$(LIBCUPS) libbackend.a
++ echo Linking $@...
++ $(CC) $(LDFLAGS) -o ipp14 ipp14.o libbackend.a $(LIBS)
++ #$(RM) http
++ #$(LN) ipp14 http
++
++
++#
+ # lpd
+ #
+
+--- /dev/null
++++ b/backend/ipp14.c
+@@ -0,0 +1,1953 @@
++/*
++ * "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $"
++ *
++ * IPP backend for the Common UNIX Printing System (CUPS).
++ *
++ * Copyright 2007-2010 by Apple Inc.
++ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
++ *
++ * These coded instructions, statements, and computer programs are the
++ * property of Apple Inc. and are protected by Federal copyright
++ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
++ * "LICENSE" which should have been included with this file. If this
++ * file is missing or damaged, see the license at "http://www.cups.org/".
++ *
++ * This file is subject to the Apple OS-Developed Software exception.
++ *
++ * Contents:
++ *
++ * main() - Send a file to the printer or server.
++ * cancel_job() - Cancel a print job.
++ * check_printer_state() - Check the printer state...
++ * compress_files() - Compress print files...
++ * password_cb() - Disable the password prompt for
++ * cupsDoFileRequest().
++ * report_attr() - Report an IPP attribute value.
++ * report_printer_state() - Report the printer state.
++ * run_pictwps_filter() - Convert PICT files to PostScript when printing
++ * remotely.
++ * sigterm_handler() - Handle 'terminate' signals that stop the backend.
++ */
++
++/*
++ * Include necessary headers.
++ */
++
++#include <cups/http-private.h>
++#include "backend-private.h"
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/wait.h>
++
++/*
++ * Globals...
++ */
++
++static char *password = NULL; /* Password for device URI */
++static int password_tries = 0; /* Password tries */
++static const char *auth_info_required = "none";
++ /* New auth-info-required value */
++#ifdef __APPLE__
++static char pstmpname[1024] = ""; /* Temporary PostScript file name */
++#endif /* __APPLE__ */
++static char tmpfilename[1024] = ""; /* Temporary spool file name */
++static int job_cancelled = 0; /* Job cancelled? */
++
++
++/*
++ * Local functions...
++ */
++
++static void cancel_job(http_t *http, const char *uri, int id,
++ const char *resource, const char *user, int version);
++static void check_printer_state(http_t *http, const char *uri,
++ const char *resource, const char *user,
++ int version, int job_id);
++#ifdef HAVE_LIBZ
++static void compress_files(int num_files, char **files);
++#endif /* HAVE_LIBZ */
++static const char *password_cb(const char *);
++static void report_attr(ipp_attribute_t *attr);
++static int report_printer_state(ipp_t *ipp, int job_id);
++
++#ifdef __APPLE__
++static int run_pictwps_filter(char **argv, const char *filename);
++#endif /* __APPLE__ */
++static void sigterm_handler(int sig);
++
++
++/*
++ * 'main()' - Send a file to the printer or server.
++ *
++ * Usage:
++ *
++ * printer-uri job-id user title copies options [file]
++ */
++
++int /* O - Exit status */
++main(int argc, /* I - Number of command-line args */
++ char *argv[]) /* I - Command-line arguments */
++{
++ int i; /* Looping var */
++ int send_options; /* Send job options? */
++ int num_options; /* Number of printer options */
++ cups_option_t *options; /* Printer options */
++ const char *device_uri; /* Device URI */
++ char scheme[255], /* Scheme in URI */
++ hostname[1024], /* Hostname */
++ username[255], /* Username info */
++ resource[1024], /* Resource info (printer name) */
++ addrname[256], /* Address name */
++ *optptr, /* Pointer to URI options */
++ *name, /* Name of option */
++ *value, /* Value of option */
++ sep; /* Separator character */
++ int snmp_fd, /* SNMP socket */
++ start_count, /* Page count via SNMP at start */
++ page_count, /* Page count via SNMP */
++ have_supplies; /* Printer supports supply levels? */
++ int num_files; /* Number of files to print */
++ char **files, /* Files to print */
++ *filename; /* Pointer to single filename */
++ int port; /* Port number (not used) */
++ char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
++ ipp_status_t ipp_status; /* Status of IPP request */
++ http_t *http; /* HTTP connection */
++ ipp_t *request, /* IPP request */
++ *response, /* IPP response */
++ *supported; /* get-printer-attributes response */
++ time_t start_time; /* Time of first connect */
++ int recoverable; /* Recoverable error shown? */
++ int contimeout; /* Connection timeout */
++ int delay; /* Delay for retries... */
++ int compression, /* Do compression of the job data? */
++ waitjob, /* Wait for job complete? */
++ waitprinter; /* Wait for printer ready? */
++ ipp_attribute_t *job_id_attr; /* job-id attribute */
++ int job_id; /* job-id value */
++ ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
++ ipp_attribute_t *job_state; /* job-state */
++ ipp_attribute_t *copies_sup; /* copies-supported */
++ ipp_attribute_t *format_sup; /* document-format-supported */
++ ipp_attribute_t *printer_state; /* printer-state attribute */
++ ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
++ int copies, /* Number of copies for job */
++ copies_remaining; /* Number of copies remaining */
++ const char *content_type, /* CONTENT_TYPE environment variable */
++ *final_content_type; /* FINAL_CONTENT_TYPE environment var */
++#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
++ struct sigaction action; /* Actions for POSIX signals */
++#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
++ int version; /* IPP version */
++ static const char * const pattrs[] =
++ { /* Printer attributes we want */
++ "com.apple.print.recoverable-message",
++ "copies-supported",
++ "document-format-supported",
++ "marker-colors",
++ "marker-high-levels",
++ "marker-levels",
++ "marker-low-levels",
++ "marker-message",
++ "marker-names",
++ "marker-types",
++ "printer-is-accepting-jobs",
++ "printer-state",
++ "printer-state-message",
++ "printer-state-reasons",
++ };
++ static const char * const jattrs[] =
++ { /* Job attributes we want */
++ "job-media-sheets-completed",
++ "job-state"
++ };
++
++
++ /*
++ * Make sure status messages are not buffered...
++ */
++
++ setbuf(stderr, NULL);
++
++ /*
++ * Ignore SIGPIPE and catch SIGTERM signals...
++ */
++
++#ifdef HAVE_SIGSET
++ sigset(SIGPIPE, SIG_IGN);
++ sigset(SIGTERM, sigterm_handler);
++#elif defined(HAVE_SIGACTION)
++ memset(&action, 0, sizeof(action));
++ action.sa_handler = SIG_IGN;
++ sigaction(SIGPIPE, &action, NULL);
++
++ sigemptyset(&action.sa_mask);
++ sigaddset(&action.sa_mask, SIGTERM);
++ action.sa_handler = sigterm_handler;
++ sigaction(SIGTERM, &action, NULL);
++#else
++ signal(SIGPIPE, SIG_IGN);
++ signal(SIGTERM, sigterm_handler);
++#endif /* HAVE_SIGSET */
++
++ /*
++ * Check command-line...
++ */
++
++ if (argc == 1)
++ {
++ char *s;
++
++ if ((s = strrchr(argv[0], '/')) != NULL)
++ s ++;
++ else
++ s = argv[0];
++
++ printf("network %s \"Unknown\" \"%s (%s)\"\n",
++ s, _cupsLangString(cupsLangDefault(),
++ _("Internet Printing Protocol")), s);
++ return (CUPS_BACKEND_OK);
++ }
++ else if (argc < 6)
++ {
++ _cupsLangPrintf(stderr,
++ _("Usage: %s job-id user title copies options [file]\n"),
++ argv[0]);
++ return (CUPS_BACKEND_STOP);
++ }
++
++ /*
++ * Get the (final) content type...
++ */
++
++ if ((content_type = getenv("CONTENT_TYPE")) == NULL)
++ content_type = "application/octet-stream";
++
++ if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
++ {
++ final_content_type = content_type;
++
++ if (!strncmp(final_content_type, "printer/", 8))
++ final_content_type = "application/vnd.cups-raw";
++ }
++
++ /*
++ * Extract the hostname and printer name from the URI...
++ */
++
++ if ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
++ return (CUPS_BACKEND_FAILED);
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
++ username, sizeof(username), hostname, sizeof(hostname), &port,
++ resource, sizeof(resource));
++
++ if (!port)
++ port = IPP_PORT; /* Default to port 631 */
++
++ if (!strcmp(scheme, "https"))
++ cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
++ else
++ cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
++
++ /*
++ * See if there are any options...
++ */
++
++ compression = 0;
++ version = 11;
++ waitjob = 1;
++ waitprinter = 1;
++ contimeout = 7 * 24 * 60 * 60;
++
++ if ((optptr = strchr(resource, '?')) != NULL)
++ {
++ /*
++ * Yup, terminate the device name string and move to the first
++ * character of the optptr...
++ */
++
++ *optptr++ = '\0';
++
++ /*
++ * Then parse the optptr...
++ */
++
++ while (*optptr)
++ {
++ /*
++ * Get the name...
++ */
++
++ name = optptr;
++
++ while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
++ optptr ++;
++
++ if ((sep = *optptr) != '\0')
++ *optptr++ = '\0';
++
++ if (sep == '=')
++ {
++ /*
++ * Get the value...
++ */
++
++ value = optptr;
++
++ while (*optptr && *optptr != '+' && *optptr != '&')
++ optptr ++;
++
++ if (*optptr)
++ *optptr++ = '\0';
++ }
++ else
++ value = (char *)"";
++
++ /*
++ * Process the option...
++ */
++
++ if (!strcasecmp(name, "waitjob"))
++ {
++ /*
++ * Wait for job completion?
++ */
++
++ waitjob = !strcasecmp(value, "on") ||
++ !strcasecmp(value, "yes") ||
++ !strcasecmp(value, "true");
++ }
++ else if (!strcasecmp(name, "waitprinter"))
++ {
++ /*
++ * Wait for printer idle?
++ */
++
++ waitprinter = !strcasecmp(value, "on") ||
++ !strcasecmp(value, "yes") ||
++ !strcasecmp(value, "true");
++ }
++ else if (!strcasecmp(name, "encryption"))
++ {
++ /*
++ * Enable/disable encryption?
++ */
++
++ if (!strcasecmp(value, "always"))
++ cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
++ else if (!strcasecmp(value, "required"))
++ cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
++ else if (!strcasecmp(value, "never"))
++ cupsSetEncryption(HTTP_ENCRYPT_NEVER);
++ else if (!strcasecmp(value, "ifrequested"))
++ cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
++ else
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unknown encryption option value \"%s\"!\n"),
++ value);
++ }
++ }
++ else if (!strcasecmp(name, "version"))
++ {
++ if (!strcmp(value, "1.0"))
++ version = 10;
++ else if (!strcmp(value, "1.1"))
++ version = 11;
++ else if (!strcmp(value, "2.0"))
++ version = 20;
++ else if (!strcmp(value, "2.1"))
++ version = 21;
++ else
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unknown version option value \"%s\"!\n"),
++ value);
++ }
++ }
++#ifdef HAVE_LIBZ
++ else if (!strcasecmp(name, "compression"))
++ {
++ compression = !strcasecmp(value, "true") ||
++ !strcasecmp(value, "yes") ||
++ !strcasecmp(value, "on") ||
++ !strcasecmp(value, "gzip");
++ }
++#endif /* HAVE_LIBZ */
++ else if (!strcasecmp(name, "contimeout"))
++ {
++ /*
++ * Set the connection timeout...
++ */
++
++ if (atoi(value) > 0)
++ contimeout = atoi(value);
++ }
++ else
++ {
++ /*
++ * Unknown option...
++ */
++
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
++ name, value);
++ }
++ }
++ }
++
++ /*
++ * If we have 7 arguments, print the file named on the command-line.
++ * Otherwise, copy stdin to a temporary file and print the temporary
++ * file.
++ */
++
++ if (argc == 6)
++ {
++ /*
++ * Copy stdin to a temporary file...
++ */
++
++ int fd; /* File descriptor */
++ http_addrlist_t *addrlist; /* Address list */
++ off_t tbytes; /* Total bytes copied */
++
++
++ fputs("STATE: +connecting-to-device\n", stderr);
++ fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
++
++ if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, "1")) == NULL)
++ {
++ _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
++ hostname);
++ return (CUPS_BACKEND_STOP);
++ }
++
++ snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
++
++ if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
++ {
++ _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ _cupsLangPuts(stderr, _("INFO: Copying print data...\n"));
++
++ tbytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
++ backendNetworkSideCB);
++
++ if (snmp_fd >= 0)
++ _cupsSNMPClose(snmp_fd);
++
++ httpAddrFreeList(addrlist);
++
++ close(fd);
++
++ /*
++ * Don't try printing files less than 2 bytes...
++ */
++
++ if (tbytes <= 1)
++ {
++ _cupsLangPuts(stderr, _("ERROR: Empty print file!\n"));
++ unlink(tmpfilename);
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ /*
++ * Point to the single file from stdin...
++ */
++
++ filename = tmpfilename;
++ num_files = 1;
++ files = &filename;
++ send_options = 0;
++ }
++ else
++ {
++ /*
++ * Point to the files on the command-line...
++ */
++
++ num_files = argc - 6;
++ files = argv + 6;
++ send_options = 1;
++
++#ifdef HAVE_LIBZ
++ if (compression)
++ compress_files(num_files, files);
++#endif /* HAVE_LIBZ */
++ }
++
++ fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
++
++ /*
++ * Set the authentication info, if any...
++ */
++
++ cupsSetPasswordCB(password_cb);
++
++ if (username[0])
++ {
++ /*
++ * Use authenticaion information in the device URI...
++ */
++
++ if ((password = strchr(username, ':')) != NULL)
++ *password++ = '\0';
++
++ cupsSetUser(username);
++ }
++ else if (!getuid())
++ {
++ /*
++ * Try loading authentication information from the environment.
++ */
++
++ const char *ptr = getenv("AUTH_USERNAME");
++
++ if (ptr)
++ cupsSetUser(ptr);
++
++ password = getenv("AUTH_PASSWORD");
++ }
++
++ /*
++ * Try connecting to the remote server...
++ */
++
++ delay = 5;
++ recoverable = 0;
++ start_time = time(NULL);
++
++ fputs("STATE: +connecting-to-device\n", stderr);
++
++ do
++ {
++ fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
++ _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
++
++ if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
++ {
++ if (job_cancelled)
++ break;
++
++ if (getenv("CLASS") != NULL)
++ {
++ /*
++ * If the CLASS environment variable is set, the job was submitted
++ * to a class and not to a specific queue. In this case, we want
++ * to abort immediately so that the job can be requeued on the next
++ * available printer in the class.
++ */
++
++ _cupsLangPuts(stderr,
++ _("INFO: Unable to contact printer, queuing on next "
++ "printer in class...\n"));
++
++ if (tmpfilename[0])
++ unlink(tmpfilename);
++
++ /*
++ * Sleep 5 seconds to keep the job from requeuing too rapidly...
++ */
++
++ sleep(5);
++
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
++ errno == EHOSTUNREACH)
++ {
++ if (contimeout && (time(NULL) - start_time) > contimeout)
++ {
++ _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ recoverable = 1;
++
++ _cupsLangPrintf(stderr,
++ _("WARNING: recoverable: Network host \'%s\' is busy; "
++ "will retry in %d seconds...\n"),
++ hostname, delay);
++
++ sleep(delay);
++
++ if (delay < 30)
++ delay += 5;
++ }
++ else if (h_errno)
++ {
++ _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
++ hostname);
++ return (CUPS_BACKEND_STOP);
++ }
++ else
++ {
++ recoverable = 1;
++
++ fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
++ _cupsLangPuts(stderr,
++ _("ERROR: recoverable: Unable to connect to printer; will "
++ "retry in 30 seconds...\n"));
++ sleep(30);
++ }
++
++ if (job_cancelled)
++ break;
++ }
++ }
++ while (http == NULL);
++
++ if (job_cancelled || !http)
++ {
++ if (tmpfilename[0])
++ unlink(tmpfilename);
++
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ fputs("STATE: -connecting-to-device\n", stderr);
++ _cupsLangPuts(stderr, _("INFO: Connected to printer...\n"));
++
++#ifdef AF_INET6
++ if (http->hostaddr->addr.sa_family == AF_INET6)
++ fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
++ httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
++ ntohs(http->hostaddr->ipv6.sin6_port));
++ else
++#endif /* AF_INET6 */
++ if (http->hostaddr->addr.sa_family == AF_INET)
++ fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
++ httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
++ ntohs(http->hostaddr->ipv4.sin_port));
++
++ /*
++ * See if the printer supports SNMP...
++ */
++
++ if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0)
++ have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count,
++ NULL);
++ else
++ have_supplies = start_count = 0;
++
++ /*
++ * Build a URI for the printer and fill the standard IPP attributes for
++ * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
++ * might contain username:password information...
++ */
++
++ httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
++ port, resource);
++
++ /*
++ * First validate the destination and see if the device supports multiple
++ * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
++ * don't support the copies attribute...
++ */
++
++ copies_sup = NULL;
++ format_sup = NULL;
++ supported = NULL;
++
++ do
++ {
++ /*
++ * Check for side-channel requests...
++ */
++
++ backendCheckSideChannel(snmp_fd, http->hostaddr);
++
++ /*
++ * Build the IPP request...
++ */
++
++ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
++ request->request.op.version[0] = version / 10;
++ request->request.op.version[1] = version % 10;
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
++ NULL, uri);
++
++ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
++ "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
++ NULL, pattrs);
++
++ /*
++ * Do the request...
++ */
++
++ fputs("DEBUG: Getting supported attributes...\n", stderr);
++
++ if (http->version < HTTP_1_1)
++ httpReconnect(http);
++
++ if ((supported = cupsDoRequest(http, request, resource)) == NULL)
++ ipp_status = cupsLastError();
++ else
++ ipp_status = supported->request.status.status_code;
++
++ if (ipp_status > IPP_OK_CONFLICT)
++ {
++ if (ipp_status == IPP_PRINTER_BUSY ||
++ ipp_status == IPP_SERVICE_UNAVAILABLE)
++ {
++ if (contimeout && (time(NULL) - start_time) > contimeout)
++ {
++ _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ recoverable = 1;
++
++ _cupsLangPrintf(stderr,
++ _("WARNING: recoverable: Network host \'%s\' is busy; "
++ "will retry in %d seconds...\n"),
++ hostname, delay);
++
++ report_printer_state(supported, 0);
++
++ sleep(delay);
++
++ if (delay < 30)
++ delay += 5;
++ }
++ else if ((ipp_status == IPP_BAD_REQUEST ||
++ ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
++ {
++ /*
++ * Switch to IPP/1.0...
++ */
++
++ _cupsLangPrintf(stderr,
++ _("INFO: Printer does not support IPP/%d.%d, trying "
++ "IPP/1.0...\n"), version / 10, version % 10);
++ version = 10;
++ httpReconnect(http);
++ }
++ else if (ipp_status == IPP_NOT_FOUND)
++ {
++ _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n"));
++
++ if (supported)
++ ippDelete(supported);
++
++ return (CUPS_BACKEND_STOP);
++ }
++ else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
++ {
++ if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
++ "Negotiate", 9))
++ auth_info_required = "negotiate";
++
++ fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
++ return (CUPS_BACKEND_AUTH_REQUIRED);
++ }
++ else
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to get printer status (%s)!\n"),
++ cupsLastErrorString());
++ sleep(10);
++ }
++
++ if (supported)
++ ippDelete(supported);
++
++ continue;
++ }
++ else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
++ IPP_TAG_RANGE)) != NULL)
++ {
++ /*
++ * Has the "copies-supported" attribute - does it have an upper
++ * bound > 1?
++ */
++
++ if (copies_sup->values[0].range.upper <= 1)
++ copies_sup = NULL; /* No */
++ }
++
++ format_sup = ippFindAttribute(supported, "document-format-supported",
++ IPP_TAG_MIMETYPE);
++
++ if (format_sup)
++ {
++ fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
++ format_sup->num_values);
++ for (i = 0; i < format_sup->num_values; i ++)
++ fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
++ format_sup->values[i].string.text);
++ }
++
++ report_printer_state(supported, 0);
++ }
++ while (ipp_status > IPP_OK_CONFLICT);
++
++ /*
++ * See if the printer is accepting jobs and is not stopped; if either
++ * condition is true and we are printing to a class, requeue the job...
++ */
++
++ if (getenv("CLASS") != NULL)
++ {
++ printer_state = ippFindAttribute(supported, "printer-state",
++ IPP_TAG_ENUM);
++ printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
++ IPP_TAG_BOOLEAN);
++
++ if (printer_state == NULL ||
++ (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
++ waitprinter) ||
++ printer_accepting == NULL ||
++ !printer_accepting->values[0].boolean)
++ {
++ /*
++ * If the CLASS environment variable is set, the job was submitted
++ * to a class and not to a specific queue. In this case, we want
++ * to abort immediately so that the job can be requeued on the next
++ * available printer in the class.
++ */
++
++ _cupsLangPuts(stderr,
++ _("INFO: Unable to contact printer, queuing on next "
++ "printer in class...\n"));
++
++ ippDelete(supported);
++ httpClose(http);
++
++ if (tmpfilename[0])
++ unlink(tmpfilename);
++
++ /*
++ * Sleep 5 seconds to keep the job from requeuing too rapidly...
++ */
++
++ sleep(5);
++
++ return (CUPS_BACKEND_FAILED);
++ }
++ }
++
++ if (recoverable)
++ {
++ /*
++ * If we've shown a recoverable error make sure the printer proxies
++ * have a chance to see the recovered message. Not pretty but
++ * necessary for now...
++ */
++
++ fputs("INFO: recovered: \n", stderr);
++ sleep(5);
++ }
++
++ /*
++ * See if the printer supports multiple copies...
++ */
++
++ copies = atoi(argv[4]);
++
++ if (copies_sup || argc < 7)
++ {
++ copies_remaining = 1;
++
++ if (argc < 7)
++ copies = 1;
++ }
++ else
++ copies_remaining = copies;
++
++ /*
++ * Then issue the print-job request...
++ */
++
++ job_id = 0;
++
++ while (copies_remaining > 0)
++ {
++ /*
++ * Check for side-channel requests...
++ */
++
++ backendCheckSideChannel(snmp_fd, http->hostaddr);
++
++ /*
++ * Build the IPP request...
++ */
++
++ if (job_cancelled)
++ break;
++
++ if (num_files > 1)
++ request = ippNewRequest(IPP_CREATE_JOB);
++ else
++ request = ippNewRequest(IPP_PRINT_JOB);
++
++ request->request.op.version[0] = version / 10;
++ request->request.op.version[1] = version % 10;
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
++ NULL, uri);
++
++ fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
++
++ if (argv[2][0])
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
++ "requesting-user-name", NULL, argv[2]);
++
++ fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
++
++ /*
++ * Only add a "job-name" attribute if the remote server supports
++ * copy generation - some IPP implementations like HP's don't seem
++ * to like UTF-8 job names (STR #1837)...
++ */
++
++ if (argv[3][0] && copies_sup)
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
++ argv[3]);
++
++ fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
++
++#ifdef HAVE_LIBZ
++ if (compression)
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
++ "compression", NULL, "gzip");
++#endif /* HAVE_LIBZ */
++
++ /*
++ * Handle options on the command-line...
++ */
++
++ options = NULL;
++ num_options = cupsParseOptions(argv[5], 0, &options);
++
++#ifdef __APPLE__
++ if (!strcasecmp(final_content_type, "application/pictwps") &&
++ num_files == 1)
++ {
++ if (format_sup != NULL)
++ {
++ for (i = 0; i < format_sup->num_values; i ++)
++ if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
++ break;
++ }
++
++ if (format_sup == NULL || i >= format_sup->num_values)
++ {
++ /*
++ * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
++ * so convert the document to PostScript...
++ */
++
++ if (run_pictwps_filter(argv, files[0]))
++ {
++ if (pstmpname[0])
++ unlink(pstmpname);
++
++ if (tmpfilename[0])
++ unlink(tmpfilename);
++
++ return (CUPS_BACKEND_FAILED);
++ }
++
++ files[0] = pstmpname;
++
++ /*
++ * Change the MIME type to application/postscript and change the
++ * number of copies to 1...
++ */
++
++ final_content_type = "application/postscript";
++ copies = 1;
++ copies_remaining = 1;
++ send_options = 0;
++ }
++ }
++#endif /* __APPLE__ */
++
++ if (format_sup != NULL)
++ {
++ for (i = 0; i < format_sup->num_values; i ++)
++ if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
++ break;
++
++ if (i < format_sup->num_values)
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
++ "document-format", NULL, final_content_type);
++ }
++
++ if (copies_sup && version > 10 && send_options)
++ {
++ /*
++ * Only send options if the destination printer supports the copies
++ * attribute and IPP/1.1. This is a hack for the HP and Lexmark
++ * implementations of IPP, which do not accept extension attributes
++ * and incorrectly report a client-error-bad-request error instead of
++ * the successful-ok-unsupported-attributes status. In short, at least
++ * some HP and Lexmark implementations of IPP are non-compliant.
++ */
++
++ cupsEncodeOptions(request, num_options, options);
++
++ ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
++ copies);
++ }
++
++ cupsFreeOptions(num_options, options);
++
++ /*
++ * If copies aren't supported, then we are likely dealing with an HP
++ * JetDirect. The HP IPP implementation seems to close the connection
++ * after every request - that is, it does *not* implement HTTP Keep-
++ * Alive, which is REQUIRED by HTTP/1.1...
++ */
++
++ if (!copies_sup)
++ httpReconnect(http);
++
++ /*
++ * Do the request...
++ */
++
++ if (http->version < HTTP_1_1)
++ httpReconnect(http);
++
++ if (num_files > 1)
++ response = cupsDoRequest(http, request, resource);
++ else
++ response = cupsDoFileRequest(http, request, resource, files[0]);
++
++ ipp_status = cupsLastError();
++
++ if (ipp_status > IPP_OK_CONFLICT)
++ {
++ job_id = 0;
++
++ if (job_cancelled)
++ break;
++
++ if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
++ ipp_status == IPP_PRINTER_BUSY)
++ {
++ _cupsLangPuts(stderr,
++ _("INFO: Printer busy; will retry in 10 seconds...\n"));
++ sleep(10);
++ }
++ else if ((ipp_status == IPP_BAD_REQUEST ||
++ ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
++ {
++ /*
++ * Switch to IPP/1.0...
++ */
++
++ _cupsLangPrintf(stderr,
++ _("INFO: Printer does not support IPP/%d.%d, trying "
++ "IPP/1.0...\n"), version / 10, version % 10);
++ version = 10;
++ httpReconnect(http);
++ }
++ else
++ {
++ /*
++ * Update auth-info-required as needed...
++ */
++
++ _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
++ cupsLastErrorString());
++
++ if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
++ {
++ fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
++ httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
++
++ /*
++ * Normal authentication goes through the password callback, which sets
++ * auth_info_required to "username,password". Kerberos goes directly
++ * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
++ * here and set auth_info_required as needed...
++ */
++
++ if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
++ "Negotiate", 9))
++ auth_info_required = "negotiate";
++ }
++ }
++ }
++ else if ((job_id_attr = ippFindAttribute(response, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ _cupsLangPuts(stderr,
++ _("NOTICE: Print file accepted - job ID unknown.\n"));
++ job_id = 0;
++ }
++ else
++ {
++ job_id = job_id_attr->values[0].integer;
++ _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
++ job_id);
++ }
++
++ ippDelete(response);
++
++ if (job_cancelled)
++ break;
++
++ if (job_id && num_files > 1)
++ {
++ for (i = 0; i < num_files; i ++)
++ {
++ /*
++ * Check for side-channel requests...
++ */
++
++ backendCheckSideChannel(snmp_fd, http->hostaddr);
++
++ /*
++ * Send the next file in the job...
++ */
++
++ request = ippNewRequest(IPP_SEND_DOCUMENT);
++ request->request.op.version[0] = version / 10;
++ request->request.op.version[1] = version % 10;
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
++ NULL, uri);
++
++ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
++ job_id);
++
++ if (argv[2][0])
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
++ "requesting-user-name", NULL, argv[2]);
++
++ if ((i + 1) == num_files)
++ ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
++ "document-format", NULL, content_type);
++
++ if (http->version < HTTP_1_1)
++ httpReconnect(http);
++
++ ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
++
++ if (cupsLastError() > IPP_OK_CONFLICT)
++ {
++ ipp_status = cupsLastError();
++
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to add file %d to job: %s\n"),
++ job_id, cupsLastErrorString());
++ break;
++ }
++ }
++ }
++
++ if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
++ {
++ fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
++ copies_remaining --;
++ }
++ else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
++ ipp_status == IPP_PRINTER_BUSY)
++ continue;
++ else
++ copies_remaining --;
++
++ /*
++ * Wait for the job to complete...
++ */
++
++ if (!job_id || !waitjob)
++ continue;
++
++ _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
++
++ for (delay = 1; !job_cancelled;)
++ {
++ /*
++ * Check for side-channel requests...
++ */
++
++ backendCheckSideChannel(snmp_fd, http->hostaddr);
++
++ /*
++ * Build an IPP_GET_JOB_ATTRIBUTES request...
++ */
++
++ request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
++ request->request.op.version[0] = version / 10;
++ request->request.op.version[1] = version % 10;
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
++ NULL, uri);
++
++ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
++ job_id);
++
++ if (argv[2][0])
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
++ "requesting-user-name", NULL, argv[2]);
++
++ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
++ "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
++ NULL, jattrs);
++
++ /*
++ * Do the request...
++ */
++
++ if (!copies_sup || http->version < HTTP_1_1)
++ httpReconnect(http);
++
++ response = cupsDoRequest(http, request, resource);
++ ipp_status = cupsLastError();
++
++ if (ipp_status == IPP_NOT_FOUND)
++ {
++ /*
++ * Job has gone away and/or the server has no job history...
++ */
++
++ ippDelete(response);
++
++ ipp_status = IPP_OK;
++ break;
++ }
++
++ if (ipp_status > IPP_OK_CONFLICT)
++ {
++ if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
++ ipp_status != IPP_PRINTER_BUSY)
++ {
++ ippDelete(response);
++
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to get job %d attributes (%s)!\n"),
++ job_id, cupsLastErrorString());
++ break;
++ }
++ }
++
++ if (response)
++ {
++ if ((job_state = ippFindAttribute(response, "job-state",
++ IPP_TAG_ENUM)) != NULL)
++ {
++ /*
++ * Stop polling if the job is finished or pending-held...
++ */
++
++ if (job_state->values[0].integer > IPP_JOB_STOPPED)
++ {
++ if ((job_sheets = ippFindAttribute(response,
++ "job-media-sheets-completed",
++ IPP_TAG_INTEGER)) != NULL)
++ fprintf(stderr, "PAGE: total %d\n",
++ job_sheets->values[0].integer);
++
++ ippDelete(response);
++ break;
++ }
++ }
++ else
++ {
++ /*
++ * If the printer does not return a job-state attribute, it does not
++ * conform to the IPP specification - break out immediately and fail
++ * the job...
++ */
++
++ fputs("DEBUG: No job-state available from printer - stopping queue.\n",
++ stderr);
++ ipp_status = IPP_INTERNAL_ERROR;
++ break;
++ }
++ }
++
++ ippDelete(response);
++
++ /*
++ * Check the printer state and report it if necessary...
++ */
++
++ check_printer_state(http, uri, resource, argv[2], version, job_id);
++
++ /*
++ * Wait 1-10 seconds before polling again...
++ */
++
++ sleep(delay);
++
++ delay ++;
++ if (delay > 10)
++ delay = 1;
++ }
++ }
++
++ /*
++ * Cancel the job as needed...
++ */
++
++ if (job_cancelled && job_id)
++ cancel_job(http, uri, job_id, resource, argv[2], version);
++
++ /*
++ * Check the printer state and report it if necessary...
++ */
++
++ check_printer_state(http, uri, resource, argv[2], version, job_id);
++
++ /*
++ * Collect the final page count as needed...
++ */
++
++ if (have_supplies &&
++ !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
++ page_count > start_count)
++ fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
++
++#ifdef HAVE_GSSAPI
++ /*
++ * See if we used Kerberos at all...
++ */
++
++ if (http->gssctx)
++ auth_info_required = "negotiate";
++#endif /* HAVE_GSSAPI */
++
++ /*
++ * Free memory...
++ */
++
++ httpClose(http);
++
++ ippDelete(supported);
++
++ /*
++ * Remove the temporary file(s) if necessary...
++ */
++
++ if (tmpfilename[0])
++ unlink(tmpfilename);
++
++#ifdef HAVE_LIBZ
++ if (compression)
++ {
++ for (i = 0; i < num_files; i ++)
++ unlink(files[i]);
++ }
++#endif /* HAVE_LIBZ */
++
++#ifdef __APPLE__
++ if (pstmpname[0])
++ unlink(pstmpname);
++#endif /* __APPLE__ */
++
++ /*
++ * Return the queue status...
++ */
++
++ fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
++
++ if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
++ return (CUPS_BACKEND_AUTH_REQUIRED);
++ else if (ipp_status == IPP_INTERNAL_ERROR)
++ return (CUPS_BACKEND_STOP);
++ else if (ipp_status > IPP_OK_CONFLICT)
++ return (CUPS_BACKEND_FAILED);
++ else
++ {
++ _cupsLangPuts(stderr, _("INFO: Ready to print.\n"));
++ return (CUPS_BACKEND_OK);
++ }
++}
++
++
++/*
++ * 'cancel_job()' - Cancel a print job.
++ */
++
++static void
++cancel_job(http_t *http, /* I - HTTP connection */
++ const char *uri, /* I - printer-uri */
++ int id, /* I - job-id */
++ const char *resource, /* I - Resource path */
++ const char *user, /* I - requesting-user-name */
++ int version) /* I - IPP version */
++{
++ ipp_t *request; /* Cancel-Job request */
++
++
++ _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
++
++ request = ippNewRequest(IPP_CANCEL_JOB);
++ request->request.op.version[0] = version / 10;
++ request->request.op.version[1] = version % 10;
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
++ NULL, uri);
++ ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
++
++ if (user && user[0])
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
++ "requesting-user-name", NULL, user);
++
++ /*
++ * Do the request...
++ */
++
++ if (http->version < HTTP_1_1)
++ httpReconnect(http);
++
++ ippDelete(cupsDoRequest(http, request, resource));
++
++ if (cupsLastError() > IPP_OK_CONFLICT)
++ _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
++ cupsLastErrorString());
++}
++
++
++/*
++ * 'check_printer_state()' - Check the printer state...
++ */
++
++static void
++check_printer_state(
++ http_t *http, /* I - HTTP connection */
++ const char *uri, /* I - Printer URI */
++ const char *resource, /* I - Resource path */
++ const char *user, /* I - Username, if any */
++ int version, /* I - IPP version */
++ int job_id) /* I - Current job ID */
++{
++ ipp_t *request, /* IPP request */
++ *response; /* IPP response */
++ static const char * const attrs[] = /* Attributes we want */
++ {
++ "com.apple.print.recoverable-message",
++ "marker-colors",
++ "marker-levels",
++ "marker-message",
++ "marker-names",
++ "marker-types",
++ "printer-state-message",
++ "printer-state-reasons"
++ };
++
++
++ /*
++ * Check on the printer state...
++ */
++
++ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
++ request->request.op.version[0] = version / 10;
++ request->request.op.version[1] = version % 10;
++
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
++ NULL, uri);
++
++ if (user && user[0])
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
++ "requesting-user-name", NULL, user);
++
++ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
++ "requested-attributes",
++ (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
++
++ /*
++ * Do the request...
++ */
++
++ if (http->version < HTTP_1_1)
++ httpReconnect(http);
++
++ if ((response = cupsDoRequest(http, request, resource)) != NULL)
++ {
++ report_printer_state(response, job_id);
++ ippDelete(response);
++ }
++}
++
++
++#ifdef HAVE_LIBZ
++/*
++ * 'compress_files()' - Compress print files...
++ */
++
++static void
++compress_files(int num_files, /* I - Number of files */
++ char **files) /* I - Files */
++{
++ int i, /* Looping var */
++ fd; /* Temporary file descriptor */
++ ssize_t bytes; /* Bytes read/written */
++ size_t total; /* Total bytes read */
++ cups_file_t *in, /* Input file */
++ *out; /* Output file */
++ struct stat outinfo; /* Output file information */
++ char filename[1024], /* Temporary filename */
++ buffer[32768]; /* Copy buffer */
++
++
++ fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
++ for (i = 0; i < num_files; i ++)
++ {
++ if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to create temporary compressed print "
++ "file: %s\n"), strerror(errno));
++ exit(CUPS_BACKEND_FAILED);
++ }
++
++ if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to open temporary compressed print "
++ "file: %s\n"), strerror(errno));
++ exit(CUPS_BACKEND_FAILED);
++ }
++
++ if ((in = cupsFileOpen(files[i], "r")) == NULL)
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to open print file \"%s\": %s\n"),
++ files[i], strerror(errno));
++ cupsFileClose(out);
++ exit(CUPS_BACKEND_FAILED);
++ }
++
++ total = 0;
++ while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
++ if (cupsFileWrite(out, buffer, bytes) < bytes)
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
++ (int)bytes, filename, strerror(errno));
++ cupsFileClose(in);
++ cupsFileClose(out);
++ exit(CUPS_BACKEND_FAILED);
++ }
++ else
++ total += bytes;
++
++ cupsFileClose(out);
++ cupsFileClose(in);
++
++ files[i] = strdup(filename);
++
++ if (!stat(filename, &outinfo))
++ fprintf(stderr,
++ "DEBUG: File %d compressed to %.1f%% of original size, "
++ CUPS_LLFMT " bytes...\n",
++ i + 1, 100.0 * outinfo.st_size / total,
++ CUPS_LLCAST outinfo.st_size);
++ }
++}
++#endif /* HAVE_LIBZ */
++
++
++/*
++ * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
++ */
++
++static const char * /* O - Password */
++password_cb(const char *prompt) /* I - Prompt (not used) */
++{
++ (void)prompt;
++
++ /*
++ * Remember that we need to authenticate...
++ */
++
++ auth_info_required = "username,password";
++
++ if (password && *password && password_tries < 3)
++ {
++ password_tries ++;
++
++ return (password);
++ }
++ else
++ {
++ /*
++ * Give up after 3 tries or if we don't have a password to begin with...
++ */
++
++ return (NULL);
++ }
++}
++
++
++/*
++ * 'report_attr()' - Report an IPP attribute value.
++ */
++
++static void
++report_attr(ipp_attribute_t *attr) /* I - Attribute */
++{
++ int i; /* Looping var */
++ char value[1024], /* Value string */
++ *valptr, /* Pointer into value string */
++ *attrptr; /* Pointer into attribute value */
++
++
++ /*
++ * Convert the attribute values into quoted strings...
++ */
++
++ for (i = 0, valptr = value;
++ i < attr->num_values && valptr < (value + sizeof(value) - 10);
++ i ++)
++ {
++ if (i > 0)
++ *valptr++ = ',';
++
++ switch (attr->value_tag)
++ {
++ case IPP_TAG_INTEGER :
++ case IPP_TAG_ENUM :
++ snprintf(valptr, sizeof(value) - (valptr - value), "%d",
++ attr->values[i].integer);
++ valptr += strlen(valptr);
++ break;
++
++ case IPP_TAG_TEXT :
++ case IPP_TAG_NAME :
++ case IPP_TAG_KEYWORD :
++ *valptr++ = '\"';
++ for (attrptr = attr->values[i].string.text;
++ *attrptr && valptr < (value + sizeof(value) - 10);
++ attrptr ++)
++ {
++ if (*attrptr == '\\' || *attrptr == '\"')
++ *valptr++ = '\\';
++
++ *valptr++ = *attrptr;
++ }
++ *valptr++ = '\"';
++ break;
++
++ default :
++ /*
++ * Unsupported value type...
++ */
++
++ return;
++ }
++ }
++
++ *valptr = '\0';
++
++ /*
++ * Tell the scheduler about the new values...
++ */
++
++ fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
++}
++
++
++/*
++ * 'report_printer_state()' - Report the printer state.
++ */
++
++static int /* O - Number of reasons shown */
++report_printer_state(ipp_t *ipp, /* I - IPP response */
++ int job_id) /* I - Current job ID */
++{
++ int i; /* Looping var */
++ int count; /* Count of reasons shown... */
++ ipp_attribute_t *caprm, /* com.apple.print.recoverable-message */
++ *psm, /* printer-state-message */
++ *reasons, /* printer-state-reasons */
++ *marker; /* marker-* attributes */
++ const char *reason; /* Current reason */
++ const char *prefix; /* Prefix for STATE: line */
++ char state[1024]; /* State string */
++ int saw_caprw; /* Saw com.apple.print.recoverable-warning state */
++
++
++ if ((psm = ippFindAttribute(ipp, "printer-state-message",
++ IPP_TAG_TEXT)) != NULL)
++ fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
++
++ if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
++ IPP_TAG_KEYWORD)) == NULL)
++ return (0);
++
++ saw_caprw = 0;
++ state[0] = '\0';
++ prefix = "STATE: ";
++
++ for (i = 0, count = 0; i < reasons->num_values; i ++)
++ {
++ reason = reasons->values[i].string.text;
++
++ if (!strcmp(reason, "com.apple.print.recoverable-warning"))
++ saw_caprw = 1;
++ else if (strcmp(reason, "paused"))
++ {
++ strlcat(state, prefix, sizeof(state));
++ strlcat(state, reason, sizeof(state));
++
++ prefix = ",";
++ }
++ }
++
++ if (state[0])
++ fprintf(stderr, "%s\n", state);
++
++ /*
++ * Relay com.apple.print.recoverable-message...
++ */
++
++ if ((caprm = ippFindAttribute(ipp, "com.apple.print.recoverable-message",
++ IPP_TAG_TEXT)) != NULL)
++ fprintf(stderr, "WARNING: %s: %s\n",
++ saw_caprw ? "recoverable" : "recovered",
++ caprm->values[0].string.text);
++
++ /*
++ * Relay the current marker-* attribute values...
++ */
++
++ if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
++ report_attr(marker);
++ if ((marker = ippFindAttribute(ipp, "marker-high-levels",
++ IPP_TAG_INTEGER)) != NULL)
++ report_attr(marker);
++ if ((marker = ippFindAttribute(ipp, "marker-levels",
++ IPP_TAG_INTEGER)) != NULL)
++ report_attr(marker);
++ if ((marker = ippFindAttribute(ipp, "marker-low-levels",
++ IPP_TAG_INTEGER)) != NULL)
++ report_attr(marker);
++ if ((marker = ippFindAttribute(ipp, "marker-message", IPP_TAG_TEXT)) != NULL)
++ report_attr(marker);
++ if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
++ report_attr(marker);
++ if ((marker = ippFindAttribute(ipp, "marker-types", IPP_TAG_KEYWORD)) != NULL)
++ report_attr(marker);
++
++ return (count);
++}
++
++
++#ifdef __APPLE__
++/*
++ * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
++ * remotely.
++ *
++ * This step is required because the PICT format is not documented and
++ * subject to change, so developing a filter for other OS's is infeasible.
++ * Also, fonts required by the PICT file need to be embedded on the
++ * client side (which has the fonts), so we run the filter to get a
++ * PostScript file for printing...
++ */
++
++static int /* O - Exit status of filter */
++run_pictwps_filter(char **argv, /* I - Command-line arguments */
++ const char *filename)/* I - Filename */
++{
++ struct stat fileinfo; /* Print file information */
++ const char *ppdfile; /* PPD file for destination printer */
++ int pid; /* Child process ID */
++ int fd; /* Temporary file descriptor */
++ int status; /* Exit status of filter */
++ const char *printer; /* PRINTER env var */
++ static char ppdenv[1024]; /* PPD environment variable */
++
++
++ /*
++ * First get the PPD file for the printer...
++ */
++
++ printer = getenv("PRINTER");
++ if (!printer)
++ {
++ _cupsLangPuts(stderr,
++ _("ERROR: PRINTER environment variable not defined!\n"));
++ return (-1);
++ }
++
++ if ((ppdfile = cupsGetPPD(printer)) == NULL)
++ {
++ _cupsLangPrintf(stderr,
++ _("ERROR: Unable to get PPD file for printer \"%s\" - "
++ "%s.\n"), printer, cupsLastErrorString());
++ }
++ else
++ {
++ snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
++ putenv(ppdenv);
++ }
++
++ /*
++ * Then create a temporary file for printing...
++ */
++
++ if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
++ {
++ _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
++ if (ppdfile)
++ unlink(ppdfile);
++ return (-1);
++ }
++
++ /*
++ * Get the owner of the spool file - it is owned by the user we want to run
++ * as...
++ */
++
++ if (argv[6])
++ stat(argv[6], &fileinfo);
++ else
++ {
++ /*
++ * Use the OSX defaults, as an up-stream filter created the PICT
++ * file...
++ */
++
++ fileinfo.st_uid = 1;
++ fileinfo.st_gid = 80;
++ }
++
++ if (ppdfile)
++ chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
++
++ fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
++
++ /*
++ * Finally, run the filter to convert the file...
++ */
++
++ if ((pid = fork()) == 0)
++ {
++ /*
++ * Child process for pictwpstops... Redirect output of pictwpstops to a
++ * file...
++ */
++
++ dup2(fd, 1);
++ close(fd);
++
++ if (!getuid())
++ {
++ /*
++ * Change to an unpriviledged user...
++ */
++
++ if (setgid(fileinfo.st_gid))
++ return (errno);
++
++ if (setuid(fileinfo.st_uid))
++ return (errno);
++ }
++
++ execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
++ filename, NULL);
++ _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
++ strerror(errno));
++ return (errno);
++ }
++
++ close(fd);
++
++ if (pid < 0)
++ {
++ /*
++ * Error!
++ */
++
++ _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
++ strerror(errno));
++ if (ppdfile)
++ unlink(ppdfile);
++ return (-1);
++ }
++
++ /*
++ * Now wait for the filter to complete...
++ */
++
++ if (wait(&status) < 0)
++ {
++ _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
++ strerror(errno));
++ close(fd);
++ if (ppdfile)
++ unlink(ppdfile);
++ return (-1);
++ }
++
++ if (ppdfile)
++ unlink(ppdfile);
++
++ close(fd);
++
++ if (status)
++ {
++ if (status >= 256)
++ _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
++ status / 256);
++ else
++ _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
++ status);
++
++ return (status);
++ }
++
++ /*
++ * Return with no errors..
++ */
++
++ return (0);
++}
++#endif /* __APPLE__ */
++
++
++/*
++ * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
++ */
++
++static void
++sigterm_handler(int sig) /* I - Signal */
++{
++ (void)sig; /* remove compiler warnings... */
++
++ if (!job_cancelled)
++ {
++ /*
++ * Flag that the job should be cancelled...
++ */
++
++ job_cancelled = 1;
++ return;
++ }
++
++ /*
++ * The scheduler already tried to cancel us once, now just terminate
++ * after removing our temp files!
++ */
++
++ if (tmpfilename[0])
++ unlink(tmpfilename);
++
++#ifdef __APPLE__
++ if (pstmpname[0])
++ unlink(pstmpname);
++#endif /* __APPLE__ */
++
++ exit(1);
++}
++
++
++/*
++ * End of "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $".
++ */
diff --git a/cups.spec b/cups.spec
index d5b8d41..fbc2044 100644
--- a/cups.spec
+++ b/cups.spec
@@ -17,7 +17,7 @@ Summary(pl.UTF-8): Ogólny system druku dla Uniksa
Summary(pt_BR.UTF-8): Sistema Unix de Impressão
Name: cups
Version: 1.5.3
-Release: 1
+Release: 2
Epoch: 1
License: LGPL v2 (libraries), GPL v2 (the rest) + openssl exception
Group: Applications/Printing
@@ -44,8 +44,10 @@ Patch10: %{name}-peercred.patch
Patch11: %{name}-usb.patch
Patch12: %{name}-desktop.patch
Patch13: %{name}-systemd-socket.patch
-# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=638521
-Patch14: ipp-revert-1.4.patch
+Patch14: add-ipp-backend-of-cups-1.4.patch
+Patch15: ipp-backend-cups-1.5.4-fixes.patch
+Patch16: reactivate_recommended_driver.patch
+Patch17: read-embedded-options-from-incoming-postscript-and-add-to-ipp-attrs.patch
# avahi patches from fedora
Patch100: %{name}-avahi-1-config.patch
Patch101: %{name}-avahi-2-backend.patch
@@ -325,8 +327,9 @@ Wsparcie dla LPD w serwerze wydruków CUPS.
#%patch11 -p1
%patch12 -p1
%patch13 -p1
-# 1.5.3 shows it may have a chance of working without this
-#%patch14 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
%if %{with avahi}
%patch100 -p1
@@ -568,6 +571,7 @@ fi
%attr(755,root,root) %{_ulibdir}/cups/backend/http
%attr(755,root,root) %{_ulibdir}/cups/backend/https
%attr(755,root,root) %{_ulibdir}/cups/backend/ipp
+%attr(755,root,root) %{_ulibdir}/cups/backend/ipp14
%attr(755,root,root) %{_ulibdir}/cups/backend/ipps
%attr(755,root,root) %{_ulibdir}/cups/backend/lpd
%attr(755,root,root) %{_ulibdir}/cups/backend/snmp
diff --git a/ipp-backend-cups-1.5.4-fixes.patch b/ipp-backend-cups-1.5.4-fixes.patch
new file mode 100644
index 0000000..28aa835
--- /dev/null
+++ b/ipp-backend-cups-1.5.4-fixes.patch
@@ -0,0 +1,187 @@
+--- a/backend/ipp.c
++++ b/backend/ipp.c
+@@ -62,7 +62,8 @@
+ *resource; /* Resource path */
+ int port, /* Port number */
+ version, /* IPP version */
+- job_id; /* Job ID for submitted job */
++ job_id, /* Job ID for submitted job */
++ get_job_attrs; /* Support Get-Job-Attributes? */
+ const char *job_name; /* Job name for submitted job */
+ http_encryption_t encryption; /* Use encryption? */
+ ipp_jstate_t job_state; /* Current job state */
+@@ -237,6 +238,7 @@
+ ipp_attribute_t *printer_state; /* printer-state attribute */
+ ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
+ int create_job = 0, /* Does printer support Create-Job? */
++ get_job_attrs = 0, /* Does printer support Get-Job-Attributes? */
+ send_document = 0, /* Does printer support Send-Document? */
+ validate_job = 0; /* Does printer support Validate-Job? */
+ int copies, /* Number of copies for job */
+@@ -1065,13 +1067,18 @@
+ create_job = 1;
+ else if (operations_sup->values[i].integer == IPP_SEND_DOCUMENT)
+ send_document = 1;
++ else if (operations_sup->values[i].integer == IPP_GET_JOB_ATTRIBUTES)
++ get_job_attrs = 1;
+ }
+
+- if (!send_document)
++ if (create_job && !send_document)
+ {
+ fputs("DEBUG: Printer supports Create-Job but not Send-Document.\n",
+ stderr);
+ create_job = 0;
++
++ update_reasons(NULL, "+cups-ipp-conformance-failure-report,"
++ "cups-ipp-missing-send-document");
+ }
+
+ if (!validate_job)
+@@ -1255,6 +1262,7 @@
+ monitor.port = port;
+ monitor.version = version;
+ monitor.job_id = 0;
++ monitor.get_job_attrs = get_job_attrs;
+ monitor.encryption = cupsEncryption();
+ monitor.job_state = IPP_JOB_PENDING;
+ monitor.printer_state = IPP_PRINTER_IDLE;
+@@ -1298,6 +1306,8 @@
+ _cupsLangPrintFilter(stderr, "INFO", _("The printer is busy."));
+ sleep(10);
+ }
++ else if (ipp_status == IPP_DOCUMENT_FORMAT)
++ goto cleanup;
+ else if (ipp_status == IPP_FORBIDDEN ||
+ ipp_status == IPP_AUTHENTICATION_CANCELED)
+ {
+@@ -1652,7 +1662,7 @@
+ * Wait for the job to complete...
+ */
+
+- if (!job_id || !waitjob)
++ if (!job_id || !waitjob || !get_job_attrs)
+ continue;
+
+ _cupsLangPrintFilter(stderr, "INFO", _("Waiting for job to complete."));
+@@ -1695,7 +1705,7 @@
+ response = cupsDoRequest(http, request, resource);
+ ipp_status = cupsLastError();
+
+- if (ipp_status == IPP_NOT_FOUND)
++ if (ipp_status == IPP_NOT_FOUND || ipp_status == IPP_NOT_POSSIBLE)
+ {
+ /*
+ * Job has gone away and/or the server has no job history...
+@@ -1717,7 +1727,6 @@
+ else
+ {
+ if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
+- ipp_status != IPP_NOT_POSSIBLE &&
+ ipp_status != IPP_PRINTER_BUSY)
+ {
+ ippDelete(response);
+@@ -1865,12 +1874,18 @@
+ return (CUPS_BACKEND_AUTH_REQUIRED);
+ else if (ipp_status == IPP_INTERNAL_ERROR)
+ return (CUPS_BACKEND_STOP);
+- else if (ipp_status == IPP_DOCUMENT_FORMAT ||
+- ipp_status == IPP_CONFLICT)
++ else if (ipp_status == IPP_CONFLICT)
+ return (CUPS_BACKEND_FAILED);
+- else if (ipp_status == IPP_REQUEST_VALUE)
++ else if (ipp_status == IPP_REQUEST_VALUE ||
++ ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0)
+ {
+- _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
++ if (ipp_status == IPP_REQUEST_VALUE)
++ _cupsLangPrintFilter(stderr, "ERROR", _("Print job too large."));
++ else if (ipp_status == IPP_DOCUMENT_FORMAT)
++ _cupsLangPrintFilter(stderr, "ERROR",
++ _("Printer cannot print supplied content."));
++ else
++ _cupsLangPrintFilter(stderr, "ERROR", _("Print job canceled at printer."));
+ return (CUPS_BACKEND_CANCEL);
+ }
+ else if (ipp_status > IPP_OK_CONFLICT && ipp_status != IPP_ERROR_JOB_CANCELED)
+@@ -2116,7 +2131,8 @@
+ * Check the status of the job itself...
+ */
+
+- job_op = monitor->job_id > 0 ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
++ job_op = (monitor->job_id > 0 && monitor->get_job_attrs) ?
++ IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS;
+ request = ippNewRequest(job_op);
+ request->request.op.version[0] = monitor->version / 10;
+ request->request.op.version[1] = monitor->version % 10;
+@@ -2306,7 +2322,7 @@
+ fprintf(stderr, "DEBUG: job-name=\"%s\"\n", title);
+ }
+
+- if (format)
++ if (format && op != IPP_CREATE_JOB)
+ {
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
+ "document-format", NULL, format);
+@@ -2314,7 +2330,7 @@
+ }
+
+ #ifdef HAVE_LIBZ
+- if (compression)
++ if (compression && op != IPP_CREATE_JOB)
+ {
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "compression", NULL, compression);
+--- a/scheduler/printers.c
++++ b/scheduler/printers.c
+@@ -4233,6 +4233,41 @@
+ }
+
+ /*
++ * media-size-supported
++ */
++
++ num_media = p->pc->num_sizes;
++ if (p->pc->custom_min_keyword)
++ num_media ++;
++
++ if ((attr = ippAddCollections(p->ppd_attrs, IPP_TAG_PRINTER,
++ "media-size-supported", num_media,
++ NULL)) != NULL)
++ {
++ val = attr->values;
++
++ for (i = p->pc->num_sizes, pwgsize = p->pc->sizes;
++ i > 0;
++ i --, pwgsize ++, val ++)
++ {
++ val->collection = ippNew();
++ ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
++ "x-dimension", pwgsize->width);
++ ippAddInteger(val->collection, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
++ "y-dimension", pwgsize->length);
++ }
++
++ if (p->pc->custom_min_keyword)
++ {
++ val->collection = ippNew();
++ ippAddRange(val->collection, IPP_TAG_PRINTER, "x-dimension",
++ p->pc->custom_min_width, p->pc->custom_max_width);
++ ippAddRange(val->collection, IPP_TAG_PRINTER, "y-dimension",
++ p->pc->custom_min_length, p->pc->custom_max_length);
++ }
++ }
++
++ /*
+ * media-source-supported
+ */
+
+@@ -5145,6 +5180,8 @@
+ message = "Printer does not support REQUIRED Validate-Job operation.";
+ else if (!strcmp(reason, "missing-get-printer-attributes"))
+ message = "Printer does not support REQUIRED Get-Printer-Attributes operation.";
++ else if (!strcmp(reason, "missing-send-document"))
++ message = "Printer supports Create-Job but not Send-Document operation.";
+ else if (!strcmp(reason, "missing-job-history"))
+ message = "Printer does not provide REQUIRED job history.";
+ else if (!strcmp(reason, "missing-job-id"))
diff --git a/reactivate_recommended_driver.patch b/reactivate_recommended_driver.patch
new file mode 100644
index 0000000..4926382
--- /dev/null
+++ b/reactivate_recommended_driver.patch
@@ -0,0 +1,29 @@
+Description: CUPS removes the "(recommended)" comments of the NickNames of Foomatic PPDs when listing available PPDs. This patch removes this remocval action.
+Author: till.kamppeter at gmail.com
+
+--- cups-1.4.0~svn8773~/scheduler/cups-driverd.cxx 2009-08-23 12:16:58.000000000 +0200
++++ cups-1.4.0~svn8773/scheduler/cups-driverd.cxx 2009-08-23 18:33:34.000000000 +0200
+@@ -211,7 +211,6 @@
+ const char *scheme) /* I - PPD scheme */
+ {
+ ppd_info_t *ppd; /* PPD */
+- char *recommended; /* Foomatic driver string */
+
+
+ /*
+@@ -250,15 +249,6 @@
+ strlcpy(ppd->record.scheme, scheme, sizeof(ppd->record.scheme));
+
+ /*
+- * Strip confusing (and often wrong) "recommended" suffix added by
+- * Foomatic drivers...
+- */
+-
+- if ((recommended = strstr(ppd->record.make_and_model,
+- " (recommended)")) != NULL)
+- *recommended = '\0';
+-
+- /*
+ * Add the PPD to the PPD arrays...
+ */
+
diff --git a/read-embedded-options-from-incoming-postscript-and-add-to-ipp-attrs.patch b/read-embedded-options-from-incoming-postscript-and-add-to-ipp-attrs.patch
new file mode 100644
index 0000000..1cb8cc2
--- /dev/null
+++ b/read-embedded-options-from-incoming-postscript-and-add-to-ipp-attrs.patch
@@ -0,0 +1,102 @@
+Author: till.kamppeter at gmail.com
+
+--- a/scheduler/ipp.c
++++ b/scheduler/ipp.c
+@@ -9639,6 +9639,11 @@
+ ipp_attribute_t *attr, /* Current attribute */
+ *attr2, /* Job attribute */
+ *prev2; /* Previous job attribute */
++ int foundfirstpage; /* Did we find the first page already
++ in the PostScript input? */
++ int num_copies; /* Number of copies according to
++ PostScript command in input file */
++ char *s, *t, buffer[10];
+
+
+ /*
+@@ -9700,6 +9705,85 @@
+ }
+
+ /*
++ * Read option settings embedded in the file...
++ */
++
++ foundfirstpage = 0;
++
++ while (cupsFileGets(fp, line, sizeof(line)))
++ {
++ /*
++ * Stop at the second page, we read also the settings of the first PageSetup
++ * to work around a bug in OpenOffice.org. This app puts options intended
++ * for the whole document into the page setup of the first page
++ */
++
++ if (!strncmp(line, "%%Page:", 7))
++ {
++ if (foundfirstpage == 1)
++ break;
++ foundfirstpage = 1;
++ }
++
++ /*
++ * Add the embedded option settings to the option array...
++ */
++
++ s = NULL;
++ if (!strncmp(line, "%%BeginFeature:", 15))
++ s = line + 15;
++ else if (!strncmp(line, "%%IncludeFeature:", 17))
++ s = line + 17;
++ else if (!strncmp(line, "%%BeginNonPPDFeature:", 21))
++ s = line + 21;
++
++ if (s && (t = strstr(s, "NumCopies")) != NULL)
++ {
++ t += 9;
++ while ((*t == ' ') || (*t == '\t')) t++;
++ if (sscanf(t, "%9d", &num_copies) == 1)
++ {
++ sprintf(buffer, "%d", num_copies);
++ num_options = cupsAddOption("copies", buffer, num_options, &options);
++ }
++ }
++ else if (s)
++ {
++ while ((*s == ' ') || (*s == '\t')) s++;
++ if (*s == '*') s++;
++ t = s;
++ while (*t && (*t != ' ') && (*t != '\t')) t++;
++ if ((*t == ' ') || (*t == '\t')) *t = '=';
++ num_options = cupsParseOptions(s, num_options, &options);
++ }
++
++ /*
++ * Read out "/#copies XXX def" and "/NumCopies XXX def" expressions from
++ * PostScript input. Some apps insert these expressions to set the
++ * number of copies.
++ */
++
++ s = NULL;
++ if ((s = strstr(line, "/#copies")) != NULL)
++ s += 8;
++ else if ((s = strstr(line, "/NumCopies")) != NULL)
++ s += 10;
++ if (s)
++ {
++ while ((*s == ' ') || (*s == '\t')) s++;
++ if (sscanf(s, "%9d %as ", &num_copies, &t) == 2)
++ {
++ if (!strncmp(t, "def", 3))
++ {
++ sprintf(buffer, "%d", num_copies);
++ num_options = cupsAddOption("copies", buffer, num_options, &options);
++ }
++ free(t);
++ }
++ }
++ }
++
++ /*
+ * Done with the file; see if we have any options...
+ */
+
More information about the pld-cvs-commit
mailing list