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