[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#683730: unblock: cups/1.5.3-2+wheezy0



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: freeze-exception

Dear Release Team,

I would like to request a freeze exception for cups. As was highlighted
by Martin Pitt [0], there is noone really maintaining Cups in Debian, so
here I am with my "Printing Team" hat with proposed changes for Cups in
Wheezy.

* The version would of this updated package would be 1.5.3-2+wheezy0 as
  experimental already has 1.5.3-4.

* The bazaar repository has the proposed changes in the "debian-wheezy"
  branch there:

  http://anonscm.debian.org/loggerhead/pkg-cups/cups/debian-wheezy/changes

* There are 5 commits above 1.5.3-2 (currently in unstable) to mimick 4
  commits above 1.5.3-1 (currently in wheezy). All of them have been
  justified by Till Kamppeter (our openprinting guy I trust fully) in
  [1]:

  - 1165-Revert-all-1.5.3-2-changes.patch
    This reduces the diff between Wheezy and the proposed package to
    only the changelog.

  - 1166-Add-the-IPP-backend-of-CUPS-1.4-as-ipp14.patch
    This adds the "old" ipp backend from CUPS 1.4 which was in testing
    last in Sep 2011 [2]. Quoting Till from [1]:

    "Addition of the ipp14 backend: The ipp14 backend is nothing else
     that the IPP backend of CUPS 1.4.x, which is code which has been in
     Debian for longer time already. I have re-added it but to not drop
     the new backend of 1.5.x with its new fixes and better standards
     compliance, I have added the old code as ipp14 backend. This
     backend will never be used automatically so regressions are not
     possible, but for the case of the new backend causing problems the
     user can switch to ipp14 manually. Changes are
     debian/patches/add-ipp-backend-of-cups-1.4.patch with the code and
     the Makefile entries and addition of an "ipp14" entry in Debconf.

     This avoids regressions by incompatibilities of the new ipp backend
     with some devices (especially LiveBox router)."

    For convenience, I also attach add-ipp-backend-of-cups-1.4.patch.

  - 1167-Add-latest-libusb-enhancements.patch
    This adds the changes developped by Till on Cups's libusb backend.
    These have been accepted upstream as r10543 on their SVN, see [3].

    Quoting Till from [1] again: 

    "Improvements of the USB backend: These improvements fix several
     compatibility problems of the new libusb-based USB CUPS backend,
     especially it adds support for uni-directional devices, quirk
     management for devices with incompatibilities, and more.
     (…)
     This fix solves many problems of the original libusb-based USB
     backend, especially one does not need to blacklist the usblp kernel
     module to work around some of these problems."

    For convenience, I also attach the usb-backend-further-enhancements.patch.

  - 1168-Lift-the-usblp-blacklist.patch
    Given the fixes above, the usblp blacklist is not needed anymore and
    in fact brings some problems in other packages (as escputil,
    #681851, …).

  - 1169-releasing-version-1.5.3-2+wheezy0.patch
    Well, …, releasing.

The above changes have not been uploaded yet and I wouldn't mind a
longer time in unstable for cups if that's needed.

Ah, the full debdiff is also attached with the following diffstat:

 changelog                                      |   81 +
 control                                        |    2 
 cups.config                                    |    2 
 cups.install                                   |    1 
 cups.lintian-overrides                         |    1 
 cups.maintscript                               |    1 
 cups.postinst                                  |   13 
 cups.prerm                                     |    2 
 cups.templates                                 |    2 
 local/blacklist-cups-usblp.conf                |    3 
 patches/add-ipp-backend-of-cups-1.4.patch      | 2002 +++++++++++++++++++++++++
 patches/series                                 |    2 
 patches/usb-backend-further-enhancements.patch |  661 ++++++++
 13 files changed, 2761 insertions(+), 12 deletions(-)

Many thanks in advance for your review, cheers,

OdyX
 
[0] https://lists.debian.org/20120721144148.GA2614@piware.de
[1] https://lists.debian.org/500EC4B2.1090205@gmail.com
[2] http://packages.qa.debian.org/c/cups/news/20110902T163912Z.html
[3] http://cups.org/str.php?L4128
=== modified file 'debian/changelog'
--- old/debian/changelog	2012-06-19 10:32:57 +0000
+++ new/debian/changelog	2012-08-02 16:35:09 +0000
@@ -1,3 +1,9 @@
+cups (1.5.3-2) UNRELEASED; urgency=low
+
+  * Revert all 1.5.3-2 changes.
+
+ -- Didier Raboud <odyx@debian.org>  Thu, 02 Aug 2012 09:31:43 +0200
+
 cups (1.5.3-2) unstable; urgency=low
 
   [ Till Kamppeter ]

=== modified file 'debian/control'
--- old/debian/control	2012-06-19 10:01:21 +0000
+++ new/debian/control	2012-08-02 16:35:09 +0000
@@ -162,7 +162,6 @@
  lsb-base (>= 3),
  cups-common (>= ${source:Upstream-Version}), 
  cups-client (>= ${binary:Version}),
- libcups2 (>= ${binary:Version}),
  ssl-cert (>= 1.0.11),
  adduser,
  bc,
@@ -198,7 +197,6 @@
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}, 
  cups-common (>= ${source:Upstream-Version}),
- libcups2 (= ${binary:Version}), 
  adduser
 Recommends: smbclient
 Suggests: cups, xpp, cups-bsd

=== modified file 'debian/libcups2-dev.install'
--- old/debian/libcups2-dev.install	2012-06-16 10:20:18 +0000
+++ new/debian/libcups2-dev.install	2012-08-02 16:35:09 +0000
@@ -14,6 +14,6 @@
 usr/include/cups/sidechannel.h
 usr/include/cups/transcode.h
 usr/include/cups/versioning.h
-#../../cups/language-private.h usr/include/cups/i18n.h
+../../cups/language-private.h usr/include/cups/i18n.h
 usr/share/man/man1/cups-config.1.gz
 usr/share/man/*/man1/cups-config.1.gz

=== modified file 'debian/libcupsppdc1.symbols'
--- old/debian/libcupsppdc1.symbols	2012-06-12 07:27:30 +0000
+++ new/debian/libcupsppdc1.symbols	2012-08-02 16:35:09 +0000
@@ -30,7 +30,6 @@
  _ZN10ppdcFilterD0Ev@Base 1.4.0
  _ZN10ppdcFilterD1Ev@Base 1.4.0
  _ZN10ppdcFilterD2Ev@Base 1.4.0
- (optional)_ZN10ppdcOption10add_choiceEP10ppdcChoice@Base 1.5.3
  _ZN10ppdcOption10class_nameEv@Base 1.4.0
  _ZN10ppdcOption11find_choiceEPKc@Base 1.4.0
  _ZN10ppdcOption13set_defchoiceEP10ppdcChoice@Base 1.4.0
@@ -169,7 +168,6 @@
  _ZN9ppdcArrayD0Ev@Base 1.4.0
  _ZN9ppdcArrayD1Ev@Base 1.4.0
  _ZN9ppdcArrayD2Ev@Base 1.4.0
- (optional)_ZN9ppdcGroup10add_optionEP10ppdcOption@Base 1.5.3
  _ZN9ppdcGroup10class_nameEv@Base 1.4.0
  _ZN9ppdcGroup11find_optionEPKc@Base 1.4.0
  _ZN9ppdcGroupC1EPKcS1_@Base 1.4.0

=== modified file 'debian/local/apparmor-profile'
--- old/debian/local/apparmor-profile	2012-06-06 15:48:45 +0000
+++ new/debian/local/apparmor-profile	2012-08-02 16:35:09 +0000
@@ -154,8 +154,6 @@
   capability dac_override,
   capability dac_read_search,
 
-  @{PROC}/*/auxv r,
-
   /bin/dash ixr,
   /bin/bash ixr,
   /bin/cp ixr,

=== modified file 'debian/patches/series'
--- old/debian/patches/series	2012-05-23 16:02:42 +0000
+++ new/debian/patches/series	2012-08-02 16:35:09 +0000
@@ -1,5 +1,4 @@
 # patches accepted and committed upstream:
-install-sh-remove-bashism.patch
 
 # patches sent upstream
 usb-backend-busy-loop-fix.patch
@@ -14,6 +13,7 @@
 colord-support.patch
 
 # patches which should go upstream
+install-sh-remove-bashism.patch
 airprint-support.patch
 removecvstag.patch
 no-conffile-timestamp.patch

=== modified file 'debian/rules'
--- old/debian/rules	2012-06-16 10:20:18 +0000
+++ new/debian/rules	2012-08-02 16:35:09 +0000
@@ -149,8 +149,6 @@
 binary-post-install/libcups2-dev::
 	rm -f debian/$(cdbs_curpkg)/usr/share/doc/$(cdbs_curpkg)/examples/scripting/php/*.o
 	rm -f debian/$(cdbs_curpkg)/usr/share/doc/$(cdbs_curpkg)/examples/scripting/php/*.so
-	# debian/libcups2-dev.install entry cannot rename files on-the-fly
-	cp cups/language-private.h debian/$(cdbs_curpkg)/usr/include/cups/i18n.h
 binary-post-install/cups-client::
 	rm -r debian/cups-client/usr/share/doc/cups-client
 	ln -s libcups2 debian/cups-client/usr/share/doc/cups-client

=== modified file 'debian/changelog'
--- old/debian/changelog	2012-08-02 16:35:09 +0000
+++ new/debian/changelog	2012-08-02 16:37:24 +0000
@@ -1,7 +1,16 @@
-cups (1.5.3-2) UNRELEASED; urgency=low
+cups (1.5.3-2+wheezy0) UNRELEASED; urgency=low
 
+  [ Didier Raboud ]
   * Revert all 1.5.3-2 changes.
 
+  [ Till Kamppeter ]
+  * 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) do not work with the current IPP backend (LP: #945028,
+    LP: #973270, LP: #990734, LP: #992468, LP: #992982).
+
  -- Didier Raboud <odyx@debian.org>  Thu, 02 Aug 2012 09:31:43 +0200
 
 cups (1.5.3-2) unstable; urgency=low

=== modified file 'debian/cups.config'
--- old/debian/cups.config	2008-05-23 08:14:05 +0000
+++ new/debian/cups.config	2012-08-02 16:37:24 +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
 

=== modified file 'debian/cups.lintian-overrides'
--- old/debian/cups.lintian-overrides	2012-01-27 23:47:09 +0000
+++ new/debian/cups.lintian-overrides	2012-08-02 16:37:24 +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

=== modified file 'debian/cups.postinst'
--- old/debian/cups.postinst	2012-05-23 12:31:13 +0000
+++ new/debian/cups.postinst	2012-08-02 16:37:24 +0000
@@ -79,9 +79,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-2+wheezy0"; 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 +104,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

=== modified file 'debian/cups.prerm'
--- old/debian/cups.prerm	2012-02-06 21:45:21 +0000
+++ new/debian/cups.prerm	2012-08-02 16:37:24 +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)
         ;;

=== modified file 'debian/cups.templates'
--- old/debian/cups.templates	2012-01-27 23:47:09 +0000
+++ new/debian/cups.templates	2012-08-02 16:37:24 +0000
@@ -19,7 +19,7 @@
 
 Template: cupsys/backend
 Type: multiselect
-__Choices: ipp, lpd, socket, usb, snmp, dnssd
+__Choices: ipp, ipp14, lpd, socket, usb, snmp, dnssd
 Default: ipp, lpd, socket, usb, snmp, dnssd
 _Description: Printer communication backends:
  CUPS uses backend programs to communicate with the printer device or port.

=== added file 'debian/patches/add-ipp-backend-of-cups-1.4.patch'
--- old/debian/patches/add-ipp-backend-of-cups-1.4.patch	1970-01-01 00:00:00 +0000
+++ new/debian/patches/add-ipp-backend-of-cups-1.4.patch	2012-08-02 16:37:24 +0000
@@ -0,0 +1,2002 @@
+Description: Add the IPP backend of CUPS 1.4.x to the current CUPS
+ package as independent backend "ipp14". Some devices (like the
+ LiveBox2) do not work with the current IPP backend.
+Author: Till Kampetter <till.kampetter@gmail.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/945028
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/973270
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/990734
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/992468
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/992982
+Last-Update: 2012-07-27
+
+--- 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 $".
++ */

=== modified file 'debian/patches/series'
--- old/debian/patches/series	2012-08-02 16:35:09 +0000
+++ new/debian/patches/series	2012-08-02 16:37:24 +0000
@@ -26,6 +26,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

=== modified file 'debian/changelog'
--- old/debian/changelog	2012-08-02 16:37:24 +0000
+++ new/debian/changelog	2012-08-02 16:38:48 +0000
@@ -10,6 +10,47 @@
     CUPS package as independent backend "ipp14". Some devices (like the
     LiveBox 2) do not work with the current IPP backend (LP: #945028,
     LP: #973270, LP: #990734, LP: #992468, LP: #992982).
+  * 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.
+     - 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, perhaps also LP: #995111).
+     - 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.
+     - debian/patches/usb-backend-further-enhancements.patch: USB backend: 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.
 
  -- Didier Raboud <odyx@debian.org>  Thu, 02 Aug 2012 09:31:43 +0200
 

=== modified file 'debian/patches/series'
--- old/debian/patches/series	2012-08-02 16:37:24 +0000
+++ new/debian/patches/series	2012-08-02 16:38:48 +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

=== added file 'debian/patches/usb-backend-further-enhancements.patch'
--- old/debian/patches/usb-backend-further-enhancements.patch	1970-01-01 00:00:00 +0000
+++ new/debian/patches/usb-backend-further-enhancements.patch	2012-08-02 16:38:48 +0000
@@ -0,0 +1,661 @@
+Description: 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.
+   - 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, perhaps also LP: #995111).
+   - 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.
+   - USB backend: 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.
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1001028
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1000253
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/987485
+Bug-Upstream: http://cups.org/str.php?L4128
+Author: Till Kamppeter <till.kamppeter@gmail.com>
+Last-Update: 2012-07-10
+
+--- 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 <kernel@zut.de> */
++	{ 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 *)&current, 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...

=== modified file 'debian/changelog'
--- old/debian/changelog	2012-08-02 16:38:48 +0000
+++ new/debian/changelog	2012-08-03 10:09:05 +0000
@@ -51,6 +51,9 @@
      - Some extra debug messages.
      - Added a missing libusb_free_config_descriptor().
     This patch is submitted upstream as CUPS STR #4128.
+  * debian/local/blacklist-cups-usblp.conf, debian/cups.maintscript,
+    debian/cups.install, debian/control: Lift the usblp blacklist, not
+    needed anymore with the above fixes (Closes: #630556, #635041).
 
  -- Didier Raboud <odyx@debian.org>  Thu, 02 Aug 2012 09:31:43 +0200
 

=== modified file 'debian/control'
--- old/debian/control	2012-08-02 16:35:09 +0000
+++ new/debian/control	2012-08-03 10:09:05 +0000
@@ -152,7 +152,7 @@
 Priority: optional
 Section: net
 Architecture: any
-Pre-Depends: dpkg (>= 1.15.7.2)
+Pre-Depends: ${misc:Pre-Depends}
 Depends: ${shlibs:Depends},
  ${misc:Depends},
  debconf (>= 1.2.9) | debconf-2.0,

=== modified file 'debian/cups.install'
--- old/debian/cups.install	2012-05-23 10:47:58 +0000
+++ new/debian/cups.install	2012-08-03 10:09:05 +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

=== added file 'debian/cups.maintscript'
--- old/debian/cups.maintscript	1970-01-01 00:00:00 +0000
+++ new/debian/cups.maintscript	2012-08-03 10:09:05 +0000
@@ -0,0 +1,1 @@
+rm_conffile /etc/modprobe.d/blacklist-cups-usblp.conf 1.5.3-2~

=== modified file 'debian/cups.postinst'
--- old/debian/cups.postinst	2012-08-02 16:37:24 +0000
+++ new/debian/cups.postinst	2012-08-03 10:09:05 +0000
@@ -175,8 +175,8 @@
 	fi
     fi
 
-    if dpkg --compare-versions "$2" lt "1.5.3"; then
-	rmmod usblp || :
+    if dpkg --compare-versions "$2" lt "1.5.3-2+wheezy0"; then
+        modprobe usblp >/dev/null 2>&1 || :
     fi
 fi
 

=== removed file 'debian/local/blacklist-cups-usblp.conf'
--- old/debian/local/blacklist-cups-usblp.conf	2012-05-23 10:47:58 +0000
+++ new/debian/local/blacklist-cups-usblp.conf	1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-# cups talks to the raw USB devices, so we need to blacklist usblp to avoid
-# grabbing them
-blacklist usblp

=== modified file 'debian/changelog'
--- old/debian/changelog	2012-08-03 10:09:05 +0000
+++ new/debian/changelog	2012-08-03 10:10:11 +0000
@@ -1,7 +1,8 @@
-cups (1.5.3-2+wheezy0) UNRELEASED; urgency=low
+cups (1.5.3-2+wheezy0) unstable; urgency=low
 
   [ Didier Raboud ]
   * Revert all 1.5.3-2 changes.
+  * Cherry-pick changes targetted at Wheezy.
 
   [ Till Kamppeter ]
   * debian/patches/add-ipp-backend-of-cups-1.4.patch, debian/cups.config,
@@ -55,7 +56,7 @@
     debian/cups.install, debian/control: Lift the usblp blacklist, not
     needed anymore with the above fixes (Closes: #630556, #635041).
 
- -- Didier Raboud <odyx@debian.org>  Thu, 02 Aug 2012 09:31:43 +0200
+ -- Didier Raboud <odyx@debian.org>  Fri, 03 Aug 2012 12:09:15 +0200
 
 cups (1.5.3-2) unstable; urgency=low
 

Description: Add the IPP backend of CUPS 1.4.x to the current CUPS
 package as independent backend "ipp14". Some devices (like the
 LiveBox2) do not work with the current IPP backend.
Author: Till Kampetter <till.kampetter@gmail.com>
Bug-Ubuntu: https://bugs.launchpad.net/bugs/945028
Bug-Ubuntu: https://bugs.launchpad.net/bugs/973270
Bug-Ubuntu: https://bugs.launchpad.net/bugs/990734
Bug-Ubuntu: https://bugs.launchpad.net/bugs/992468
Bug-Ubuntu: https://bugs.launchpad.net/bugs/992982
Last-Update: 2012-07-27

--- 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 $".
+ */
Description: 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.
   - 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, perhaps also LP: #995111).
   - 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.
   - USB backend: 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.
Bug-Ubuntu: https://bugs.launchpad.net/bugs/1001028
Bug-Ubuntu: https://bugs.launchpad.net/bugs/1000253
Bug-Ubuntu: https://bugs.launchpad.net/bugs/987485
Bug-Upstream: http://cups.org/str.php?L4128
Author: Till Kamppeter <till.kamppeter@gmail.com>
Last-Update: 2012-07-10

--- 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 <kernel@zut.de> */
+	{ 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 *)&current, 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...
diff -Nru cups-1.5.3/debian/changelog cups-1.5.3/debian/changelog
--- cups-1.5.3/debian/changelog	2012-05-23 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/changelog	2012-08-03 12:10:06.000000000 +0200
@@ -1,3 +1,84 @@
+cups (1.5.3-2+wheezy0) unstable; urgency=low
+
+  [ Didier Raboud ]
+  * Revert all 1.5.3-2 changes.
+  * Cherry-pick changes targetted at Wheezy.
+
+  [ Till Kamppeter ]
+  * 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) do not work with the current IPP backend (LP: #945028,
+    LP: #973270, LP: #990734, LP: #992468, LP: #992982).
+  * 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.
+     - 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, perhaps also LP: #995111).
+     - 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.
+     - debian/patches/usb-backend-further-enhancements.patch: USB backend: 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/local/blacklist-cups-usblp.conf, debian/cups.maintscript,
+    debian/cups.install, debian/control: Lift the usblp blacklist, not
+    needed anymore with the above fixes (Closes: #630556, #635041).
+
+ -- Didier Raboud <odyx@debian.org>  Fri, 03 Aug 2012 12:09:15 +0200
+
+cups (1.5.3-2) unstable; urgency=low
+
+  [ Till Kamppeter ]
+  * debian/libcups2-dev.install, debian/rules: Correctly install
+    language-private.h as /usr/include/cups/i18n.h, .install file entries
+    cannot rename files (LP: #1013470).
+
+  [ Martin Pitt ]
+  * debian/local/apparmor-profile: Allow cups-pdf to read /proc/*/auxv; it's
+    harmless information. (LP: #1009367)
+  * debian/control: Tighten cups' and cups-client's dependency to libcups2 to
+    current binary version. They use private symbols from the libraries which
+    the automatic dependencies from the .symbols files don't cover.
+    (Closes: #668662, #677180)
+
+  [ Andy Whitcroft ]
+  * debian/libcupsppdc1.symbols: add two optional symbols which are only
+    emitted on armel and armhf architectures.
+
+ -- Martin Pitt <mpitt@debian.org>  Tue, 19 Jun 2012 12:25:11 +0200
+
 cups (1.5.3-1) unstable; urgency=low
 
   [ Till Kamppeter ]
diff -Nru cups-1.5.3/debian/control cups-1.5.3/debian/control
--- cups-1.5.3/debian/control	2012-05-23 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/control	2012-08-02 22:46:17.000000000 +0200
@@ -152,7 +152,7 @@
 Priority: optional
 Section: net
 Architecture: any
-Pre-Depends: dpkg (>= 1.15.7.2)
+Pre-Depends: ${misc:Pre-Depends}
 Depends: ${shlibs:Depends},
  ${misc:Depends},
  debconf (>= 1.2.9) | debconf-2.0,
diff -Nru cups-1.5.3/debian/cups.config cups-1.5.3/debian/cups.config
--- cups-1.5.3/debian/cups.config	2012-05-23 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/cups.config	2012-08-02 18:35:29.000000000 +0200
@@ -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 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/cups.install	2012-08-02 22:39:20.000000000 +0200
@@ -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-05-23 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/cups.lintian-overrides	2012-08-02 18:35:29.000000000 +0200
@@ -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.maintscript cups-1.5.3/debian/cups.maintscript
--- cups-1.5.3/debian/cups.maintscript	1970-01-01 01:00:00.000000000 +0100
+++ cups-1.5.3/debian/cups.maintscript	2012-08-02 22:45:11.000000000 +0200
@@ -0,0 +1 @@
+rm_conffile /etc/modprobe.d/blacklist-cups-usblp.conf 1.5.3-2~
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 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/cups.postinst	2012-08-02 22:55:04.000000000 +0200
@@ -79,9 +79,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-2+wheezy0"; 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 +104,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 +175,8 @@
 	fi
     fi
 
-    if dpkg --compare-versions "$2" lt "1.5.3"; then
-	rmmod usblp || :
+    if dpkg --compare-versions "$2" lt "1.5.3-2+wheezy0"; then
+        modprobe usblp >/dev/null 2>&1 || :
     fi
 fi
 
diff -Nru cups-1.5.3/debian/cups.prerm cups-1.5.3/debian/cups.prerm
--- cups-1.5.3/debian/cups.prerm	2012-05-23 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/cups.prerm	2012-08-02 18:35:29.000000000 +0200
@@ -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-05-23 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/cups.templates	2012-08-02 18:35:29.000000000 +0200
@@ -19,7 +19,7 @@
 
 Template: cupsys/backend
 Type: multiselect
-__Choices: ipp, lpd, socket, usb, snmp, dnssd
+__Choices: ipp, ipp14, lpd, socket, usb, snmp, dnssd
 Default: ipp, 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 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/local/blacklist-cups-usblp.conf	1970-01-01 01:00:00.000000000 +0100
@@ -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 01:00:00.000000000 +0100
+++ cups-1.5.3/debian/patches/add-ipp-backend-of-cups-1.4.patch	2012-08-02 18:36:03.000000000 +0200
@@ -0,0 +1,2002 @@
+Description: Add the IPP backend of CUPS 1.4.x to the current CUPS
+ package as independent backend "ipp14". Some devices (like the
+ LiveBox2) do not work with the current IPP backend.
+Author: Till Kampetter <till.kampetter@gmail.com>
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/945028
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/973270
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/990734
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/992468
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/992982
+Last-Update: 2012-07-27
+
+--- 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 -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 14:49:38.000000000 +0200
+++ cups-1.5.3/debian/patches/series	2012-08-02 18:38:31.000000000 +0200
@@ -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 01:00:00.000000000 +0100
+++ cups-1.5.3/debian/patches/usb-backend-further-enhancements.patch	2012-08-02 18:37:45.000000000 +0200
@@ -0,0 +1,661 @@
+Description: 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.
+   - 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, perhaps also LP: #995111).
+   - 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.
+   - USB backend: 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.
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1001028
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1000253
+Bug-Ubuntu: https://bugs.launchpad.net/bugs/987485
+Bug-Upstream: http://cups.org/str.php?L4128
+Author: Till Kamppeter <till.kamppeter@gmail.com>
+Last-Update: 2012-07-10
+
+--- 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 <kernel@zut.de> */
++	{ 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 *)&current, 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...

Reply to: