diff -Nru cups-1.5.3/debian/changelog cups-1.5.3/debian/changelog --- cups-1.5.3/debian/changelog 2012-05-23 13:40:24.000000000 +0000 +++ cups-1.5.3/debian/changelog 2012-07-10 21:42:38.000000000 +0000 @@ -1,3 +1,62 @@ +cups (1.5.3-0ubuntu2) precise-proposed; urgency=low + + * debian/patches/usb-backend-further-enhancements.patch: Added latest + development work on the libusb-based USB backend: + - Support for uni-directional devices, both protocol-1 devices and + devices where no read endpoint is found (LP: #1000253, LP: #1001028). + - Soft reset specific to the "PRINTER" device class. This allows a + reset without reconnecting. + - When closing the device, it will also get reset to its original + configuration, before re-attaching the usblp kernel module. Do not + restore the configuration setting when the old configuration was zero, + as zero means "unconfigured". + - Added option "usb-unidir" to force the backend into uni-directional + mode. This allows to work around problems with bi-di communications, + especially also a delay at the end of the job caused by closing the + read channel (happens only for some devices, LP: #1001028). Also + useful for debugging. + - Added the quirk management of the usblp kernel module. So the problems + of all printers which were worked around in the kernel module are + also worked around in the libusb-based CUPS backend now (LP: #1000253). + - Added new quirk type to quirk manager: Printers for which the usblp + kernel module should not get reattached after printing a job + (LP: #1000253). + - Added additional quirks for the Prolific Technology USB -> Parallel + adapter, as the adapter needs uni-directional mode to be forced and + also does not like re-attaching the usblp kernel module after the + job (last third of last page gets cut off, re-attaching probably + sends a reset to the printer while there is still data to be printed + in the printer's internal buffer, LP: #987485). + - Added the command line option "usb-no-reattach". With the option set + the usblp kernel module does not get reattached after a job has been + printed. Some printers cut off the end of the job or even crash by + re-attaching the module. This is a development/debug mode to test + whether re-attaching was the culprit of a problem. Users should + report such issues so that their printers can get added to the quirk + list. + - Do a printer reset after each job, this makes the Prolific USB -> + Parallel adapter finally work (LP: #987485) and makes it unnecessary + to blacklist the usblp kernel module for some printers (LP: #997040). + - Some extra debug messages. + - Added a missing libusb_free_config_descriptor(). + This patch is submitted upstream as CUPS STR #4128. + * debian/patches/add-ipp-backend-of-cups-1.4.patch, debian/cups.config, + debian/cups.lintian-overrides, debian/cups.postinst, debian/cups.prerm, + debian/cups.templates: Add the IPP backend of CUPS 1.4.x to the current + CUPS package as independent backend "ipp14". Some devices (like the + LiveBox 2 and some Samsung printers) do not work with the current IPP + backend (LP: #945028, LP: #973270). + * debian/local/blacklist-cups-usblp.conf, debian/cups.postinst, + debian/cups.install, debian/cups.preinst, debian/cups.postinst, + debian/cups.postrm: As we have vastly improved the USB backend, we + lift the blacklisting again as it is not needed any more. This way + users with proprietary third-party backends based on /dev/usb/lp* + device files can print again. The problems which we have worked around + with the first SRU for Precise are now actually fixed in the USB + backend (LP: 997040, LP: #1000253). + + -- Till Kamppeter Tue, 10 Jul 2012 23:09:01 +0200 + cups (1.5.3-0ubuntu1) precise-proposed; urgency=low [ Till Kamppeter ] diff -Nru cups-1.5.3/debian/cups.config cups-1.5.3/debian/cups.config --- cups-1.5.3/debian/cups.config 2012-04-05 00:09:23.000000000 +0000 +++ cups-1.5.3/debian/cups.config 2012-07-10 20:57:38.000000000 +0000 @@ -21,7 +21,7 @@ if [ "$ARCH" = "ppc" -o "$ARCH" = "ppc-none" ]; then db_fget cupsys/backend seen if [ "$RET" = "false" ]; then - db_set cupsys/backend "ipp, lpd, socket, usb" + db_set cupsys/backend "ipp, ipp14, lpd, socket, usb" fi fi diff -Nru cups-1.5.3/debian/cups.install cups-1.5.3/debian/cups.install --- cups-1.5.3/debian/cups.install 2012-05-23 13:34:43.000000000 +0000 +++ cups-1.5.3/debian/cups.install 2012-07-10 20:57:38.000000000 +0000 @@ -50,4 +50,3 @@ usr/share/man/man8/cupsfilter.8.gz #usr/share/man/*/man8/cupsfilter.8.gz ../presubj usr/share/bug/cups/ -../local/blacklist-cups-usblp.conf etc/modprobe.d diff -Nru cups-1.5.3/debian/cups.lintian-overrides cups-1.5.3/debian/cups.lintian-overrides --- cups-1.5.3/debian/cups.lintian-overrides 2012-04-05 00:09:23.000000000 +0000 +++ cups-1.5.3/debian/cups.lintian-overrides 2012-07-10 20:57:38.000000000 +0000 @@ -1,4 +1,5 @@ cups: non-standard-executable-perm usr/lib/cups/backend-available/ipp 0744 != 0755 +cups: non-standard-executable-perm usr/lib/cups/backend-available/ipp14 0744 != 0755 cups: non-standard-executable-perm usr/lib/cups/backend-available/lpd 0744 != 0755 cups: non-standard-executable-perm usr/lib/cups/backend-available/dnssd 0744 != 0755 cups: non-standard-executable-perm usr/lib/cups/backend-available/snmp 0555 != 0755 diff -Nru cups-1.5.3/debian/cups.postinst cups-1.5.3/debian/cups.postinst --- cups-1.5.3/debian/cups.postinst 2012-05-23 13:33:07.000000000 +0000 +++ cups-1.5.3/debian/cups.postinst 2012-07-10 21:07:44.000000000 +0000 @@ -22,6 +22,8 @@ # installation fails and the `postinst' is called with `abort-upgrade', # `abort-remove' or `abort-deconfigure'. +dpkg-maintscript-helper rm_conffile /etc/modprobe.d/blacklist-cups-usblp.conf 1.5.3-0ubuntu1 -- "$@" + # Debconf . /usr/share/debconf/confmodule @@ -79,9 +81,14 @@ db_get cupsys/backend && SELECTED=$RET # We remove the scsi backend from the output as it got removed in CUPS 1.5.0 list=`echo $SELECTED | sed -e 's/, /,/g' | sed -e 's/scsi,*//g' | sed -e 's/parallel,*//g' | sed -e 's/serial,*//g'` + if dpkg --compare-versions "$2" lt-nl "1.5.3-3"; then + if ! echo $list | grep -q "\bipp14\b"; then + list=`echo $list | sed -e 's/\bipp\b/ipp,ipp14/g'` + fi + fi save_IFS=$IFS IFS=, - (cd /usr/lib/cups/backend && rm -f http https ipp ipps lpd socket usb snmp dnssd mdns) + (cd /usr/lib/cups/backend && rm -f http https ipp ipp14 ipps lpd socket usb snmp dnssd mdns) for module in $list; do ln /usr/lib/cups/backend-available/$module /usr/lib/cups/backend/$module if [ "$module" = "ipp" ]; then @@ -99,7 +106,7 @@ db_fset cupsys/backend changed false # Resync Debconf database with real state - list=`( cd /usr/lib/cups/backend && for f in ipp lpd socket usb snmp dnssd; do [ -e $f ] && echo -n "$f, "; done ) | sed -e 's/, $//'` + list=`( cd /usr/lib/cups/backend && for f in ipp ipp14 lpd socket usb snmp dnssd; do [ -e $f ] && echo -n "$f, "; done ) | sed -e 's/, $//'` db_set cupsys/backend $list; if [ -f /etc/cups/classes.conf ]; then @@ -170,8 +177,8 @@ fi fi - if dpkg --compare-versions "$2" lt "1.5.3"; then - rmmod usblp || : + if dpkg --compare-versions "$2" lt "1.5.3-3"; then + modprobe usblp >/dev/null 2>&1 || : fi fi diff -Nru cups-1.5.3/debian/cups.postrm cups-1.5.3/debian/cups.postrm --- cups-1.5.3/debian/cups.postrm 2012-05-23 13:31:41.000000000 +0000 +++ cups-1.5.3/debian/cups.postrm 2012-07-10 21:07:29.000000000 +0000 @@ -21,6 +21,8 @@ #DEBHELPER# +dpkg-maintscript-helper rm_conffile /etc/modprobe.d/blacklist-cups-usblp.conf 1.5.3-0ubuntu1 -- "$@" + case "$1" in purge) rm -rf /var/lib/cups diff -Nru cups-1.5.3/debian/cups.preinst cups-1.5.3/debian/cups.preinst --- cups-1.5.3/debian/cups.preinst 2012-05-23 13:31:06.000000000 +0000 +++ cups-1.5.3/debian/cups.preinst 2012-07-10 21:03:04.000000000 +0000 @@ -1,6 +1,8 @@ #! /bin/sh set -e +dpkg-maintscript-helper rm_conffile /etc/modprobe.d/blacklist-cups-usblp.conf 1.5.3-0ubuntu1 -- "$@" + # On Ubuntu, replace the obsolete init script (replaced by an upstart job) if [ "`lsb_release -is 2>/dev/null`" = "Ubuntu" ] && [ ! -L /etc/init.d/cups ]; then dpkg-maintscript-helper rm_conffile /etc/init.d/cups 1.4.4-5 -- "$@" diff -Nru cups-1.5.3/debian/cups.prerm cups-1.5.3/debian/cups.prerm --- cups-1.5.3/debian/cups.prerm 2012-04-05 00:09:23.000000000 +0000 +++ cups-1.5.3/debian/cups.prerm 2012-07-10 20:58:01.000000000 +0000 @@ -19,7 +19,7 @@ case "$1" in remove) - (cd /usr/lib/cups/backend && rm -f http https ipp ipps lpd socket usb snmp dnssd mdns) + (cd /usr/lib/cups/backend && rm -f http https ipp ipp14 ipps lpd socket usb snmp dnssd mdns) ;; upgrade|deconfigure) ;; diff -Nru cups-1.5.3/debian/cups.templates cups-1.5.3/debian/cups.templates --- cups-1.5.3/debian/cups.templates 2012-04-05 00:09:23.000000000 +0000 +++ cups-1.5.3/debian/cups.templates 2012-07-10 20:58:01.000000000 +0000 @@ -19,8 +19,8 @@ Template: cupsys/backend Type: multiselect -__Choices: ipp, lpd, socket, usb, snmp, dnssd -Default: ipp, lpd, socket, usb, snmp, dnssd +__Choices: ipp, ipp14, lpd, socket, usb, snmp, dnssd +Default: ipp, ipp14, lpd, socket, usb, snmp, dnssd _Description: Printer communication backends: CUPS uses backend programs to communicate with the printer device or port. . diff -Nru cups-1.5.3/debian/local/blacklist-cups-usblp.conf cups-1.5.3/debian/local/blacklist-cups-usblp.conf --- cups-1.5.3/debian/local/blacklist-cups-usblp.conf 2012-05-23 13:35:30.000000000 +0000 +++ cups-1.5.3/debian/local/blacklist-cups-usblp.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# cups talks to the raw USB devices, so we need to blacklist usblp to avoid -# grabbing them -blacklist usblp diff -Nru cups-1.5.3/debian/patches/add-ipp-backend-of-cups-1.4.patch cups-1.5.3/debian/patches/add-ipp-backend-of-cups-1.4.patch --- cups-1.5.3/debian/patches/add-ipp-backend-of-cups-1.4.patch 1970-01-01 00:00:00.000000000 +0000 +++ cups-1.5.3/debian/patches/add-ipp-backend-of-cups-1.4.patch 2012-07-10 20:58:01.000000000 +0000 @@ -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 ++#include "backend-private.h" ++#include ++#include ++#include ++ ++/* ++ * 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 -Nru cups-1.5.3/debian/patches/series cups-1.5.3/debian/patches/series --- cups-1.5.3/debian/patches/series 2012-05-23 13:38:08.000000000 +0000 +++ cups-1.5.3/debian/patches/series 2012-07-10 21:02:02.000000000 +0000 @@ -4,6 +4,7 @@ usb-backend-busy-loop-fix.patch usb-backend-detach-usblp-earlier-crash-guards.patch usb-backend-initialize-usblp-attached-state.patch +usb-backend-further-enhancements.patch pidfile.patch ppd-poll-with-client-conf.patch # no answer yet, po4a might not be appropriate @@ -26,6 +27,7 @@ configure-default-browse-protocols.patch # Debian patches +add-ipp-backend-of-cups-1.4.patch logfiles_adm_readable.patch default_log_settings.patch confdirperms.patch diff -Nru cups-1.5.3/debian/patches/usb-backend-further-enhancements.patch cups-1.5.3/debian/patches/usb-backend-further-enhancements.patch --- cups-1.5.3/debian/patches/usb-backend-further-enhancements.patch 1970-01-01 00:00:00.000000000 +0000 +++ cups-1.5.3/debian/patches/usb-backend-further-enhancements.patch 2012-07-10 21:14:47.000000000 +0000 @@ -0,0 +1,612 @@ +--- a/backend/usb-libusb.c ++++ b/backend/usb-libusb.c +@@ -13,7 +13,7 @@ + * + * Contents: + * +- * list_devices() - List the available printers. ++ * list_devices() - List the available printers. + * print_device() - Print a file to a USB device. + * close_device() - Close the connection to the USB printer. + * find_device() - Find or enumerate USB printers. +@@ -22,6 +22,9 @@ + * make_device_uri() - Create a device URI for a USB printer. + * open_device() - Open a connection to the USB printer. + * print_cb() - Find a USB printer for printing. ++ * printer_class_soft_reset()' - Do the soft reset request specific to ++ * printers ++ * quirks() - Get the known quirks of a given printer model + * read_thread() - Thread to read the backchannel data on. + * sidechannel_thread() - Handle side-channel requests. + * soft_reset() - Send a soft reset to the device. +@@ -60,13 +63,15 @@ + { + struct libusb_device *device; /* Device info */ + int conf, /* Configuration */ ++ origconf, /* Original configuration */ + iface, /* Interface */ + altset, /* Alternate setting */ + write_endp, /* Write endpoint */ +- read_endp, /* Read endpoint */ ++ read_endp, /* Read endpoint */ + protocol, /* Protocol: 1 = Uni-di, 2 = Bi-di. */ +- usblp_attached; /* Is the "usblp" kernel module +- attached? */ ++ usblp_attached, /* "usblp" kernel module attached? */ ++ opened_for_job; /* Set to 1 by print_device() */ ++ unsigned int quirks; /* Quirks flags */ + struct libusb_device_handle *handle; /* Open handle to device */ + } usb_printer_t; + +@@ -99,6 +104,55 @@ + int sidechannel_thread_done; + } usb_globals_t; + ++/* ++ * Quirks: various printer quirks are handled by this table & its flags. ++ * ++ * This is copied from the usblp kernel module. So we can easily copy and paste ++ * new quirks from the module. ++ */ ++ ++struct quirk_printer_struct { ++ int vendorId; ++ int productId; ++ unsigned int quirks; ++}; ++ ++#define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires ++ unidirectional mode (no INs/reads) */ ++#define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */ ++#define USBLP_QUIRK_BAD_CLASS 0x4 /* descriptor uses vendor-specific ++ Class or SubClass */ ++#define USBLP_QUIRK_NO_REATTACH 0x8000 /* After printing we cannot re-attach ++ the usblp kernel module */ ++ ++static const struct quirk_printer_struct quirk_printers[] = { ++ { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */ ++ { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */ ++ { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */ ++ { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */ ++ { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */ ++ { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */ ++ { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */ ++ { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */ ++ { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */ ++ { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */ ++ { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */ ++ { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ ++ { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ ++ { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, ++ by zut */ ++ { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR | ++ USBLP_QUIRK_NO_REATTACH }, /* Brother Industries, Ltd ++ HL-1440 Laser Printer */ ++ { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt ++ Printer M129C */ ++ { 0x067b, 0x2305, USBLP_QUIRK_BIDIR | ++ USBLP_QUIRK_NO_REATTACH }, ++ /* Prolific Technology, Inc. PL2305 Parallel Port ++ (USB -> Parallel adapter) */ ++ { 0, 0 } ++}; ++ + + /* + * Globals... +@@ -124,6 +178,8 @@ + static int open_device(usb_printer_t *printer, int verbose); + static int print_cb(usb_printer_t *printer, const char *device_uri, + const char *device_id, const void *data); ++static int printer_class_soft_reset(usb_printer_t *printer); ++static unsigned int quirks(int vendor, int product); + static void *read_thread(void *reference); + static void *sidechannel_thread(void *reference); + static void soft_reset(void); +@@ -163,7 +219,8 @@ + iostatus; /* Current IO status */ + pthread_t read_thread_id, /* Read thread */ + sidechannel_thread_id; /* Side-channel thread */ +- int have_sidechannel = 0; /* Was the side-channel thread started? */ ++ int have_sidechannel = 0, /* Was the side-channel thread started? */ ++ have_backchannel = 0; /* Do we have a back channel? */ + struct stat sidechannel_info; /* Side-channel file descriptor info */ + unsigned char print_buffer[8192], /* Print data buffer */ + *print_ptr; /* Pointer into print data buffer */ +@@ -172,6 +229,9 @@ + struct timeval *timeout, /* Timeout pointer */ + tv; /* Time value */ + struct timespec cond_timeout; /* pthread condition timeout */ ++ int num_opts; /* Number of options */ ++ cups_option_t *opts; /* Options */ ++ const char *val; /* Option value */ + + + /* +@@ -187,6 +247,7 @@ + * Connect to the printer... + */ + ++ fprintf(stderr, "DEBUG: Printing on printer with URI: %s\n", uri); + while ((g.printer = find_device(print_cb, uri)) == NULL) + { + _cupsLangPrintFilter(stderr, "INFO", +@@ -195,6 +256,7 @@ + } + + g.print_fd = print_fd; ++ g.printer->opened_for_job = 1; + + /* + * If we are printing data from a print driver on stdin, ignore SIGTERM +@@ -240,24 +302,61 @@ + } + + /* +- * Get the read thread going... ++ * Debug mode: If option "usb-unidir" is given, always deactivate ++ * backchannel ++ */ ++ ++ num_opts = cupsParseOptions(argv[5], 0, &opts); ++ val = cupsGetOption("usb-unidir", num_opts, opts); ++ if (val && strcasecmp(val, "no") && strcasecmp(val, "off") && ++ strcasecmp(val, "false")) ++ { ++ g.printer->read_endp = -1; ++ fprintf(stderr, "DEBUG: Forced uni-directional communication " ++ "via \"usb-unidir\" option.\n"); ++ } ++ ++ /* ++ * Debug mode: If option "usb-no-reattach" is given, do not re-attach ++ * the usblp kernel module after the job has completed. + */ + +- g.read_thread_stop = 0; +- g.read_thread_done = 0; ++ val = cupsGetOption("usb-no-reattach", num_opts, opts); ++ if (val && strcasecmp(val, "no") && strcasecmp(val, "off") && ++ strcasecmp(val, "false")) ++ { ++ g.printer->usblp_attached = 0; ++ fprintf(stderr, "DEBUG: Forced not re-attaching the usblp kernel module " ++ "after the job via \"usb-no-reattach\" option.\n"); ++ } + +- pthread_cond_init(&g.read_thread_cond, NULL); +- pthread_mutex_init(&g.read_thread_mutex, NULL); ++ /* ++ * Get the read thread going... ++ */ + +- if (pthread_create(&read_thread_id, NULL, read_thread, NULL)) ++ if (g.printer->read_endp != -1) + { +- fprintf(stderr, "DEBUG: Fatal USB error.\n"); +- _cupsLangPrintFilter(stderr, "ERROR", +- _("There was an unrecoverable USB error.")); +- fputs("DEBUG: Couldn't create read thread.\n", stderr); +- close_device(g.printer); +- return (CUPS_BACKEND_STOP); ++ have_backchannel = 1; ++ ++ g.read_thread_stop = 0; ++ g.read_thread_done = 0; ++ ++ pthread_cond_init(&g.read_thread_cond, NULL); ++ pthread_mutex_init(&g.read_thread_mutex, NULL); ++ ++ if (pthread_create(&read_thread_id, NULL, read_thread, NULL)) ++ { ++ fprintf(stderr, "DEBUG: Fatal USB error.\n"); ++ _cupsLangPrintFilter(stderr, "ERROR", ++ _("There was an unrecoverable USB error.")); ++ fputs("DEBUG: Couldn't create read thread.\n", stderr); ++ close_device(g.printer); ++ return (CUPS_BACKEND_STOP); ++ } + } ++ else ++ fprintf(stderr, "DEBUG: Uni-directional device/mode, back channel " ++ "deactivated.\n"); + + /* + * The main thread sends the print file... +@@ -515,50 +614,54 @@ + * Signal the read thread to exit then wait 7 seconds for it to complete... + */ + +- g.read_thread_stop = 1; +- +- pthread_mutex_lock(&g.read_thread_mutex); +- +- if (!g.read_thread_done) ++ if (have_backchannel) + { +- fputs("DEBUG: Waiting for read thread to exit...\n", stderr); ++ g.read_thread_stop = 1; + +- gettimeofday(&tv, NULL); +- cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY; +- cond_timeout.tv_nsec = tv.tv_usec * 1000; +- +- while (!g.read_thread_done) +- { +- if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, +- &cond_timeout) != 0) +- break; +- } ++ pthread_mutex_lock(&g.read_thread_mutex); + +- /* +- * If it didn't exit abort the pending read and wait an additional second... +- */ +- + if (!g.read_thread_done) + { +- fputs("DEBUG: Read thread still active, aborting the pending read...\n", +- stderr); +- +- g.wait_eof = 0; ++ fputs("DEBUG: Waiting for read thread to exit...\n", stderr); + + gettimeofday(&tv, NULL); +- cond_timeout.tv_sec = tv.tv_sec + 1; ++ cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY; + cond_timeout.tv_nsec = tv.tv_usec * 1000; +- ++ + while (!g.read_thread_done) + { + if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, + &cond_timeout) != 0) + break; + } ++ ++ /* ++ * If it didn't exit abort the pending read and wait an additional ++ * second... ++ */ ++ ++ if (!g.read_thread_done) ++ { ++ fputs("DEBUG: Read thread still active, aborting the pending read...\n", ++ stderr); ++ ++ g.wait_eof = 0; ++ ++ gettimeofday(&tv, NULL); ++ cond_timeout.tv_sec = tv.tv_sec + 1; ++ cond_timeout.tv_nsec = tv.tv_usec * 1000; ++ ++ while (!g.read_thread_done) ++ { ++ if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex, ++ &cond_timeout) != 0) ++ break; ++ } ++ } + } +- } + +- pthread_mutex_unlock(&g.read_thread_mutex); ++ pthread_mutex_unlock(&g.read_thread_mutex); ++ } + + if (print_fd) + close(print_fd); +@@ -601,24 +704,54 @@ + */ + + int errcode; /* Return value of libusb function */ +- int number; /* Interface number */ ++ int number1, /* Interface number */ ++ number2; /* Configuration number */ + +- errcode = +- libusb_get_config_descriptor (printer->device, printer->conf, &confptr); ++ errcode = ++ libusb_get_config_descriptor(printer->device, printer->conf, &confptr); + if (errcode >= 0) + { +- number = confptr->interface[printer->iface]. ++ number1 = confptr->interface[printer->iface]. + altsetting[printer->altset].bInterfaceNumber; +- libusb_release_interface(printer->handle, number); +- if (number != 0) +- libusb_release_interface(printer->handle, 0); ++ libusb_release_interface(printer->handle, number1); ++ ++ number2 = confptr->bConfigurationValue; ++ ++ libusb_free_config_descriptor(confptr); ++ ++ /* ++ * If we have changed the configuration from one valid configuration ++ * to another, restore the old one ++ */ ++ if (printer->origconf > 0 && printer->origconf != number2) ++ { ++ fprintf(stderr, "DEBUG: Restoring USB device configuration: %d -> %d\n", ++ number2, printer->origconf); ++ if ((errcode = libusb_set_configuration(printer->handle, ++ printer->origconf)) < 0) ++ { ++ if (errcode != LIBUSB_ERROR_BUSY) ++ { ++ errcode = ++ libusb_get_device_descriptor (printer->device, &devdesc); ++ if (errcode < 0) ++ fprintf(stderr, ++ "DEBUG: Failed to set configuration %d\n", ++ printer->origconf); ++ else ++ fprintf(stderr, ++ "DEBUG: Failed to set configuration %d for %04x:%04x\n", ++ printer->origconf, devdesc.idVendor, devdesc.idProduct); ++ } ++ } ++ } + + /* + * Re-attach "usblp" kernel module if it was attached before using this + * device + */ + if (printer->usblp_attached == 1) +- if (libusb_attach_kernel_driver(printer->handle, printer->iface) < 0) ++ if (libusb_attach_kernel_driver(printer->handle, number1) < 0) + { + errcode = libusb_get_device_descriptor (printer->device, &devdesc); + if (errcode < 0) +@@ -629,8 +762,25 @@ + "DEBUG: Failed to re-attach \"usblp\" kernel module to " + "%04x:%04x\n", devdesc.idVendor, devdesc.idProduct); + } ++ } ++ else ++ fprintf(stderr, ++ "DEBUG: Failed to get configuration descriptor %d\n", ++ printer->conf); + +- libusb_free_config_descriptor(confptr); ++ /* ++ * Reset the device to clean up after the job ++ */ ++ ++ if (printer->opened_for_job == 1) ++ { ++ if ((errcode = libusb_reset_device(printer->handle)) < 0) ++ fprintf(stderr, ++ "DEBUG: Device reset failed, error code: %d\n", ++ errcode); ++ else ++ fprintf(stderr, ++ "DEBUG: Resetting printer.\n"); + } + + /* +@@ -702,16 +852,18 @@ + * a printer... + */ + +- if (libusb_get_device_descriptor (device, &devdesc) < 0) ++ if (libusb_get_device_descriptor(device, &devdesc) < 0) + continue; + + if (!devdesc.bNumConfigurations || !devdesc.idVendor || + !devdesc.idProduct) + continue; + ++ printer.quirks = quirks(devdesc.idVendor, devdesc.idProduct); ++ + for (conf = 0; conf < devdesc.bNumConfigurations; conf ++) + { +- if (libusb_get_config_descriptor (device, conf, &confptr) < 0) ++ if (libusb_get_config_descriptor(device, conf, &confptr) < 0) + continue; + for (iface = 0, ifaceptr = confptr->interface; + iface < confptr->bNumInterfaces; +@@ -733,13 +885,18 @@ + * 1284.4 (packet mode) protocol as well. + */ + +- if (altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER || +- altptr->bInterfaceSubClass != 1 || ++ if (((altptr->bInterfaceClass != LIBUSB_CLASS_PRINTER || ++ altptr->bInterfaceSubClass != 1) && ++ ((printer.quirks & USBLP_QUIRK_BAD_CLASS) == 0)) || + (altptr->bInterfaceProtocol != 1 && /* Unidirectional */ + altptr->bInterfaceProtocol != 2) || /* Bidirectional */ + altptr->bInterfaceProtocol < protocol) + continue; + ++ if (printer.quirks & USBLP_QUIRK_BAD_CLASS) ++ fprintf(stderr, "DEBUG: Printer does not report class 7 and/or " ++ "subclass 1 but works as a printer anyway\n"); ++ + read_endp = -1; + write_endp = -1; + +@@ -764,7 +921,10 @@ + protocol = altptr->bInterfaceProtocol; + printer.altset = altset; + printer.write_endp = write_endp; +- printer.read_endp = read_endp; ++ if (protocol > 1) ++ printer.read_endp = read_endp; ++ else ++ printer.read_endp = -1; + } + } + +@@ -782,16 +942,41 @@ + make_device_uri(&printer, device_id, device_uri, + sizeof(device_uri)); + ++ fprintf(stderr, "DEBUG2: Printer found with device ID: %s " ++ "Device URI: %s\n", ++ device_id, device_uri); ++ + if ((*cb)(&printer, device_uri, device_id, data)) + { +- printer.read_endp = confptr->interface[printer.iface]. +- altsetting[printer.altset]. +- endpoint[printer.read_endp]. +- bEndpointAddress; ++ fprintf(stderr, "DEBUG: Device protocol: %d\n", ++ printer.protocol); ++ if (printer.quirks & USBLP_QUIRK_BIDIR) ++ { ++ printer.read_endp = -1; ++ fprintf(stderr, "DEBUG: Printer reports bi-di support " ++ "but in reality works only uni-directionally\n"); ++ } ++ if (printer.read_endp != -1) ++ { ++ printer.read_endp = confptr->interface[printer.iface]. ++ altsetting[printer.altset]. ++ endpoint[printer.read_endp]. ++ bEndpointAddress; ++ } ++ else ++ fprintf(stderr, "DEBUG: Uni-directional USB communication " ++ "only!\n"); + printer.write_endp = confptr->interface[printer.iface]. + altsetting[printer.altset]. + endpoint[printer.write_endp]. + bEndpointAddress; ++ if (printer.quirks & USBLP_QUIRK_NO_REATTACH) ++ { ++ printer.usblp_attached = 0; ++ fprintf(stderr, "DEBUG: Printer does not like usblp " ++ "kernel module to be re-attached after job\n"); ++ } ++ libusb_free_config_descriptor(confptr); + return (&printer); + } + +@@ -959,7 +1144,7 @@ + if ((sern = cupsGetOption("SERIALNUMBER", num_values, values)) == NULL) + if ((sern = cupsGetOption("SERN", num_values, values)) == NULL) + if ((sern = cupsGetOption("SN", num_values, values)) == NULL && +- ((libusb_get_device_descriptor (printer->device, &devdesc) >= 0) && ++ ((libusb_get_device_descriptor(printer->device, &devdesc) >= 0) && + devdesc.iSerialNumber)) + { + /* +@@ -1095,15 +1280,20 @@ + * Try opening the printer... + */ + +- if (libusb_open(printer->device, &printer->handle) < 0) ++ if ((errcode = libusb_open(printer->device, &printer->handle)) < 0) ++ { ++ fprintf(stderr, "DEBUG: Failed to open device, code: %d\n", ++ errcode); + return (-1); ++ } + + printer->usblp_attached = 0; ++ printer->opened_for_job = 0; + + if (verbose) + fputs("STATE: +connecting-to-device\n", stderr); + +- if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0) ++ if ((errcode = libusb_get_device_descriptor(printer->device, &devdesc)) < 0) + { + fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n", + errcode); +@@ -1151,6 +1341,8 @@ + 0, 0, (unsigned char *)¤t, 1, 5000) < 0) + current = 0; /* Assume not configured */ + ++ printer->origconf = current; ++ + if ((errcode = + libusb_get_config_descriptor (printer->device, printer->conf, &confptr)) + < 0) +@@ -1163,6 +1355,8 @@ + + if (number1 != current) + { ++ fprintf(stderr, "DEBUG: Switching USB device configuration: %d -> %d\n", ++ current, number1); + if ((errcode = libusb_set_configuration(printer->handle, number1)) < 0) + { + /* +@@ -1342,6 +1536,64 @@ + + + /* ++ * 'printer_class_soft_reset()' - Do the soft reset request specific to printers ++ * ++ * This soft reset is specific to the printer device class and is much less ++ * invasive than the general USB reset libusb_reset_device(). Especially it ++ * does never happen that the USB addressing and configuration changes. What ++ * is actually done is that all buffers get flushed and the bulk IN and OUT ++ * pipes get reset to their default states. This clears all stall conditions. ++ * See http://cholla.mmto.org/computers/linux/usb/usbprint11.pdf ++ */ ++ ++static int /* O - 0 on success, < 0 on error */ ++printer_class_soft_reset(usb_printer_t *printer) /* I - Printer */ ++{ ++ struct libusb_config_descriptor *confptr = NULL; ++ /* Pointer to current configuration */ ++ int interface, ++ errcode; ++ ++ if (libusb_get_config_descriptor(printer->device, printer->conf, &confptr) ++ < 0) ++ interface = printer->iface; ++ else ++ interface = confptr->interface[printer->iface]. ++ altsetting[printer->altset].bInterfaceNumber; ++ libusb_free_config_descriptor(confptr); ++ if ((errcode = libusb_control_transfer(printer->handle, ++ LIBUSB_REQUEST_TYPE_CLASS | ++ LIBUSB_ENDPOINT_OUT | ++ LIBUSB_RECIPIENT_OTHER, ++ 2, 0, interface, NULL, 0, 5000)) < 0) ++ errcode = libusb_control_transfer(printer->handle, ++ LIBUSB_REQUEST_TYPE_CLASS | ++ LIBUSB_ENDPOINT_OUT | ++ LIBUSB_RECIPIENT_INTERFACE, ++ 2, 0, interface, NULL, 0, 5000); ++ return errcode; ++} ++ ++ ++/* ++ * 'quirks()' - Get the known quirks of a given printer model ++ */ ++ ++static unsigned int quirks(int vendor, int product) ++{ ++ int i; ++ ++ for (i = 0; quirk_printers[i].vendorId; i++) ++ { ++ if (vendor == quirk_printers[i].vendorId && ++ product == quirk_printers[i].productId) ++ return quirk_printers[i].quirks; ++ } ++ return 0; ++} ++ ++ ++/* + * 'read_thread()' - Thread to read the backchannel data on. + */ + +@@ -1615,7 +1867,7 @@ + * Send the reset... + */ + +- libusb_reset_device (g.printer->handle); ++ printer_class_soft_reset(g.printer); + + /* + * Release the I/O lock...