From a29fd7ddb5d63688a4d44d08e4ab16428921c321 Mon Sep 17 00:00:00 2001 From: msweet Date: Wed, 23 May 2012 22:51:18 +0000 Subject: [PATCH] Merge changes from CUPS 1.6svn-r10510. git-svn-id: svn+ssh://src.apple.com/svn/cups/easysw/current@3833 a1ca3aef-8c08-0410-bb20-df032aa958be --- CHANGES-1.5.txt | 22 + CHANGES.txt | 7 +- INSTALL.txt | 2 +- IPPTOOL.txt | 1 + Makedefs.in | 4 +- Makefile | 6 +- README.txt | 2 +- backend/dnssd.c | 16 +- backend/ipp.c | 38 +- backend/snmp-supplies.c | 20 +- backend/usb-libusb.c | 75 +- berkeley/lpr.c | 5 +- cgi-bin/makedocset.c | 4 +- config-scripts/cups-common.m4 | 1 - config-scripts/cups-dnssd.m4 | 2 - config-scripts/cups-ssl.m4 | 12 +- config.h.in | 8 - cups/cups-private.h | 9 + cups/dest-job.c | 4 +- cups/dest-localization.c | 330 +++++- cups/dest-options.c | 655 ++++++++++- cups/dest.c | 530 ++++++++- cups/http-addr.c | 6 +- cups/http.c | 33 +- cups/ipp.c | 130 +-- cups/language-private.h | 1 + cups/language.c | 45 +- cups/page.c | 2 +- cups/pwg-media.c | 107 +- cups/pwg-private.h | 4 +- cups/request.c | 7 +- cups/testcups.c | 118 +- cups/util.c | 3 +- doc/help/api-cups.html | 18 + doc/help/api-ppd.html | 4 +- doc/help/man-ipptoolfile.html | 10 + doc/help/ref-cupsd-conf.html.in | 17 + doc/help/spec-cmp.html | 858 +++++---------- doc/help/spec-ppd.html | 19 + examples/ppdx.c | 314 ++++++ examples/ppdx.h | 82 ++ examples/testppdx.c | 117 ++ examples/testppdx.ppd | 121 +++ filter/spec-ppd.shtml | 18 + install-sh | 2 +- man/cupsd.conf.man.in | 9 +- man/filter.man | 7 +- man/ipptoolfile.man | 10 +- packaging/cups.list.in | 1 + scheduler/Makefile | 1 + scheduler/client.c | 16 +- scheduler/colorman.c | 1505 ++++++++++++++++++++++++++ scheduler/colorman.h | 28 + scheduler/conf.c | 4 +- scheduler/conf.h | 2 + scheduler/cups-driverd.cxx | 4 +- scheduler/cupsd.h | 3 +- scheduler/dirsvc.c | 28 +- scheduler/env.c | 4 +- scheduler/ipp.c | 764 +------------ scheduler/job.c | 47 +- scheduler/job.h | 2 + scheduler/printers.c | 40 +- scheduler/printers.h | 3 +- scheduler/server.c | 16 +- scheduler/sysman.c | 2 + scheduler/tls-darwin.c | 10 + systemv/lp.c | 7 +- templates/jobs.tmpl | 2 +- test/5.1-lpadmin.sh | 14 +- test/5.5-lp.sh | 6 +- test/5.6-lpr.sh | 6 +- test/Makefile | 1 + test/ipp-1.1.test | 66 +- test/ipptool.c | 91 +- test/run-stp-tests.sh | 55 +- test/testfile.pcl | 60 + tools/testbtmm.c | 65 ++ xcode/CUPS.xcodeproj/project.pbxproj | 7 +- 79 files changed, 4956 insertions(+), 1719 deletions(-) create mode 100644 examples/ppdx.c create mode 100644 examples/ppdx.h create mode 100644 examples/testppdx.c create mode 100644 examples/testppdx.ppd create mode 100644 scheduler/colorman.c create mode 100644 scheduler/colorman.h create mode 100644 test/testfile.pcl create mode 100644 tools/testbtmm.c diff --git a/CHANGES-1.5.txt b/CHANGES-1.5.txt index c8dcb991e..d8dda7d09 100644 --- a/CHANGES-1.5.txt +++ b/CHANGES-1.5.txt @@ -1,8 +1,30 @@ CHANGES-1.5.txt --------------- +CHANGES IN CUPS V1.5.4 + + - Multiple libusb backend fixes (STR #4098, STR #4100) + - The IPP backend no longer tries to get the job status for printers + that do not implement the required operation (STR #4083) + - Sending a document in an unsupported format to an IPP printer now + automatically cancels the job (STR #4093) + - Fix some error reporting issues when printing from /dev/null and + other unusual situations (STR #4015) + - The scheduler now sets the CUPS_MAX_MESSAGE environment variable for + filters (STR #4074) + - Fixed a build issue when using older versions of autoconf (STR #4084) + - The IPP backend now treats the client-error-not-possible status code + as a job history issue, allowing IPP printing to Windows to work + (STR #4047) + - The IPP backend incorrectly included the document-format and + compression attributes in Create-Job requests (STR #4086) + - The libusb-based USB backend did not work on non-Linux platforms + (STR #4088) + + CHANGES IN CUPS V1.5.3 + - httpReconnect() did not reset the read/write buffers (STR #4065) - Compiling without threading support failed (STR #4060) - Fixed compile problem with old versions of OpenSSL (STR #4036) - The network backends did not check SNMP supply levels regularly diff --git a/CHANGES.txt b/CHANGES.txt index 33aa961dc..7fc5a4262 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,9 +1,12 @@ -CHANGES.txt - 1.6b1 - 2012-04-17 +CHANGES.txt - 1.6b1 - 2012-05-22 -------------------------------- CHANGES IN CUPS V1.6b1 - - Documentation updates (STR #3927, STR #3980, STR #4010) + - Documentation updates (STR #3927, STR #3980, STR #4010, STR #4068) + - The scheduler now consolidates all PPD updates from filters at the + end of the job (STR #4075) + - CUPS now supports color management using colord (STR #3808) - CUPS now supports Bonjour using Avahi (STR #3066) - The PreserveJobFiles and PreserveJobHistory directives now support specification of a time interval (STR #3143) diff --git a/INSTALL.txt b/INSTALL.txt index dc623d075..0e9ebe391 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -1,4 +1,4 @@ -INSTALL - CUPS v1.6.0 - 2012-04-23 +INSTALL - CUPS v1.6.0 - 2012-05-23 ---------------------------------- This file describes how to compile and install CUPS from source code. For more diff --git a/IPPTOOL.txt b/IPPTOOL.txt index 9cd220103..1d1fce704 100644 --- a/IPPTOOL.txt +++ b/IPPTOOL.txt @@ -88,6 +88,7 @@ CONFORMANCE TESTS onepage-letter.pdf onepage-letter.ps testfile.jpg + testfile.pcl testfile.pdf testfile.ps testfile.txt diff --git a/Makedefs.in b/Makedefs.in index 9bf7afa85..41612af71 100644 --- a/Makedefs.in +++ b/Makedefs.in @@ -24,7 +24,7 @@ CHMOD = @CHMOD@ CXX = @LIBTOOL@ @CXX@ DSO = @DSO@ DSOXX = @DSOXX@ -HTMLDOC = @HTMLDOC@ +GZIP = @GZIP@ INSTALL = @INSTALL@ LD = @LD@ LIBTOOL = @LIBTOOL@ @@ -41,8 +41,8 @@ SHELL = /bin/sh # INSTALL_BIN = $(LIBTOOL) $(INSTALL) -c -m 555 @INSTALL_STRIP@ -INSTALL_CONFIG = $(INSTALL) -c -m @CUPS_CONFIG_FILE_PERM@ INSTALL_COMPDATA = $(INSTALL) -c -m 444 @INSTALL_GZIP@ +INSTALL_CONFIG = $(INSTALL) -c -m @CUPS_CONFIG_FILE_PERM@ INSTALL_DATA = $(INSTALL) -c -m 444 INSTALL_DIR = $(INSTALL) -d INSTALL_LIB = $(LIBTOOL) $(INSTALL) -c -m 555 @INSTALL_STRIP@ diff --git a/Makefile b/Makefile index 6d61c6fba..ca8416dae 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # # Top-level Makefile for CUPS. # -# Copyright 2007-2010 by Apple Inc. +# Copyright 2007-2012 by Apple Inc. # Copyright 1997-2007 by Easy Software Products, all rights reserved. # # These coded instructions, statements, and computer programs are the @@ -275,10 +275,10 @@ docset: apihelp doc/help/api-*.tokens $(RM) doc/help/api-*.tokens echo Indexing docset... - /Developer/usr/bin/docsetutil index org.cups.docset + /Applications/Xcode.app/Contents/Developer/usr/bin/docsetutil index org.cups.docset echo Generating docset archive and feed... $(RM) org.cups.docset.atom - /Developer/usr/bin/docsetutil package --output org.cups.docset.xar \ + /Applications/Xcode.app/Contents/Developer/usr/bin/docsetutil package --output org.cups.docset.xar \ --atom org.cups.docset.atom \ --download-url http://www.cups.org/org.cups.docset.xar \ org.cups.docset diff --git a/README.txt b/README.txt index 944fb67e7..6fe8727a4 100644 --- a/README.txt +++ b/README.txt @@ -1,4 +1,4 @@ -README - CUPS v1.6.0 - 2012-04-23 +README - CUPS v1.6.0 - 2012-05-23 ---------------------------------- Looking for compile instructions? Read the file "INSTALL.txt" diff --git a/backend/dnssd.c b/backend/dnssd.c index 2984407df..7cff1cb4f 100644 --- a/backend/dnssd.c +++ b/backend/dnssd.c @@ -331,7 +331,7 @@ main(int argc, /* I - Number of command-line args */ #ifdef HAVE_AVAHI if ((simple_poll = avahi_simple_poll_new()) == NULL) { - fputs("DEBUG: Unable to create avahi simple poll object.\n", stderr); + fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr); return (1); } @@ -341,7 +341,7 @@ main(int argc, /* I - Number of command-line args */ 0, client_callback, simple_poll, &error); if (!client) { - fputs("DEBUG: Unable to create avahi client.\n", stderr); + fputs("DEBUG: Unable to create Avahi client.\n", stderr); return (1); } @@ -587,10 +587,7 @@ browse_callback( "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", " "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", sdRef, flags, interfaceIndex, errorCode, - serviceName ? serviceName : "(null)", - regtype ? regtype : "(null)", - replyDomain ? replyDomain : "(null)", - context); + serviceName, regtype, replyDomain, context); /* * Only process "add" data... @@ -629,10 +626,7 @@ browse_local_callback( "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", " "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", sdRef, flags, interfaceIndex, errorCode, - serviceName ? serviceName : "(null)", - regtype ? regtype : "(null)", - replyDomain ? replyDomain : "(null)", - context); + serviceName, regtype, replyDomain, context); /* * Only process "add" data... @@ -907,7 +901,7 @@ get_device(cups_array_t *devices, /* I - Device array */ replyDomain); #else /* HAVE_AVAHI */ avahi_service_name_join(fullName, kDNSServiceMaxDomainName, - serviceName, regtype, replyDomain); + serviceName, regtype, replyDomain); #endif /* HAVE_DNSSD */ free(device->fullName); diff --git a/backend/ipp.c b/backend/ipp.c index 73491452d..a0736a8bb 100644 --- a/backend/ipp.c +++ b/backend/ipp.c @@ -66,7 +66,8 @@ typedef struct _cups_monitor_s /**** Monitoring data ****/ *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 */ @@ -241,6 +242,7 @@ main(int argc, /* I - Number of command-line args */ 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 */ @@ -1074,6 +1076,8 @@ main(int argc, /* I - Number of command-line args */ 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) @@ -1081,6 +1085,9 @@ main(int argc, /* I - Number of command-line args */ 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) @@ -1262,6 +1269,7 @@ main(int argc, /* I - Number of command-line args */ 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; @@ -1305,6 +1313,8 @@ main(int argc, /* I - Number of command-line args */ _cupsLangPrintFilter(stderr, "INFO", _("The printer is in use.")); sleep(10); } + else if (ipp_status == IPP_DOCUMENT_FORMAT) + goto cleanup; else if (ipp_status == IPP_FORBIDDEN || ipp_status == IPP_AUTHENTICATION_CANCELED) { @@ -1328,7 +1338,8 @@ main(int argc, /* I - Number of command-line args */ "cups-ipp-missing-validate-job"); break; } - else if (ipp_status < IPP_REDIRECTION_OTHER_SITE) + else if (ipp_status < IPP_REDIRECTION_OTHER_SITE || + ipp_status == IPP_BAD_REQUEST) break; } @@ -1658,7 +1669,7 @@ main(int argc, /* I - Number of command-line args */ * 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.")); @@ -1701,7 +1712,7 @@ main(int argc, /* I - Number of command-line args */ 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... @@ -1723,7 +1734,6 @@ main(int argc, /* I - Number of command-line args */ else { if (ipp_status != IPP_SERVICE_UNAVAILABLE && - ipp_status != IPP_NOT_POSSIBLE && ipp_status != IPP_PRINTER_BUSY) { ippDelete(response); @@ -1871,13 +1881,16 @@ main(int argc, /* I - Number of command-line args */ 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 || job_canceled < 0) + else if (ipp_status == IPP_REQUEST_VALUE || + ipp_status == IPP_DOCUMENT_FORMAT || job_canceled < 0) { 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.")); @@ -2123,7 +2136,8 @@ monitor_printer( * 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; @@ -2321,7 +2335,7 @@ new_request( 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); @@ -2329,7 +2343,7 @@ new_request( } #ifdef HAVE_LIBZ - if (compression) + if (compression && op != IPP_CREATE_JOB) { ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "compression", NULL, compression); @@ -2569,7 +2583,7 @@ new_request( * When talking to another CUPS server, send all options... */ - cupsEncodeOptions(request, num_options, options); + cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB); } if (copies > 1 && (!pc || copies <= pc->max_copies)) diff --git a/backend/snmp-supplies.c b/backend/snmp-supplies.c index 75dc9dc7a..380dd793d 100644 --- a/backend/snmp-supplies.c +++ b/backend/snmp-supplies.c @@ -49,6 +49,9 @@ #define CUPS_CLEANER_NEAR_EOL 0x0400 /* Proposed JPS3 */ #define CUPS_CLEANER_LIFE_OVER 0x0800 /* Proposed JPS3 */ +#define CUPS_SNMP_NONE 0x0000 +#define CUPS_SNMP_CAPACITY 0x0001 /* Supply levels reported as percentages */ + /* * Local structures... @@ -79,6 +82,8 @@ static http_addr_t current_addr; /* Current address */ static int current_state = -1; /* Current device state bits */ static int charset = -1; /* Character set for supply names */ +static unsigned quirks = CUPS_SNMP_NONE; + /* Quirks we have to work around */ static int num_supplies = 0; /* Number of supplies found */ static backend_supplies_t supplies[CUPS_MAX_SUPPLIES]; @@ -246,6 +251,9 @@ backendSNMPSupplies( { if (supplies[i].max_capacity > 0 && supplies[i].level >= 0) percent = 100 * supplies[i].level / supplies[i].max_capacity; + else if (supplies[i].level >= 0 && supplies[i].level <= 100 && + (quirks & CUPS_SNMP_CAPACITY)) + percent = supplies[i].level; else percent = 50; @@ -308,7 +316,8 @@ backendSNMPSupplies( if (i) *ptr++ = ','; - if (supplies[i].max_capacity > 0 && supplies[i].level >= 0) + if ((supplies[i].max_capacity > 0 || (quirks & CUPS_SNMP_CAPACITY)) && + supplies[i].level >= 0) sprintf(ptr, "%d", percent); else strcpy(ptr, "-1"); @@ -506,6 +515,12 @@ backend_init_supplies( return; } + if ((ppdattr = ppdFindAttr(ppd, "cupsSNMPQuirks", NULL)) != NULL) + { + if (!_cups_strcasecmp(ppdattr->value, "capacity")) + quirks |= CUPS_SNMP_CAPACITY; + } + ppdClose(ppd); /* @@ -932,7 +947,8 @@ backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */ supplies[i - 1].level = packet->object_value.integer; } - else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity)) + else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity) && + !(quirks & CUPS_SNMP_CAPACITY)) { /* * Get max capacity... diff --git a/backend/usb-libusb.c b/backend/usb-libusb.c index 103a0e30b..5811fde1f 100644 --- a/backend/usb-libusb.c +++ b/backend/usb-libusb.c @@ -537,10 +537,10 @@ print_device(const char *uri, /* I - Device URI */ /* * 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", + fputs("DEBUG: Read thread still active, aborting the pending read...\n", stderr); g.wait_eof = 0; @@ -548,7 +548,7 @@ print_device(const char *uri, /* I - Device URI */ 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, @@ -602,8 +602,8 @@ close_device(usb_printer_t *printer) /* I - Printer */ int number; /* Interface number */ - libusb_get_device_descriptor (printer->device, &devdesc); - libusb_get_config_descriptor (printer->device, printer->conf, &confptr); + libusb_get_device_descriptor(printer->device, &devdesc); + libusb_get_config_descriptor(printer->device, printer->conf, &confptr); number = confptr->interface[printer->iface]. altsetting[printer->altset].bInterfaceNumber; libusb_release_interface(printer->handle, number); @@ -694,7 +694,7 @@ find_device(usb_cb_t cb, /* I - Callback function */ * a printer... */ - libusb_get_device_descriptor (device, &devdesc); + libusb_get_device_descriptor(device, &devdesc); if (!devdesc.bNumConfigurations || !devdesc.idVendor || !devdesc.idProduct) @@ -702,7 +702,7 @@ find_device(usb_cb_t cb, /* I - Callback function */ 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; @@ -950,7 +950,7 @@ make_device_uri( 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)) { /* @@ -1089,9 +1089,45 @@ open_device(usb_printer_t *printer, /* I - Printer */ if (libusb_open(printer->device, &printer->handle) < 0) return (-1); + printer->usblp_attached = 0; + if (verbose) fputs("STATE: +connecting-to-device\n", stderr); + if ((errcode = libusb_get_device_descriptor (printer->device, &devdesc)) < 0) + { + fprintf(stderr, "DEBUG: Failed to get device descriptor, code: %d\n", + errcode); + goto error; + } + + /* + * Get the "usblp" kernel module out of the way. This backend only + * works without the module attached. + */ + + errcode = libusb_kernel_driver_active(printer->handle, printer->iface); + if (errcode == 0) + printer->usblp_attached = 0; + else if (errcode == 1) + { + printer->usblp_attached = 1; + if ((errcode = + libusb_detach_kernel_driver(printer->handle, printer->iface)) < 0) + { + fprintf(stderr, "DEBUG: Failed to detach \"usblp\" module from %04x:%04x\n", + devdesc.idVendor, devdesc.idProduct); + goto error; + } + } + else + { + printer->usblp_attached = 0; + fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" kernel module attached\n", + devdesc.idVendor, devdesc.idProduct); + goto error; + } + /* * Set the desired configuration, but only if it needs changing. Some * printers (e.g., Samsung) don't like libusb_set_configuration. It will @@ -1106,8 +1142,8 @@ open_device(usb_printer_t *printer, /* I - Printer */ 0, 0, (unsigned char *)¤t, 1, 5000) < 0) current = 0; /* Assume not configured */ - libusb_get_device_descriptor (printer->device, &devdesc); - libusb_get_config_descriptor (printer->device, printer->conf, &confptr); + libusb_get_device_descriptor(printer->device, &devdesc); + libusb_get_config_descriptor(printer->device, printer->conf, &confptr); number1 = confptr->bConfigurationValue; if (number1 != current) @@ -1148,9 +1184,14 @@ open_device(usb_printer_t *printer, /* I - Printer */ else { printer->usblp_attached = 0; - fprintf(stderr, "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" kernel module attached\n", - devdesc.idVendor, devdesc.idProduct); - goto error; + + if (errcode != LIBUSB_ERROR_NOT_SUPPORTED) + { + fprintf(stderr, + "DEBUG: Failed to check whether %04x:%04x has the \"usblp\" " + "kernel module attached\n", devdesc.idVendor, devdesc.idProduct); + goto error; + } } /* @@ -1163,11 +1204,13 @@ open_device(usb_printer_t *printer, /* I - Printer */ while ((errcode = libusb_claim_interface(printer->handle, number1)) < 0) { if (errcode != LIBUSB_ERROR_BUSY) + { fprintf(stderr, "DEBUG: Failed to claim interface %d for %04x:%04x: %s\n", number1, devdesc.idVendor, devdesc.idProduct, strerror(errno)); - goto error; + goto error; + } } /* @@ -1187,12 +1230,14 @@ open_device(usb_printer_t *printer, /* I - Printer */ < 0) { if (errcode != LIBUSB_ERROR_BUSY) + { fprintf(stderr, "DEBUG: Failed to set alternate interface %d for %04x:%04x: " "%s\n", number2, devdesc.idVendor, devdesc.idProduct, strerror(errno)); - goto error; + goto error; + } } } diff --git a/berkeley/lpr.c b/berkeley/lpr.c index 27d73c55c..7d30cc6e6 100644 --- a/berkeley/lpr.c +++ b/berkeley/lpr.c @@ -89,7 +89,7 @@ main(int argc, /* I - Number of command-line arguments */ cupsSetUser(argv[i]); } break; - + case 'H' : /* Connect to host */ if (argv[i][2] != '\0') cupsSetServer(argv[i] + 2); @@ -401,8 +401,9 @@ main(int argc, /* I - Number of command-line arguments */ if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK) { + _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString()); cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0); - job_id = 0; + return (1); } } diff --git a/cgi-bin/makedocset.c b/cgi-bin/makedocset.c index 577282889..4aee5d9a4 100644 --- a/cgi-bin/makedocset.c +++ b/cgi-bin/makedocset.c @@ -3,7 +3,7 @@ * * Xcode documentation set generator. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products. * * These coded instructions, statements, and computer programs are the @@ -31,7 +31,7 @@ * Include necessary headers... */ -#include "cgi.h" +#include "cgi-private.h" #include diff --git a/config-scripts/cups-common.m4 b/config-scripts/cups-common.m4 index de6996d47..b8c740523 100644 --- a/config-scripts/cups-common.m4 +++ b/config-scripts/cups-common.m4 @@ -51,7 +51,6 @@ AC_PROG_RANLIB AC_PATH_PROG(AR,ar) AC_PATH_PROG(CHMOD,chmod) AC_PATH_PROG(GZIP,gzip) -AC_PATH_PROG(HTMLDOC,htmldoc) AC_PATH_PROG(LD,ld) AC_PATH_PROG(LN,ln) AC_PATH_PROG(MV,mv) diff --git a/config-scripts/cups-dnssd.m4 b/config-scripts/cups-dnssd.m4 index 3ed91f79b..426637a20 100644 --- a/config-scripts/cups-dnssd.m4 +++ b/config-scripts/cups-dnssd.m4 @@ -43,8 +43,6 @@ if test "x$DNSSD_BACKEND" = x -a x$enable_dnssd != xno; then Darwin*) # Darwin and MacOS X... AC_DEFINE(HAVE_DNSSD) - AC_DEFINE(HAVE_COREFOUNDATION) - AC_DEFINE(HAVE_SYSTEMCONFIGURATION) DNSSDLIBS="-framework CoreFoundation -framework SystemConfiguration" DNSSD_BACKEND="dnssd" ;; diff --git a/config-scripts/cups-ssl.m4 b/config-scripts/cups-ssl.m4 index 157ed0f42..28d5d3482 100644 --- a/config-scripts/cups-ssl.m4 +++ b/config-scripts/cups-ssl.m4 @@ -124,7 +124,7 @@ if test x$enable_ssl != xno; then dnl Check for the OpenSSL library last... if test $have_ssl = 0 -a "x$enable_openssl" != "xno"; then - AC_CHECK_HEADER(openssl/ssl.h, + AC_CHECK_HEADER(openssl/ssl.h,[ dnl Save the current libraries so the crypto stuff isn't always dnl included... SAVELIBS="$LIBS" @@ -149,14 +149,16 @@ if test x$enable_ssl != xno; then $libcrypto) if test "x${SSLLIBS}" != "x"; then - LIBS="$SAVELIBS $SSLLIBS" - AC_CHECK_FUNC(SSL_set_tlsext_host_name, - AC_DEFINE(HAVE_SSL_SET_TLSEXT_HOST_NAME)) break fi done - LIBS="$SAVELIBS") + if test "x${SSLLIBS}" != "x"; then + LIBS="$SAVELIBS $SSLLIBS" + AC_CHECK_FUNCS(SSL_set_tlsext_host_name) + fi + + LIBS="$SAVELIBS"]) fi fi diff --git a/config.h.in b/config.h.in index 32a2d715c..9498b7f5e 100644 --- a/config.h.in +++ b/config.h.in @@ -537,14 +537,6 @@ #define CUPS_GHOSTSCRIPT "/usr/bin/gs" -/* - * Do we have Darwin's CoreFoundation and SystemConfiguration frameworks? - */ - -#undef HAVE_COREFOUNDATION -#undef HAVE_SYSTEMCONFIGURATION - - /* * Do we have CoreFoundation public and private headers? */ diff --git a/cups/cups-private.h b/cups/cups-private.h index e2c0239ad..816e72552 100644 --- a/cups/cups-private.h +++ b/cups/cups-private.h @@ -190,13 +190,22 @@ typedef struct _cups_media_db_s /* Media database */ * millimeters */ } _cups_media_db_t; +typedef struct _cups_dconstres_s /* Constraint/resolver */ +{ + char *name; /* Name of resolver */ + ipp_t *collection; /* Collection containing attrs */ +} _cups_dconstres_t; + struct _cups_dinfo_s /* Destination capability and status * information */ { const char *uri; /* Printer URI */ char *resource; /* Resource path */ ipp_t *attrs; /* Printer attributes */ + int num_defaults; /* Number of default options */ + cups_option_t *defaults; /* Default options */ cups_array_t *constraints; /* Job constraints */ + cups_array_t *resolvers; /* Job resolvers */ cups_array_t *localizations; /* Localization information */ cups_array_t *media_db; /* Media database */ _cups_media_db_t min_size, /* Minimum size */ diff --git a/cups/dest-job.c b/cups/dest-job.c index dc4f0879b..f0792c728 100644 --- a/cups/dest-job.c +++ b/cups/dest-job.c @@ -196,7 +196,9 @@ cupsCreateDestJob( if (title) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title); - cupsEncodeOptions(request, num_options, options); + + cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB); + cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION); /* * Send the request and get the job-id... diff --git a/cups/dest-localization.c b/cups/dest-localization.c index 375386909..ad709ee82 100644 --- a/cups/dest-localization.c +++ b/cups/dest-localization.c @@ -15,10 +15,14 @@ * * Contents: * - * cupsLocalizeDestOption() - Get the localized string for a destination - * option. - * cupsLocalizeDestValue() - Get the localized string for a destination - * option+value pair. + * cupsLocalizeDestOption() - Get the localized string for a destination + * option. + * cupsLocalizeDestValue() - Get the localized string for a destination + * option+value pair. + * cups_create_localizations() - Create the localizations array for a + * destination. + * cups_read_strings() - Read a pair of strings from a .strings file. + * cups_scan_strings() - Scan a quoted string. */ /* @@ -28,12 +32,22 @@ #include "cups-private.h" +/* + * Local functions... + */ + +static void cups_create_localizations(http_t *http, cups_dinfo_t *dinfo); +static int cups_read_strings(cups_file_t *fp, char *buffer, size_t bufsize, + char **id, char **str); +static char *cups_scan_strings(char *buffer); + + /* * 'cupsLocalizeDestOption()' - Get the localized string for a destination * option. * - * The returned string is stored in the localization array and will become - * invalid if the localization array is deleted. + * The returned string is stored in the destination information and will become + * invalid if the destination information is deleted. * * @since CUPS 1.6/OS X 10.8@ */ @@ -45,7 +59,25 @@ cupsLocalizeDestOption( cups_dinfo_t *dinfo, /* I - Destination information */ const char *option) /* I - Option to localize */ { - return (option); + _cups_message_t key, /* Search key */ + *match; /* Matching entry */ + + + if (!http || !dest || !dinfo) + return (option); + + if (!dinfo->localizations) + cups_create_localizations(http, dinfo); + + if (cupsArrayCount(dinfo->localizations) == 0) + return (option); + + key.id = (char *)option; + if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, + &key)) != NULL) + return (match->str); + else + return (option); } @@ -53,8 +85,8 @@ cupsLocalizeDestOption( * 'cupsLocalizeDestValue()' - Get the localized string for a destination * option+value pair. * - * The returned string is stored in the localization array and will become - * invalid if the localization array is deleted. + * The returned string is stored in the destination information and will become + * invalid if the destination information is deleted. * * @since CUPS 1.6/OS X 10.8@ */ @@ -67,10 +99,288 @@ cupsLocalizeDestValue( const char *option, /* I - Option to localize */ const char *value) /* I - Value to localize */ { - return (value); + _cups_message_t key, /* Search key */ + *match; /* Matching entry */ + char pair[256]; /* option.value pair */ + + + if (!http || !dest || !dinfo) + return (value); + + if (!dinfo->localizations) + cups_create_localizations(http, dinfo); + + if (cupsArrayCount(dinfo->localizations) == 0) + return (value); + + snprintf(pair, sizeof(pair), "%s.%s", option, value); + key.id = pair; + if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, + &key)) != NULL) + return (match->str); + else + return (value); +} + + +/* + * 'cups_create_localizations()' - Create the localizations array for a + * destination. + */ + +static void +cups_create_localizations( + http_t *http, /* I - Connection to destination */ + cups_dinfo_t *dinfo) /* I - Destination informations */ +{ + http_t *http2; /* Connection for strings file */ + http_status_t status; /* Request status */ + ipp_attribute_t *attr; /* "printer-strings-uri" attribute */ + char scheme[32], /* URI scheme */ + userpass[256], /* Username/password info */ + hostname[256], /* Hostname */ + resource[1024], /* Resource */ + http_hostname[256], + /* Hostname of connection */ + tempfile[1024]; /* Temporary filename */ + int port; /* Port number */ + http_encryption_t encryption; /* Encryption to use */ + cups_file_t *temp; /* Temporary file */ + + + /* + * Create an empty message catalog... + */ + + dinfo->localizations = _cupsMessageNew(NULL); + + /* + * See if there are any localizations... + */ + + if ((attr = ippFindAttribute(dinfo->attrs, "printer-strings-uri", + IPP_TAG_URI)) == NULL) + { + /* + * Nope... + */ + + DEBUG_puts("4cups_create_localizations: No printer-strings-uri (uri) " + "value."); + return; /* Nope */ + } + + /* + * Pull apart the URI and determine whether we need to try a different + * server... + */ + + if (httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, + scheme, sizeof(scheme), userpass, sizeof(userpass), + hostname, sizeof(hostname), &port, resource, + sizeof(resource)) < HTTP_URI_OK) + { + DEBUG_printf(("4cups_create_localizations: Bad printer-strings-uri value " + "\"%s\".", attr->values[0].string.text)); + return; + } + + httpGetHostname(http, http_hostname, sizeof(http_hostname)); + + if (!_cups_strcasecmp(http_hostname, hostname) && + port == _httpAddrPort(http->hostaddr)) + { + /* + * Use the same connection... + */ + + http2 = http; + } + else + { + /* + * Connect to the alternate host... + */ + + if (!strcmp(scheme, "https")) + encryption = HTTP_ENCRYPT_ALWAYS; + else + encryption = HTTP_ENCRYPT_IF_REQUESTED; + + if ((http2 = httpConnectEncrypt(hostname, port, encryption)) == NULL) + { + DEBUG_printf(("4cups_create_localizations: Unable to connect to " + "%s:%d: %s", hostname, port, cupsLastErrorString())); + return; + } + } + + /* + * Get a temporary file... + */ + + if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL) + { + DEBUG_printf(("4cups_create_localizations: Unable to create temporary " + "file: %s", cupsLastErrorString())); + if (http2 != http) + httpClose(http2); + return; + } + + status = cupsGetFd(http2, resource, cupsFileNumber(temp)); + + DEBUG_printf(("4cups_create_localizations: GET %s = %s", resource, + httpStatus(status))); + + if (status == HTTP_OK) + { + /* + * Got the file, read it... + */ + + char buffer[8192], /* Message buffer */ + *id, /* ID string */ + *str; /* Translated message */ + _cups_message_t *m; /* Current message */ + + lseek(cupsFileNumber(temp), 0, SEEK_SET); + + while (cups_read_strings(temp, buffer, sizeof(buffer), &id, &str)) + { + if ((m = malloc(sizeof(_cups_message_t))) == NULL) + break; + + m->id = strdup(id); + m->str = strdup(str); + + if (m->id && m->str) + cupsArrayAdd(dinfo->localizations, m); + else + { + if (m->id) + free(m->id); + + if (m->str) + free(m->str); + + free(m); + break; + } + } + } + + DEBUG_printf(("4cups_create_localizations: %d messages loaded.", + cupsArrayCount(dinfo->localizations))); + + /* + * Cleanup... + */ + + unlink(tempfile); + cupsFileClose(temp); + + if (http2 != http) + httpClose(http2); +} + + +/* + * 'cups_read_strings()' - Read a pair of strings from a .strings file. + */ + +static int /* O - 1 on success, 0 on failure */ +cups_read_strings(cups_file_t *strings, /* I - .strings file */ + char *buffer, /* I - Line buffer */ + size_t bufsize, /* I - Size of line buffer */ + char **id, /* O - Pointer to ID string */ + char **str) /* O - Pointer to translation string */ +{ + char *bufptr; /* Pointer into buffer */ + + + while (cupsFileGets(strings, buffer, bufsize)) + { + if (buffer[0] != '\"') + continue; + + *id = buffer + 1; + bufptr = cups_scan_strings(buffer); + + if (*bufptr != '\"') + continue; + + *bufptr++ = '\0'; + + while (*bufptr && *bufptr != '\"') + bufptr ++; + + if (!*bufptr) + continue; + + *str = bufptr + 1; + bufptr = cups_scan_strings(bufptr); + + if (*bufptr != '\"') + continue; + + *bufptr = '\0'; + + return (1); + } + + return (0); +} + + +/* + * 'cups_scan_strings()' - Scan a quoted string. + */ + +static char * /* O - End of string */ +cups_scan_strings(char *buffer) /* I - Start of string */ +{ + char *bufptr; /* Pointer into string */ + + + for (bufptr = buffer + 1; *bufptr && *bufptr != '\"'; bufptr ++) + { + if (*bufptr == '\\') + { + if (bufptr[1] >= '0' && bufptr[1] <= '3' && + bufptr[2] >= '0' && bufptr[2] <= '7' && + bufptr[3] >= '0' && bufptr[3] <= '7') + { + /* + * Decode \nnn octal escape... + */ + + *bufptr = ((((bufptr[1] - '0') << 3) | (bufptr[2] - '0')) << 3) | + (bufptr[3] - '0'); + _cups_strcpy(bufptr + 1, bufptr + 4); + } + else + { + /* + * Decode \C escape... + */ + + _cups_strcpy(bufptr, bufptr + 1); + if (*bufptr == 'n') + *bufptr = '\n'; + else if (*bufptr == 'r') + *bufptr = '\r'; + else if (*bufptr == 't') + *bufptr = '\t'; + } + } + } + + return (bufptr); } + /* * End of "$Id$". */ diff --git a/cups/dest-options.c b/cups/dest-options.c index cfdf6077e..32616a683 100644 --- a/cups/dest-options.c +++ b/cups/dest-options.c @@ -15,23 +15,28 @@ * * Contents: * -* cupsCheckDestSupported() - Check that the option and value are supported - * by the destination. - * cupsCopyDestConflicts() - Get conflicts and resolutions for a new - * option/value pair. - * cupsCopyDestInfo() - Get the supported values/capabilities for the - * destination. - * cupsFreeDestInfo() - Free destination information obtained using - * @link cupsCopyDestInfo@. - * cupsGetDestMediaByName() - Get media names, dimensions, and margins. - * cupsGetDestMediaBySize() - Get media names, dimensions, and margins. - * cups_compare_media_db() - Compare two media entries. - * cups_copy_media_db() - Copy a media entry. - * cups_create_media_db() - Create the media database. - * cups_free_media_cb() - Free a media entry. - * cups_get_media_db() - Lookup the media entry for a given size. - * cups_is_close_media_db() - Compare two media entries to see if they are - * close to the same size. + * cupsCheckDestSupported() - Check that the option and value are supported + * by the destination. + * cupsCopyDestConflicts() - Get conflicts and resolutions for a new + * option/value pair. + * cupsCopyDestInfo() - Get the supported values/capabilities for the + * destination. + * cupsFreeDestInfo() - Free destination information obtained using + * @link cupsCopyDestInfo@. + * cupsGetDestMediaByName() - Get media names, dimensions, and margins. + * cupsGetDestMediaBySize() - Get media names, dimensions, and margins. + * cups_add_dconstres() - Add a constraint or resolver to an array. + * cups_compare_dconstres() - Compare to resolver entries. + * cups_compare_media_db() - Compare two media entries. + * cups_copy_media_db() - Copy a media entry. + * cups_create_constraints() - Create the constraints and resolvers arrays. + * cups_create_defaults() - Create the -default option array. + * cups_create_media_db() - Create the media database. + * cups_free_media_cb() - Free a media entry. + * cups_get_media_db() - Lookup the media entry for a given size. + * cups_is_close_media_db() - Compare two media entries to see if they are + * close to the same size. + * cups_test_constraints() - Test constraints. */ /* @@ -45,9 +50,14 @@ * Local functions... */ +static void cups_add_dconstres(cups_array_t *a, ipp_t *collection); +static int cups_compare_dconstres(_cups_dconstres_t *a, + _cups_dconstres_t *b); static int cups_compare_media_db(_cups_media_db_t *a, _cups_media_db_t *b); static _cups_media_db_t *cups_copy_media_db(_cups_media_db_t *mdb); +static void cups_create_constraints(cups_dinfo_t *dinfo); +static void cups_create_defaults(cups_dinfo_t *dinfo); static void cups_create_media_db(cups_dinfo_t *dinfo); static void cups_free_media_db(_cups_media_db_t *mdb); static int cups_get_media_db(cups_dinfo_t *dinfo, @@ -55,6 +65,14 @@ static int cups_get_media_db(cups_dinfo_t *dinfo, cups_size_t *size); static int cups_is_close_media_db(_cups_media_db_t *a, _cups_media_db_t *b); +static cups_array_t *cups_test_constraints(cups_dinfo_t *dinfo, + const char *new_option, + const char *new_value, + int num_options, + cups_option_t *options, + int *num_conflicts, + cups_option_t **conflicts); + /* * 'cupsCheckDestSupported()' - Check that the option and value are supported @@ -176,6 +194,15 @@ cupsCheckDestSupported( case IPP_TAG_BOOLEAN : return (attr->values[0].boolean); + case IPP_TAG_RANGE : + int_value = atoi(value); + + for (i = 0; i < attr->num_values; i ++) + if (int_value >= attr->values[i].range.lower && + int_value <= attr->values[i].range.upper) + return (1); + break; + case IPP_TAG_RESOLUTION : if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3) { @@ -239,7 +266,8 @@ cupsCheckDestSupported( * user. "new_option" and "new_value" are the setting the user has just * changed. * - * Returns 1 if there is a conflict and 0 otherwise. + * Returns 1 if there is a conflict, 0 if there are no conflicts, and -1 if + * there was an unrecoverable error such as a resolver loop. * * If "num_conflicts" and "conflicts" are not NULL, they are set to contain the * list of conflicting option/value pairs. Similarly, if "num_resolved" and @@ -252,7 +280,7 @@ cupsCheckDestSupported( * @since CUPS 1.6/OS X 10.8@ */ -int /* O - 1 if there is a conflict */ +int /* O - 1 if there is a conflict, 0 if none, -1 on error */ cupsCopyDestConflicts( http_t *http, /* I - Connection to destination */ cups_dest_t *dest, /* I - Destination */ @@ -266,6 +294,27 @@ cupsCopyDestConflicts( int *num_resolved, /* O - Number of options to resolve */ cups_option_t **resolved) /* O - Resolved options */ { + int i, /* Looping var */ + have_conflicts = 0, /* Do we have conflicts? */ + changed, /* Did we change something? */ + tries, /* Number of tries for resolution */ + num_myconf = 0, /* My number of conflicting options */ + num_myres = 0; /* My number of resolved options */ + cups_option_t *myconf = NULL, /* My conflicting options */ + *myres = NULL, /* My resolved options */ + *myoption, /* My current option */ + *option; /* Current option */ + cups_array_t *active, /* Active conflicts */ + *pass = NULL, /* Resolvers for this pass */ + *resolvers = NULL, /* Resolvers we have used */ + *test; /* Test array for conflicts */ + _cups_dconstres_t *c, /* Current constraint */ + *r; /* Current resolver */ + ipp_attribute_t *attr; /* Current attribute */ + char value[2048]; /* Current attribute value as string */ + const char *myvalue; /* Current value of an option */ + + /* * Clear returned values... */ @@ -286,18 +335,223 @@ cupsCopyDestConflicts( * Range check input... */ - if (!http || !dest || !dinfo || !new_option || !new_value || + if (!http || !dest || !dinfo || (num_conflicts != NULL) != (conflicts != NULL) || (num_resolved != NULL) != (resolved != NULL)) return (0); /* - * Check for and resolve any conflicts... + * Load constraints as needed... */ - /* TODO: implement me! */ + if (!dinfo->constraints) + cups_create_constraints(dinfo); - return (0); + if (cupsArrayCount(dinfo->constraints) == 0) + return (0); + + if (!dinfo->num_defaults) + cups_create_defaults(dinfo); + + /* + * If we are resolving, create a shadow array... + */ + + if (num_resolved) + { + for (i = num_options, option = options; i > 0; i --, option ++) + num_myres = cupsAddOption(option->name, option->value, num_myres, &myres); + + if (new_option && new_value) + num_myres = cupsAddOption(new_option, new_value, num_myres, &myres); + } + else + { + num_myres = num_options; + myres = options; + } + + /* + * Check for any conflicts... + */ + + if (num_resolved) + pass = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL); + + for (tries = 0; tries < 100; tries ++) + { + /* + * Check for any conflicts... + */ + + if (num_conflicts || num_resolved) + { + cupsFreeOptions(num_myconf, myconf); + + num_myconf = 0; + myconf = NULL; + active = cups_test_constraints(dinfo, new_option, new_value, + num_myres, myres, &num_myconf, + &myconf); + } + else + active = cups_test_constraints(dinfo, new_option, new_value, num_myres, + myres, NULL, NULL); + + have_conflicts = (active != NULL); + + if (!active || !num_resolved) + break; /* All done */ + + /* + * Scan the constraints that were triggered to apply resolvers... + */ + + if (!resolvers) + resolvers = cupsArrayNew((cups_array_func_t)cups_compare_dconstres, NULL); + + for (c = (_cups_dconstres_t *)cupsArrayFirst(active), changed = 0; + c; + c = (_cups_dconstres_t *)cupsArrayNext(active)) + { + if (cupsArrayFind(pass, c)) + continue; /* Already applied this resolver... */ + + if (cupsArrayFind(resolvers, c)) + { + DEBUG_printf(("1cupsCopyDestConflicts: Resolver loop with %s.", + c->name)); + have_conflicts = -1; + goto cleanup; + } + + if ((r = cupsArrayFind(dinfo->resolvers, c)) == NULL) + { + DEBUG_printf(("1cupsCopyDestConflicts: Resolver %s not found.", + c->name)); + have_conflicts = -1; + goto cleanup; + } + + /* + * Add the options from the resolver... + */ + + cupsArrayAdd(pass, r); + cupsArrayAdd(resolvers, r); + + for (attr = ippFirstAttribute(r->collection); + attr; + attr = ippNextAttribute(r->collection)) + { + if (new_option && !strcmp(attr->name, new_option)) + continue; /* Ignore this if we just changed it */ + + if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value)) + continue; /* Ignore if the value is too long */ + + if ((test = cups_test_constraints(dinfo, attr->name, value, num_myres, + myres, NULL, NULL)) == NULL) + { + /* + * That worked, flag it... + */ + + changed = 1; + } + else + cupsArrayDelete(test); + + /* + * Add the option/value from the resolver regardless of whether it + * worked; this makes sure that we can cascade several changes to + * make things resolve... + */ + + num_myres = cupsAddOption(attr->name, value, num_myres, &myres); + } + } + + if (!changed) + { + DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve constraints."); + have_conflicts = -1; + goto cleanup; + } + + cupsArrayClear(pass); + + cupsArrayDelete(active); + active = NULL; + } + + if (tries >= 0) + { + DEBUG_puts("1cupsCopyDestConflicts: Unable to resolve after 100 tries."); + have_conflicts = -1; + goto cleanup; + } + + /* + * Copy resolved options as needed... + */ + + if (num_resolved) + { + for (i = num_myres, myoption = myres; i > 0; i --, myoption ++) + { + if ((myvalue = cupsGetOption(myoption->name, num_options, + options)) == NULL || + strcmp(myvalue, myoption->value)) + { + if (new_option && !strcmp(new_option, myoption->name) && + new_value && !strcmp(new_value, myoption->value)) + continue; + + *num_resolved = cupsAddOption(myoption->name, myoption->value, + *num_resolved, resolved); + } + } + } + + /* + * Clean up... + */ + + cleanup: + + cupsArrayDelete(active); + cupsArrayDelete(pass); + cupsArrayDelete(resolvers); + + if (num_resolved) + { + /* + * Free shadow copy of options... + */ + + cupsFreeOptions(num_myres, myres); + } + + if (num_conflicts) + { + /* + * Return conflicting options to caller... + */ + + *num_conflicts = num_myconf; + *conflicts = myconf; + } + else + { + /* + * Free conflicting options... + */ + + cupsFreeOptions(num_myconf, myconf); + } + + return (have_conflicts); } @@ -319,6 +573,9 @@ cupsCopyDestInfo( cups_dinfo_t *dinfo; /* Destination information */ ipp_t *request, /* Get-Printer-Attributes request */ *response; /* Supported attributes */ + int tries, /* Number of tries so far */ + delay, /* Current retry delay */ + prev_delay; /* Next retry delay */ const char *uri; /* Printer URI */ char resource[1024]; /* Resource path */ int version; /* IPP version */ @@ -352,7 +609,10 @@ cupsCopyDestInfo( * Get the supported attributes... */ - version = 20; + delay = 1; + prev_delay = 1; + tries = 0; + version = 20; do { @@ -383,11 +643,22 @@ cupsCopyDestInfo( if (status == IPP_VERSION_NOT_SUPPORTED && version > 11) version = 11; + else if (status == IPP_PRINTER_BUSY) + { + sleep(delay); + + delay = _cupsNextDelay(delay, &prev_delay); + } else return (NULL); } + + tries ++; } - while (!response); + while (!response && tries < 10); + + if (!response) + return (NULL); /* * Allocate a cups_dinfo_t structure and return it... @@ -429,14 +700,15 @@ cupsFreeDestInfo(cups_dinfo_t *dinfo) /* I - Destination information */ _cupsStrFree(dinfo->resource); - ippDelete(dinfo->attrs); - cupsArrayDelete(dinfo->constraints); + cupsArrayDelete(dinfo->resolvers); cupsArrayDelete(dinfo->localizations); cupsArrayDelete(dinfo->media_db); + ippDelete(dinfo->attrs); + free(dinfo); } @@ -576,6 +848,46 @@ cupsGetDestMediaBySize( } +/* + * 'cups_add_dconstres()' - Add a constraint or resolver to an array. + */ + +static void +cups_add_dconstres( + cups_array_t *a, /* I - Array */ + ipp_t *collection) /* I - Collection value */ +{ + ipp_attribute_t *attr; /* Attribute */ + _cups_dconstres_t *temp; /* Current constraint/resolver */ + + + if ((attr = ippFindAttribute(collection, "resolver-name", + IPP_TAG_NAME)) == NULL) + return; + + if ((temp = calloc(1, sizeof(_cups_dconstres_t))) == NULL) + return; + + temp->name = attr->values[0].string.text; + temp->collection = collection; + + cupsArrayAdd(a, temp); +} + + +/* + * 'cups_compare_dconstres()' - Compare to resolver entries. + */ + +static int /* O - Result of comparison */ +cups_compare_dconstres( + _cups_dconstres_t *a, /* I - First resolver */ + _cups_dconstres_t *b) /* I - Second resolver */ +{ + return (strcmp(a->name, b->name)); +} + + /* * 'cups_compare_media_db()' - Compare two media entries. */ @@ -633,6 +945,93 @@ cups_copy_media_db( } +/* + * 'cups_create_constraints()' - Create the constraints and resolvers arrays. + */ + +static void +cups_create_constraints( + cups_dinfo_t *dinfo) /* I - Destination information */ +{ + int i; /* Looping var */ + ipp_attribute_t *attr; /* Attribute */ + _ipp_value_t *val; /* Current value */ + + + dinfo->constraints = cupsArrayNew3(NULL, NULL, NULL, 0, NULL, + (cups_afree_func_t)free); + dinfo->resolvers = cupsArrayNew3((cups_array_func_t)cups_compare_dconstres, + NULL, NULL, 0, NULL, + (cups_afree_func_t)free); + + if ((attr = ippFindAttribute(dinfo->attrs, "job-constraints-supported", + IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + for (i = attr->num_values, val = attr->values; i > 0; i --, val ++) + cups_add_dconstres(dinfo->constraints, val->collection); + } + + if ((attr = ippFindAttribute(dinfo->attrs, "job-resolvers-supported", + IPP_TAG_BEGIN_COLLECTION)) != NULL) + { + for (i = attr->num_values, val = attr->values; i > 0; i --, val ++) + cups_add_dconstres(dinfo->resolvers, val->collection); + } +} + + +/* + * 'cups_create_defaults()' - Create the -default option array. + * + * TODO: Need to support collection defaults... + */ + +static void +cups_create_defaults( + cups_dinfo_t *dinfo) /* I - Destination information */ +{ + ipp_attribute_t *attr; /* Current attribute */ + char name[IPP_MAX_NAME + 1], + /* Current name */ + *nameptr, /* Pointer into current name */ + value[2048]; /* Current value */ + + + /* + * Iterate through the printer attributes looking for xxx-default and adding + * xxx=value to the defaults option array. + */ + + for (attr = ippFirstAttribute(dinfo->attrs); + attr; + attr = ippNextAttribute(dinfo->attrs)) + { + if (!attr->name || attr->group_tag != IPP_TAG_PRINTER) + continue; + + if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION) + continue; /* TODO: STR #4096 */ + + if ((nameptr = attr->name + strlen(attr->name) - 8) <= attr->name || + strcmp(nameptr, "-default")) + continue; + + strlcpy(name, attr->name, sizeof(name)); + if ((nameptr = name + strlen(name) - 8) <= name || + strcmp(nameptr, "-default")) + continue; + + *nameptr = '\0'; + + if (ippAttributeString(attr, value, sizeof(value)) >= sizeof(value)) + continue; + + dinfo->num_defaults = cupsAddOption(name, value, dinfo->num_defaults, + &dinfo->defaults); + } +} + + /* * 'cups_create_media_db()' - Create the media database. */ @@ -1159,6 +1558,208 @@ cups_is_close_media_db( } +/* + * 'cups_test_constraints()' - Test constraints. + * + * TODO: STR #4096 - Need to properly support media-col contraints... + */ + +static cups_array_t * /* O - Active constraints */ +cups_test_constraints( + cups_dinfo_t *dinfo, /* I - Destination information */ + const char *new_option, /* I - Newly selected option */ + const char *new_value, /* I - Newly selected value */ + int num_options, /* I - Number of options */ + cups_option_t *options, /* I - Options */ + int *num_conflicts, /* O - Number of conflicting options */ + cups_option_t **conflicts) /* O - Conflicting options */ +{ + int i, /* Looping var */ + match; /* Value matches? */ + int num_matching; /* Number of matching options */ + cups_option_t *matching; /* Matching options */ + _cups_dconstres_t *c; /* Current constraint */ + cups_array_t *active = NULL; /* Active constraints */ + ipp_attribute_t *attr; /* Current attribute */ + _ipp_value_t *attrval; /* Current attribute value */ + const char *value; /* Current value */ + char temp[1024]; /* Temporary string */ + int int_value; /* Integer value */ + int xres_value, /* Horizontal resolution */ + yres_value; /* Vertical resolution */ + ipp_res_t units_value; /* Resolution units */ + + + for (c = (_cups_dconstres_t *)cupsArrayFirst(dinfo->constraints); + c; + c = (_cups_dconstres_t *)cupsArrayNext(dinfo->constraints)) + { + num_matching = 0; + matching = NULL; + + for (attr = ippFirstAttribute(c->collection); + attr; + attr = ippNextAttribute(c->collection)) + { + if (attr->value_tag == IPP_TAG_BEGIN_COLLECTION) + break; /* TODO: STR #4096 */ + + /* + * Get the value for the current attribute in the constraint... + */ + + if (new_option && new_value && !strcmp(attr->name, new_option)) + value = new_value; + else if ((value = cupsGetOption(attr->name, num_options, + options)) == NULL) + value = cupsGetOption(attr->name, dinfo->num_defaults, dinfo->defaults); + + if (!value) + { + /* + * Not set so this constraint does not apply... + */ + + break; + } + + match = 0; + + switch (attr->value_tag) + { + case IPP_TAG_INTEGER : + case IPP_TAG_ENUM : + int_value = atoi(value); + + for (i = attr->num_values, attrval = attr->values; + i > 0; + i --, attrval ++) + { + if (attrval->integer == int_value) + { + match = 1; + break; + } + } + break; + + case IPP_TAG_BOOLEAN : + int_value = !strcmp(value, "true"); + + for (i = attr->num_values, attrval = attr->values; + i > 0; + i --, attrval ++) + { + if (attrval->boolean == int_value) + { + match = 1; + break; + } + } + break; + + case IPP_TAG_RANGE : + int_value = atoi(value); + + for (i = attr->num_values, attrval = attr->values; + i > 0; + i --, attrval ++) + { + if (int_value >= attrval->range.lower && + int_value <= attrval->range.upper) + { + match = 1; + break; + } + } + break; + + case IPP_TAG_RESOLUTION : + if (sscanf(value, "%dx%d%15s", &xres_value, &yres_value, temp) != 3) + { + if (sscanf(value, "%d%15s", &xres_value, temp) != 2) + break; + + yres_value = xres_value; + } + + if (!strcmp(temp, "dpi")) + units_value = IPP_RES_PER_INCH; + else if (!strcmp(temp, "dpc") || !strcmp(temp, "dpcm")) + units_value = IPP_RES_PER_CM; + else + break; + + for (i = attr->num_values, attrval = attr->values; + i > 0; + i --, attrval ++) + { + if (attrval->resolution.xres == xres_value && + attrval->resolution.yres == yres_value && + attrval->resolution.units == units_value) + { + match = 1; + break; + } + } + break; + + case IPP_TAG_TEXT : + case IPP_TAG_NAME : + case IPP_TAG_KEYWORD : + case IPP_TAG_CHARSET : + case IPP_TAG_URI : + case IPP_TAG_URISCHEME : + case IPP_TAG_MIMETYPE : + case IPP_TAG_LANGUAGE : + case IPP_TAG_TEXTLANG : + case IPP_TAG_NAMELANG : + for (i = attr->num_values, attrval = attr->values; + i > 0; + i --, attrval ++) + { + if (!strcmp(attrval->string.text, value)) + { + match = 1; + break; + } + } + break; + + default : + break; + } + + if (!match) + break; + + num_matching = cupsAddOption(attr->name, value, num_matching, &matching); + } + + if (!attr) + { + if (!active) + active = cupsArrayNew(NULL, NULL); + + cupsArrayAdd(active, c); + + if (num_conflicts && conflicts) + { + cups_option_t *moption; /* Matching option */ + + for (i = num_matching, moption = matching; i > 0; i --, moption ++) + *num_conflicts = cupsAddOption(moption->name, moption->value, + *num_conflicts, conflicts); + } + } + + cupsFreeOptions(num_matching, matching); + } + + return (active); +} + + /* * End of "$Id$". */ diff --git a/cups/dest.c b/cups/dest.c index ee9d57208..3bc89d5da 100644 --- a/cups/dest.c +++ b/cups/dest.c @@ -62,13 +62,17 @@ * cups_block_cb() - Enumeration callback for block API. * cups_compare_dests() - Compare two destinations. * cups_dnssd_browse_cb() - Browse for printers. + * cups_dnssd_browse_cb() - Browse for printers. + * cups_dnssd_client_cb() - Avahi client callback function. * cups_dnssd_compare_device() - Compare two devices. * cups_dnssd_free_device() - Free the memory used by a device. * cups_dnssd_get_device() - Lookup a device and create it as needed. * cups_dnssd_local_cb() - Browse for local printers. + * cups_dnssd_poll_cb() - Wait for input on the specified file + * descriptors. * cups_dnssd_query_cb() - Process query data. - * cups_dnssd_resolve() - Resolve a Bonjour printer URI. - * cups_dnssd_resolve_cb() - See if we should continue resolving. + * cups_dnssd_resolve() - Resolve a Bonjour printer URI. + * cups_dnssd_resolve_cb() - See if we should continue resolving. * cups_dnssd_unquote() - Unquote a name string. * cups_find_dest() - Find a destination using a binary search. * cups_get_default() - Get the default destination from an @@ -97,6 +101,16 @@ # include #endif /* HAVE_DNSSD */ +#ifdef HAVE_AVAHI +# include +# include +# include +# include +# include +# include +#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX +#endif /* HAVE_AVAHI */ + /* * Constants... @@ -117,7 +131,7 @@ * Types... */ -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) typedef enum _cups_dnssd_state_e /* Enumerated device state */ { _CUPS_DNSSD_NEW, @@ -125,12 +139,19 @@ typedef enum _cups_dnssd_state_e /* Enumerated device state */ _CUPS_DNSSD_PENDING, _CUPS_DNSSD_ACTIVE, _CUPS_DNSSD_LOCAL, + _CUPS_DNSSD_INCOMPATIBLE, _CUPS_DNSSD_ERROR } _cups_dnssd_state_t; typedef struct _cups_dnssd_data_s /* Enumeration data */ { +# ifdef HAVE_DNSSD DNSServiceRef main_ref; /* Main service reference */ +# else /* HAVE_AVAHI */ + AvahiSimplePoll *simple_poll; /* Polling interface */ + AvahiClient *client; /* Client information */ + int got_data; /* Did we get data? */ +# endif /* HAVE_DNSSD */ cups_dest_cb_t cb; /* Callback */ void *user_data; /* User data pointer */ cups_ptype_t type, /* Printer type filter */ @@ -141,7 +162,11 @@ typedef struct _cups_dnssd_data_s /* Enumeration data */ typedef struct _cups_dnssd_device_s /* Enumerated device */ { _cups_dnssd_state_t state; /* State of device listing */ +# ifdef HAVE_DNSSD DNSServiceRef ref; /* Service reference for query */ +# else /* HAVE_AVAHI */ + AvahiRecordBrowser *ref; /* Browser for query */ +# endif /* HAVE_DNSSD */ char *domain, /* Domain name */ *fullName, /* Full name */ *regtype; /* Registration type */ @@ -165,8 +190,8 @@ typedef struct _cups_dnssd_resolve_s /* Data for resolving URI */ static CFArrayRef appleCopyLocations(void); static CFStringRef appleCopyNetwork(void); static char *appleGetPaperSize(char *name, int namesize); -static CFStringRef appleGetPrinter(CFArrayRef locations, CFStringRef network, - CFIndex *locindex); +static CFStringRef appleGetPrinter(CFArrayRef locations, + CFStringRef network, CFIndex *locindex); #endif /* __APPLE__ */ static cups_dest_t *cups_add_dest(const char *name, const char *instance, int *num_dests, cups_dest_t **dests); @@ -175,7 +200,8 @@ static int cups_block_cb(cups_dest_block_t block, unsigned flags, cups_dest_t *dest); #endif /* __BLOCKS__ */ static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b); -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) +# ifdef HAVE_DNSSD static void cups_dnssd_browse_cb(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -184,6 +210,20 @@ static void cups_dnssd_browse_cb(DNSServiceRef sdRef, const char *regtype, const char *replyDomain, void *context); +# else /* HAVE_AVAHI */ +static void cups_dnssd_browse_cb(AvahiServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *serviceName, + const char *regtype, + const char *replyDomain, + AvahiLookupResultFlags flags, + void *context); +static void cups_dnssd_client_cb(AvahiClient *client, + AvahiClientState state, + void *context); +# endif /* HAVE_DNSSD */ static int cups_dnssd_compare_devices(_cups_dnssd_device_t *a, _cups_dnssd_device_t *b); static void cups_dnssd_free_device(_cups_dnssd_device_t *device, @@ -193,6 +233,7 @@ static _cups_dnssd_device_t * const char *serviceName, const char *regtype, const char *replyDomain); +# ifdef HAVE_DNSSD static void cups_dnssd_local_cb(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -209,13 +250,27 @@ static void cups_dnssd_query_cb(DNSServiceRef sdRef, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context); +# else /* HAVE_AVAHI */ +static int cups_dnssd_poll_cb(struct pollfd *pollfds, + unsigned int num_pollfds, + int timeout, void *context); +static void cups_dnssd_query_cb(AvahiRecordBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, uint16_t rrclass, + uint16_t rrtype, const void *rdata, + size_t rdlen, + AvahiLookupResultFlags flags, + void *context); +# endif /* HAVE_DNSSD */ static const char *cups_dnssd_resolve(cups_dest_t *dest, const char *uri, int msec, int *cancel, cups_dest_cb_t cb, void *user_data); static int cups_dnssd_resolve_cb(void *context); static void cups_dnssd_unquote(char *dst, const char *src, size_t dstsize); -#endif /* HAVE_DNSSD */ +#endif /* HAVE_DNSSD || HAVE_AVAHI */ static int cups_find_dest(const char *name, const char *instance, int num_dests, cups_dest_t *dests, int prev, int *rdiff); @@ -605,14 +660,14 @@ cupsConnectDest( return (NULL); } -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (strstr(uri, "._tcp")) { if ((uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data)) == NULL) return (NULL); } -#endif /* HAVE_DNSSD */ +#endif /* HAVE_DNSSD || HAVE_AVAHI */ if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), @@ -834,27 +889,34 @@ cupsEnumDests( num_dests; /* Number of destinations */ cups_dest_t *dests = NULL, /* Destinations */ *dest; /* Current destination */ -#ifdef HAVE_DNSSD - int nfds, /* Number of files responded */ - count, /* Number of queries started */ +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) + int count, /* Number of queries started */ remaining; /* Remainder of timeout */ _cups_dnssd_data_t data; /* Data for callback */ _cups_dnssd_device_t *device; /* Current device */ - int main_fd; /* File descriptor for lookups */ +# ifdef HAVE_DNSSD + int nfds, /* Number of files responded */ + main_fd; /* File descriptor for lookups */ DNSServiceRef ipp_ref, /* IPP browser */ local_ipp_ref; /* Local IPP browser */ -# ifdef HAVE_SSL +# ifdef HAVE_SSL DNSServiceRef ipps_ref, /* IPPS browser */ local_ipps_ref; /* Local IPPS browser */ -# endif /* HAVE_SSL */ -# ifdef HAVE_POLL +# endif /* HAVE_SSL */ +# ifdef HAVE_POLL struct pollfd pfd; /* Polling data */ -# else +# else fd_set input; /* Input set for select() */ struct timeval timeout; /* Timeout for select() */ -# endif /* HAVE_POLL */ -#endif /* HAVE_DNSSD */ - +# endif /* HAVE_POLL */ +# else /* HAVE_AVAHI */ + int error; /* Error value */ + AvahiServiceBrowser *ipp_ref; /* IPP browser */ +# ifdef HAVE_SSL + AvahiServiceBrowser *ipps_ref; /* IPPS browser */ +# endif /* HAVE_SSL */ +# endif /* HAVE_DNSSD */ +#endif /* HAVE_DNSSD || HAVE_AVAHI */ /* * Range check input... @@ -884,7 +946,7 @@ cupsEnumDests( if (i > 0 || msec == 0) return (1); -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) /* * Get Bonjour-shared printers... */ @@ -895,6 +957,7 @@ cupsEnumDests( NULL, NULL, 0, NULL, (cups_afree_func_t)cups_dnssd_free_device); +# ifdef HAVE_DNSSD if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError) return (0); @@ -902,27 +965,56 @@ cupsEnumDests( ipp_ref = data.main_ref; DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0, - "_ipp._tcp,_cups", NULL, + "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data); local_ipp_ref = data.main_ref; DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, - "_ipp._tcp,_cups", NULL, + "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_local_cb, &data); -# ifdef HAVE_SSL +# ifdef HAVE_SSL ipps_ref = data.main_ref; DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0, - "_ipps._tcp,_cups", NULL, + "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data); local_ipps_ref = data.main_ref; DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, - "_ipps._tcp,_cups", NULL, + "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_local_cb, &data); -# endif /* HAVE_SSL */ +# endif /* HAVE_SSL */ + +# else /* HAVE_AVAHI */ + if ((data.simple_poll = avahi_simple_poll_new()) == NULL) + { + DEBUG_puts("cupsEnumDests: Unable to create Avahi simple poll object."); + return (1); + } + + avahi_simple_poll_set_func(data.simple_poll, cups_dnssd_poll_cb, &data); + + data.client = avahi_client_new(avahi_simple_poll_get(data.simple_poll), + 0, cups_dnssd_client_cb, &data, + &error); + if (!data.client) + { + DEBUG_puts("cupsEnumDests: Unable to create Avahi client."); + avahi_simple_poll_free(data.simple_poll); + return (1); + } + + ipp_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, "_ipp._tcp", NULL, + 0, cups_dnssd_browse_cb, &data); +# ifdef HAVE_SSL + ipps_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, "_ipps._tcp", NULL, + 0, cups_dnssd_browse_cb, &data); +# endif /* HAVE_SSL */ +# endif /* HAVE_DNSSD */ if (msec < 0) remaining = INT_MAX; @@ -935,13 +1027,14 @@ cupsEnumDests( * Check for input... */ -# ifdef HAVE_POLL +# ifdef HAVE_DNSSD +# ifdef HAVE_POLL pfd.fd = main_fd; pfd.events = POLLIN; nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining); -# else +# else FD_ZERO(&input); FD_SET(main_fd, &input); @@ -949,13 +1042,30 @@ cupsEnumDests( timeout.tv_usec = remaining > 250 ? 250000 : remaining * 1000; nfds = select(main_fd + 1, &input, NULL, NULL, &timeout); -# endif /* HAVE_POLL */ +# endif /* HAVE_POLL */ if (nfds > 0) DNSServiceProcessResult(data.main_ref); else if (nfds == 0) remaining -= 250; +# else /* HAVE_AVAHI */ + data.got_data = 0; + + if ((error = avahi_simple_poll_iterate(data.simple_poll, 250)) > 0) + { + /* + * We've been told to exit the loop. Perhaps the connection to + * Avahi failed. + */ + + break; + } + + if (!data.got_data) + remaining -= 250; +# endif /* HAVE_DNSSD */ + for (device = (_cups_dnssd_device_t *)cupsArrayFirst(data.devices), count = 0; device; @@ -966,10 +1076,11 @@ cupsEnumDests( if (!device->ref && device->state == _CUPS_DNSSD_NEW) { - device->ref = data.main_ref; - DEBUG_printf(("1cupsEnumDests: Querying '%s'.", device->fullName)); +# ifdef HAVE_DNSSD + device->ref = data.main_ref; + if (DNSServiceQueryRecord(&(device->ref), kDNSServiceFlagsShareConnection, 0, device->fullName, @@ -987,14 +1098,39 @@ cupsEnumDests( DEBUG_puts("1cupsEnumDests: Query failed."); } + +# else /* HAVE_AVAHI */ + if ((device->ref = avahi_record_browser_new(data.client, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + device->fullName, + AVAHI_DNS_CLASS_IN, + AVAHI_DNS_TYPE_TXT, + 0, + cups_dnssd_query_cb, + &data)) != NULL) + { + count ++; + } + else + { + device->state = _CUPS_DNSSD_ERROR; + + DEBUG_printf(("1cupsEnumDests: Query failed: %s", + avahi_strerror(avahi_client_errno(data.client)))); + } +# endif /* HAVE_DNSSD */ } else if (device->ref && device->state == _CUPS_DNSSD_PENDING) { - if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest)) - { - remaining = -1; - break; - } + if ((device->type & mask) == type) + { + if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest)) + { + remaining = -1; + break; + } + } device->state = _CUPS_DNSSD_ACTIVE; } @@ -1003,16 +1139,27 @@ cupsEnumDests( cupsArrayDelete(data.devices); +# ifdef HAVE_DNSSD DNSServiceRefDeallocate(ipp_ref); DNSServiceRefDeallocate(local_ipp_ref); -# ifdef HAVE_SSL +# ifdef HAVE_SSL DNSServiceRefDeallocate(ipp_ref); DNSServiceRefDeallocate(local_ipp_ref); -# endif /* HAVE_SSL */ +# endif /* HAVE_SSL */ DNSServiceRefDeallocate(data.main_ref); -#endif /* HAVE_DNSSD */ + +# else /* HAVE_AVAHI */ + avahi_service_browser_free(ipp_ref); +# ifdef HAVE_SSL + avahi_service_browser_free(ipps_ref); +# endif /* HAVE_SSL */ + + avahi_client_free(data.client); + avahi_simple_poll_free(data.simple_poll); +# endif /* HAVE_DNSSD */ +#endif /* HAVE_DNSSD || HAVE_DNSSD */ return (1); } @@ -2473,7 +2620,8 @@ cups_compare_dests(cups_dest_t *a, /* I - First destination */ } -#ifdef HAVE_DNSSD +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) +# ifdef HAVE_DNSSD /* * 'cups_dnssd_browse_cb()' - Browse for printers. */ @@ -2514,6 +2662,102 @@ cups_dnssd_browse_cb( } +# else /* HAVE_AVAHI */ +/* + * 'cups_dnssd_browse_cb()' - Browse for printers. + */ + +static void +cups_dnssd_browse_cb( + AvahiServiceBrowser *browser, /* I - Browser */ + AvahiIfIndex interface, /* I - Interface index (unused) */ + AvahiProtocol protocol, /* I - Network protocol (unused) */ + AvahiBrowserEvent event, /* I - What happened */ + const char *name, /* I - Service name */ + const char *type, /* I - Registration type */ + const char *domain, /* I - Domain */ + AvahiLookupResultFlags flags, /* I - Flags */ + void *context) /* I - Devices array */ +{ + AvahiClient *client = avahi_service_browser_get_client(browser); + /* Client information */ + _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context; + /* Enumeration data */ + + + (void)interface; + (void)protocol; + (void)context; + + switch (event) + { + case AVAHI_BROWSER_FAILURE: + DEBUG_printf(("cups_dnssd_browse_cb: %s", + avahi_strerror(avahi_client_errno(client)))); + avahi_simple_poll_quit(data->simple_poll); + break; + + case AVAHI_BROWSER_NEW: + /* + * This object is new on the network. + */ + + if (flags & AVAHI_LOOKUP_RESULT_LOCAL) + { + /* + * This comes from the local machine so ignore it. + */ + + DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".", + name)); + } + else + { + /* + * Create a device entry for it if it doesn't yet exist. + */ + + cups_dnssd_get_device(data, name, type, domain); + } + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } +} + + +/* + * 'cups_dnssd_client_cb()' - Avahi client callback function. + */ + +static void +cups_dnssd_client_cb( + AvahiClient *client, /* I - Client information (unused) */ + AvahiClientState state, /* I - Current state */ + void *context) /* I - User data (unused) */ +{ + _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context; + /* Enumeration data */ + + + (void)client; + + /* + * If the connection drops, quit. + */ + + if (state == AVAHI_CLIENT_FAILURE) + { + DEBUG_puts("cups_dnssd_client_cb: Avahi connection failed."); + avahi_simple_poll_quit(data->simple_poll); + } +} +# endif /* HAVE_DNSSD */ + + /* * 'cups_dnssd_compare_device()' - Compare two devices. */ @@ -2539,8 +2783,13 @@ cups_dnssd_free_device( DEBUG_printf(("5cups_dnssd_free_device(device=%p(%s), data=%p)", device, device->dest.name, data)); +# ifdef HAVE_DNSSD if (device->ref) DNSServiceRefDeallocate(device->ref); +# else /* HAVE_AVAHI */ + if (device->ref) + avahi_record_browser_free(device->ref); +# endif /* HAVE_DNSSD */ _cupsStrFree(device->domain); _cupsStrFree(device->fullName); @@ -2650,14 +2899,25 @@ cups_dnssd_get_device( * Set the "full name" of this service, which is used for queries... */ +# ifdef HAVE_DNSSD DNSServiceConstructFullName(fullName, device->dest.name, device->regtype, device->domain); +# else /* HAVE_AVAHI */ + avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, + regtype, replyDomain); +# endif /* HAVE_DNSSD */ + _cupsStrFree(device->fullName); device->fullName = _cupsStrAlloc(fullName); if (device->ref) { +# ifdef HAVE_DNSSD DNSServiceRefDeallocate(device->ref); +# else /* HAVE_AVAHI */ + avahi_record_browser_free(device->ref); +# endif /* HAVE_DNSSD */ + device->ref = 0; } @@ -2671,6 +2931,7 @@ cups_dnssd_get_device( } +# ifdef HAVE_DNSSD /* * 'cups_dnssd_local_cb()' - Browse for local printers. */ @@ -2728,12 +2989,51 @@ cups_dnssd_local_cb( device->state = _CUPS_DNSSD_LOCAL; } +# endif /* HAVE_DNSSD */ + + +# ifdef HAVE_AVAHI +/* + * 'cups_dnssd_poll_cb()' - Wait for input on the specified file descriptors. + * + * Note: This function is needed because avahi_simple_poll_iterate is broken + * and always uses a timeout of 0 (!) milliseconds. + * (Avahi Ticket #364) + */ + +static int /* O - Number of file descriptors matching */ +cups_dnssd_poll_cb( + struct pollfd *pollfds, /* I - File descriptors */ + unsigned int num_pollfds, /* I - Number of file descriptors */ + int timeout, /* I - Timeout in milliseconds (unused) */ + void *context) /* I - User data (unused) */ +{ + _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context; + /* Enumeration data */ + int val; /* Return value */ + + + (void)timeout; + + val = poll(pollfds, num_pollfds, 250); + + if (val < 0) + { + DEBUG_printf(("cups_dnssd_poll_cb: %s", strerror(errno))); + } + else if (val > 0) + data->got_data = 1; + + return (val); +} +# endif /* HAVE_AVAHI */ /* * 'cups_dnssd_query_cb()' - Process query data. */ +# ifdef HAVE_DNSSD static void cups_dnssd_query_cb( DNSServiceRef sdRef, /* I - Service reference */ @@ -2748,6 +3048,25 @@ cups_dnssd_query_cb( uint32_t ttl, /* I - Time-to-live */ void *context) /* I - Enumeration data */ { +# else /* HAVE_AVAHI */ +static void +cups_dnssd_query_cb( + AvahiRecordBrowser *browser, /* I - Record browser */ + AvahiIfIndex interfaceIndex, + /* I - Interface index (unused) */ + AvahiProtocol protocol, /* I - Network protocol (unused) */ + AvahiBrowserEvent event, /* I - What happened? */ + const char *fullName, /* I - Service name */ + uint16_t rrclass, /* I - Record class */ + uint16_t rrtype, /* I - Record type */ + const void *rdata, /* I - TXT record */ + size_t rdlen, /* I - Length of TXT record */ + AvahiLookupResultFlags flags, /* I - Flags */ + void *context) /* I - Enumeration data */ +{ + AvahiClient *client = avahi_record_browser_get_client(browser); + /* Client information */ +# endif /* HAVE_DNSSD */ _cups_dnssd_data_t *data = (_cups_dnssd_data_t *)context; /* Enumeration data */ char name[1024], /* Service name */ @@ -2756,6 +3075,7 @@ cups_dnssd_query_cb( *device; /* Device */ +# ifdef HAVE_DNSSD DEBUG_printf(("5cups_dnssd_query_cb(sdRef=%p, flags=%x, " "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", " "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, " @@ -2769,6 +3089,27 @@ cups_dnssd_query_cb( if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) return; +# else /* HAVE_AVAHI */ + DEBUG_printf(("5cups_dnssd_query_cb(browser=%p, interfaceIndex=%d, " + "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, " + "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)", + browser, interfaceIndex, protocol, event, fullName, rrclass, + rrtype, rdata, (unsigned)rdlen, flags, context)); + + /* + * Only process "add" data... + */ + + if (event != AVAHI_BROWSER_NEW) + { + if (event == AVAHI_BROWSER_FAILURE) + DEBUG_printf(("cups_dnssd_query_cb: %s", + avahi_strerror(avahi_client_errno(client)))); + + return; + } +# endif /* HAVE_DNSSD */ + /* * Lookup the service in the devices array. */ @@ -2783,8 +3124,7 @@ cups_dnssd_query_cb( if ((device = cupsArrayFind(data->devices, &dkey)) != NULL) { /* - * Found it, pull out the priority and make and model from the TXT - * record and save it... + * Found it, pull out the make and model from the TXT record and save it... */ const uint8_t *txt, /* Pointer into data */ @@ -2798,6 +3138,10 @@ cups_dnssd_query_cb( model[256], /* Model */ uriname[1024], /* Name for URI */ uri[1024]; /* Printer URI */ + cups_ptype_t type = CUPS_PRINTER_REMOTE | CUPS_PRINTER_BW; + /* Printer type */ + int saw_printer_type = 0; + /* Did we see a printer-type key? */ device->state = _CUPS_DNSSD_PENDING; make_and_model[0] = '\0'; @@ -2871,18 +3215,102 @@ cups_dnssd_query_cb( if ((ptr = strchr(model, ',')) != NULL) *ptr = '\0'; } + else if (!_cups_strcasecmp(key, "note")) + device->dest.num_options = cupsAddOption("printer-location", value, + device->dest.num_options, + &device->dest.options); + else if (!_cups_strcasecmp(key, "pdl")) + { + /* + * Look for PDF-capable printers; only PDF-capable printers are shown. + */ + + const char *start, *next; /* Pointer into value */ + int have_pdf = 0; /* Have PDF? */ + + for (start = value; start && *start; start = next) + { + if (!_cups_strncasecmp(start, "application/pdf", 15) && + (!start[15] || start[15] == ',')) + { + have_pdf = 1; + break; + } + + if ((next = strchr(start, ',')) != NULL) + next ++; + } + + if (!have_pdf) + device->state = _CUPS_DNSSD_INCOMPATIBLE; + } else if (!_cups_strcasecmp(key, "printer-type")) { - device->dest.num_options = cupsAddOption("printer-type", value, - device->dest.num_options, - &device->dest.options); + /* + * Value is either NNNN or 0xXXXX + */ + + saw_printer_type = 1; + type = strtol(value, NULL, 0); + } + else if (!saw_printer_type) + { + if (!_cups_strcasecmp(key, "air") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_AUTHENTICATED; + else if (!_cups_strcasecmp(key, "bind") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_BIND; + else if (!_cups_strcasecmp(key, "collate") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_COLLATE; + else if (!_cups_strcasecmp(key, "color") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_COLOR; + else if (!_cups_strcasecmp(key, "copies") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_COPIES; + else if (!_cups_strcasecmp(key, "duplex") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_DUPLEX; + else if (!_cups_strcasecmp(key, "fax") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_MFP; + else if (!_cups_strcasecmp(key, "papercustom") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_VARIABLE; + else if (!_cups_strcasecmp(key, "papermax")) + { + if (!_cups_strcasecmp(value, "legal-a4")) + type |= CUPS_PRINTER_SMALL; + else if (!_cups_strcasecmp(value, "isoc-a2")) + type |= CUPS_PRINTER_MEDIUM; + else if (!_cups_strcasecmp(value, ">isoc-a2")) + type |= CUPS_PRINTER_LARGE; + } + else if (!_cups_strcasecmp(key, "punch") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_PUNCH; + else if (!_cups_strcasecmp(key, "scan") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_MFP; + else if (!_cups_strcasecmp(key, "sort") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_SORT; + else if (!_cups_strcasecmp(key, "staple") && + !_cups_strcasecmp(value, "t")) + type |= CUPS_PRINTER_STAPLE; } } /* - * Save the make-and-model... + * Save the printer-xxx values... */ + device->dest.num_options = cupsAddOption("printer-info", name, + device->dest.num_options, + &device->dest.options); + if (make_and_model[0]) { strlcat(make_and_model, " ", sizeof(make_and_model)); @@ -2899,6 +3327,12 @@ cups_dnssd_query_cb( device->dest.num_options, &device->dest.options); + device->type = type; + snprintf(value, sizeof(value), "%u", type); + device->dest.num_options = cupsAddOption("printer-type", value, + device->dest.num_options, + &device->dest.options); + /* * Save the URI... */ @@ -2906,7 +3340,7 @@ cups_dnssd_query_cb( cups_dnssd_unquote(uriname, device->fullName, sizeof(uriname)); httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), !strcmp(device->regtype, "_ipps._tcp") ? "ipps" : "ipp", - NULL, uriname, 0, "/cups"); + NULL, uriname, 0, saw_printer_type ? "/cups" : "/"); DEBUG_printf(("6cups_dnssd_query: printer-uri-supported=\"%s\"", uri)); diff --git a/cups/http-addr.c b/cups/http-addr.c index 9e1ef660f..4b09d8eac 100644 --- a/cups/http-addr.c +++ b/cups/http-addr.c @@ -34,12 +34,10 @@ #ifdef HAVE_RESOLV_H # include #endif /* HAVE_RESOLV_H */ -#ifdef HAVE_COREFOUNDATION +#ifdef __APPLE__ # include -#endif /* HAVE_COREFOUNDATION */ -#ifdef HAVE_SYSTEMCONFIGURATION # include -#endif /* HAVE_SYSTEMCONFIGURATION */ +#endif /* __APPLE__ */ /* diff --git a/cups/http.c b/cups/http.c index 945404cdd..9957a16ae 100644 --- a/cups/http.c +++ b/cups/http.c @@ -2170,14 +2170,10 @@ httpRead2(http_t *http, /* I - Connection to server */ { if (http->data_encoding == HTTP_ENCODE_CHUNKED) httpGets(len, sizeof(len), http); - - if (http->data_encoding != HTTP_ENCODE_CHUNKED) - { - if (http->state == HTTP_POST_RECV) - http->state ++; - else - http->state = HTTP_WAITING; - } + else if (http->state == HTTP_POST_RECV) + http->state ++; + else + http->state = HTTP_WAITING; } return (bytes); @@ -2356,6 +2352,23 @@ httpReconnect2(http_t *http, /* I - Connection to server */ http->fd = -1; } + /* + * Reset all state (except fields, which may be reused)... + */ + + http->state = HTTP_WAITING; + http->status = HTTP_CONTINUE; + http->version = HTTP_1_1; + http->keep_alive = HTTP_KEEPALIVE_OFF; + memset(&http->_hostaddr, 0, sizeof(http->_hostaddr)); + http->data_encoding = HTTP_ENCODE_LENGTH; + http->_data_remaining = 0; + http->used = 0; + http->expect = 0; + http->data_remaining = 0; + http->hostaddr = NULL; + http->wused = 0; + /* * Connect to the server... */ @@ -2394,8 +2407,6 @@ httpReconnect2(http_t *http, /* I - Connection to server */ http->hostaddr = &(addr->addr); http->error = 0; - http->status = HTTP_CONTINUE; - http->state = HTTP_WAITING; #ifdef HAVE_SSL if (http->encryption == HTTP_ENCRYPT_ALWAYS) @@ -3968,7 +3979,9 @@ http_setup_ssl(http_t *http) /* I - Connection to server */ http->tls = SSL_new(context); SSL_set_bio(http->tls, bio, bio); +# ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME SSL_set_tlsext_host_name(http->tls, hostname); +# endif /* HAVE_SSL_SET_TLSEXT_HOST_NAME */ if (SSL_connect(http->tls) != 1) { diff --git a/cups/ipp.c b/cups/ipp.c index dd3e4e0d4..0a1e31c56 100644 --- a/cups/ipp.c +++ b/cups/ipp.c @@ -2577,7 +2577,7 @@ ippReadIO(void *src, /* I - Data source */ */ _cupsSetError(IPP_INTERNAL_ERROR, _("IPP extension tag larger than 0x7FFFFFFF."), 1); - DEBUG_printf(("1ippReadIO: bad name length %d.", n)); + DEBUG_printf(("1ippReadIO: bad tag 0x%x.", tag)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -2846,7 +2846,7 @@ ippReadIO(void *src, /* I - Data source */ else _cupsSetError(IPP_INTERNAL_ERROR, _("IPP enum value not 4 bytes."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad integer value length %d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -2872,7 +2872,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_INTERNAL_ERROR, _("IPP boolean value not 1 byte."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad boolean value length %d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -2913,11 +2913,14 @@ ippReadIO(void *src, /* I - Data source */ case IPP_TAG_CHARSET : case IPP_TAG_LANGUAGE : case IPP_TAG_MIMETYPE : - if ((*cb)(src, buffer, n) < n) - { - DEBUG_puts("1ippReadIO: unable to read string value."); - _cupsBufferRelease((char *)buffer); - return (IPP_ERROR); + if (n > 0) + { + if ((*cb)(src, buffer, n) < n) + { + DEBUG_puts("1ippReadIO: unable to read string value."); + _cupsBufferRelease((char *)buffer); + return (IPP_ERROR); + } } buffer[n] = '\0'; @@ -2929,7 +2932,7 @@ ippReadIO(void *src, /* I - Data source */ if (n != 11) { _cupsSetError(IPP_INTERNAL_ERROR, _("IPP date value not 11 bytes."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad date value length %d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -2947,7 +2950,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_INTERNAL_ERROR, _("IPP resolution value not 9 bytes."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad resolution value length %d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -2974,7 +2977,8 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_INTERNAL_ERROR, _("IPP rangeOfInteger value not 8 bytes."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad rangeOfInteger value length " + "%d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -3006,7 +3010,8 @@ ippReadIO(void *src, /* I - Data source */ _cupsSetError(IPP_INTERNAL_ERROR, _("IPP nameWithLanguage value less than " "minimum 4 bytes."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad stringWithLanguage value " + "length %d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -3038,7 +3043,8 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_INTERNAL_ERROR, _("IPP language length overflows value."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad language value length %d.", + n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -3055,7 +3061,7 @@ ippReadIO(void *src, /* I - Data source */ { _cupsSetError(IPP_INTERNAL_ERROR, _("IPP string length overflows value."), 1); - DEBUG_printf(("1ippReadIO: bad value length %d.", n)); + DEBUG_printf(("1ippReadIO: bad string value length %d.", n)); _cupsBufferRelease((char *)buffer); return (IPP_ERROR); } @@ -3110,7 +3116,15 @@ ippReadIO(void *src, /* I - Data source */ * we need to carry over... */ - if ((*cb)(src, buffer, n) < n) + if (n == 0) + { + _cupsSetError(IPP_INTERNAL_ERROR, + _("IPP memberName value is empty."), 1); + DEBUG_puts("1ippReadIO: Empty member name value."); + _cupsBufferRelease((char *)buffer); + return (IPP_ERROR); + } + else if ((*cb)(src, buffer, n) < n) { DEBUG_puts("1ippReadIO: Unable to read member name value."); _cupsBufferRelease((char *)buffer); @@ -5287,9 +5301,8 @@ ipp_read_http(http_t *http, /* I - Client connection */ ipp_uchar_t *buffer, /* O - Buffer for data */ size_t length) /* I - Total length */ { - int tbytes, /* Total bytes read */ - bytes; /* Bytes read this pass */ - char len[32]; /* Length string */ + int tbytes, /* Total bytes read */ + bytes; /* Bytes read this pass */ DEBUG_printf(("7ipp_read_http(http=%p, buffer=%p, length=%d)", @@ -5309,87 +5322,36 @@ ipp_read_http(http_t *http, /* I - Client connection */ if (http->state == HTTP_WAITING) break; - if (http->used > 0 && http->data_encoding == HTTP_ENCODE_LENGTH) + if (http->used == 0 && !http->blocking) { /* - * Do "fast read" from HTTP buffer directly... + * Wait up to 10 seconds for more data on non-blocking sockets... */ - if (http->used > (int)(length - tbytes)) - bytes = (int)(length - tbytes); - else - bytes = http->used; - - if (bytes == 1) - buffer[0] = http->buffer[0]; - else - memcpy(buffer, http->buffer, bytes); - - http->used -= bytes; - http->data_remaining -= bytes; - - if (http->data_remaining <= INT_MAX) - http->_data_remaining = (int)http->data_remaining; - else - http->_data_remaining = INT_MAX; - - if (http->used > 0) - memmove(http->buffer, http->buffer + bytes, http->used); - - if (http->data_remaining == 0) - { - if (http->data_encoding == HTTP_ENCODE_CHUNKED) - { - /* - * Get the trailing CR LF after the chunk... - */ - - if (!httpGets(len, sizeof(len), http)) - return (-1); - } - - if (http->data_encoding != HTTP_ENCODE_CHUNKED) - { - if (http->state == HTTP_POST_RECV) - http->state ++; - else - http->state = HTTP_WAITING; - } - } - } - else - { - if (!http->blocking) + if (!httpWait(http, 10000)) { /* - * Wait up to 10 seconds for more data on non-blocking sockets... + * Signal no data... */ - if (!httpWait(http, 10000)) - { - /* - * Signal no data... - */ - - bytes = -1; - break; - } + bytes = -1; + break; } + } - if ((bytes = httpRead2(http, (char *)buffer, length - tbytes)) < 0) - { + if ((bytes = httpRead2(http, (char *)buffer, length - tbytes)) < 0) + { #ifdef WIN32 - break; + break; #else - if (errno != EAGAIN && errno != EINTR) - break; + if (errno != EAGAIN && errno != EINTR) + break; - bytes = 0; + bytes = 0; #endif /* WIN32 */ - } - else if (bytes == 0) - break; } + else if (bytes == 0) + break; } /* diff --git a/cups/language-private.h b/cups/language-private.h index 6a2a886b1..cf5b482b4 100644 --- a/cups/language-private.h +++ b/cups/language-private.h @@ -71,6 +71,7 @@ extern const char *_cupsLangString(cups_lang_t *lang, extern void _cupsMessageFree(cups_array_t *a); extern cups_array_t *_cupsMessageLoad(const char *filename, int unquote); extern const char *_cupsMessageLookup(cups_array_t *a, const char *m); +extern cups_array_t *_cupsMessageNew(void *context); extern void _cupsSetLocale(char *argv[]); diff --git a/cups/language.c b/cups/language.c index 3e9db9ff8..74af58932 100644 --- a/cups/language.c +++ b/cups/language.c @@ -17,26 +17,27 @@ * Contents: * * _cupsAppleLanguage() - Get the Apple language identifier associated with - * a locale ID. + * a locale ID. * _cupsEncodingName() - Return the character encoding name string for the - * given encoding enumeration. - * cupsLangDefault() - Return the default language. - * cupsLangEncoding() - Return the character encoding (us-ascii, etc.) for - * the given language. - * cupsLangFlush() - Flush all language data out of the cache. - * cupsLangFree() - Free language data. - * cupsLangGet() - Get a language. - * _cupsLangString() - Get a message string. + * given encoding enumeration. + * cupsLangDefault() - Return the default language. + * cupsLangEncoding() - Return the character encoding (us-ascii, etc.) + * for the given language. + * cupsLangFlush() - Flush all language data out of the cache. + * cupsLangFree() - Free language data. + * cupsLangGet() - Get a language. + * _cupsLangString() - Get a message string. * _cupsMessageFree() - Free a messages array. * _cupsMessageLoad() - Load a .po file into a messages array. * _cupsMessageLookup() - Lookup a message string. + * _cupsMessageNew() - Make a new message catalog array. * appleLangDefault() - Get the default locale string. * appleMessageLoad() - Load a message catalog from a localizable bundle. * cups_cache_lookup() - Lookup a language in the cache... * cups_message_compare() - Compare two messages. * cups_message_free() - Free a message. * cups_message_load() - Load the message catalog for a language. - * cups_unquote() - Unquote characters in strings... + * cups_unquote() - Unquote characters in strings... */ /* @@ -854,10 +855,7 @@ _cupsMessageLoad(const char *filename, /* I - Message catalog to load */ * Create an array to hold the messages... */ - if ((a = cupsArrayNew3((cups_array_func_t)cups_message_compare, NULL, - (cups_ahash_func_t)NULL, 0, - (cups_acopy_func_t)NULL, - (cups_afree_func_t)cups_message_free)) == NULL) + if ((a = _cupsMessageNew(NULL)) == NULL) { DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!"); return (NULL); @@ -1140,6 +1138,20 @@ _cupsMessageLookup(cups_array_t *a, /* I - Message array */ } +/* + * '_cupsMessageNew()' - Make a new message catalog array. + */ + +cups_array_t * /* O - Array */ +_cupsMessageNew(void *context) /* I - User data */ +{ + return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context, + (cups_ahash_func_t)NULL, 0, + (cups_acopy_func_t)NULL, + (cups_afree_func_t)cups_message_free)); +} + + #ifdef __APPLE__ /* * 'appleLangDefault()' - Get the default locale string. @@ -1374,10 +1386,7 @@ appleMessageLoad(const char *locale) /* I - Locale ID */ * plist as the user data. */ - return (cupsArrayNew3((cups_array_func_t)cups_message_compare, (void *)plist, - (cups_ahash_func_t)NULL, 0, - (cups_acopy_func_t)NULL, - (cups_afree_func_t)cups_message_free)); + return (_cupsMessageNew((void *)plist)); } # endif /* CUPS_BUNDLEDIR */ #endif /* __APPLE__ */ diff --git a/cups/page.c b/cups/page.c index fb5841ca5..b5c5e34cd 100644 --- a/cups/page.c +++ b/cups/page.c @@ -34,7 +34,7 @@ /* - * 'ppdPageSize()' - Get the page size record for the given size. + * 'ppdPageSize()' - Get the page size record for the named size. */ ppd_size_t * /* O - Size record for page or NULL */ diff --git a/cups/pwg-media.c b/cups/pwg-media.c index 9e811054e..f4850dcf6 100644 --- a/cups/pwg-media.c +++ b/cups/pwg-media.c @@ -15,17 +15,6 @@ * * Contents: * - * _pwgGenerateSize() - Generate a PWG size keyword. - * _pwgInitSize() - Initialize a PWG size using IPP job template - * attributes. - * _pwgMediaForLegacy() - Find a PWG media size by ISO/IPP legacy name. - * _pwgMediaForPPD() - Find a PWG media size by Adobe PPD name. - * _pwgMediaForPWG() - Find a PWG media size by 5101.1 self-describing - * name. - * _pwgMediaForSize() - Get the PWG media name for a given size. - * pwg_compare_legacy() - Compare two sizes using the legacy names. - * pwg_compare_ppd() - Compare two sizes using the PPD names. - * pwg_compare_pwg() - Compare two sizes using the PWG names. */ /* @@ -241,6 +230,83 @@ static _pwg_media_t const cups_pwg_media[] = }; +/* + * '_pwgFormatInches()' - Convert and format PWG units as inches. + */ + +char * /* O - String */ +_pwgFormatInches(char *buf, /* I - Buffer */ + size_t bufsize, /* I - Size of buffer */ + int val) /* I - Value in hundredths of millimeters */ +{ + int thousandths, /* Thousandths of inches */ + integer, /* Integer portion */ + fraction; /* Fractional portion */ + + + /* + * Convert hundredths of millimeters to thousandths of inches and round to + * the nearest thousandth. + */ + + thousandths = (val * 1000 + 1270) / 2540; + integer = thousandths / 1000; + fraction = thousandths % 1000; + + /* + * Format as a pair of integers (avoids locale stuff), avoiding trailing + * zeros... + */ + + if (fraction == 0) + snprintf(buf, bufsize, "%d", integer); + else if (fraction % 10) + snprintf(buf, bufsize, "%d.%03d", integer, fraction); + else if (fraction % 100) + snprintf(buf, bufsize, "%d.%02d", integer, fraction / 10); + else + snprintf(buf, bufsize, "%d.%01d", integer, fraction / 100); + + return (buf); +} + + +/* + * '_pwgFormatMillimeters()' - Convert and format PWG units as millimeters. + */ + +char * /* O - String */ +_pwgFormatMillimeters(char *buf, /* I - Buffer */ + size_t bufsize, /* I - Size of buffer */ + int val) /* I - Value in hundredths of millimeters */ +{ + int integer, /* Integer portion */ + fraction; /* Fractional portion */ + + + /* + * Convert hundredths of millimeters to integer and fractional portions. + */ + + integer = val / 100; + fraction = val % 100; + + /* + * Format as a pair of integers (avoids locale stuff), avoiding trailing + * zeros... + */ + + if (fraction == 0) + snprintf(buf, bufsize, "%d", integer); + else if (fraction % 10) + snprintf(buf, bufsize, "%d.%02d", integer, fraction); + else + snprintf(buf, bufsize, "%d.%01d", integer, fraction / 10); + + return (buf); +} + + /* * '_pwgGenerateSize()' - Generate a PWG size keyword. */ @@ -253,25 +319,21 @@ _pwgGenerateSize(char *keyword, /* I - Keyword buffer */ int width, /* I - Width of page in 2540ths */ int length) /* I - Length of page in 2540ths */ { - struct lconv *loc; /* Locale conversion data */ - double uwidth, /* Width in inches or millimeters */ - ulength; /* Height in inches or millimeters */ const char *units; /* Units to report */ char usize[12 + 1 + 12 + 3], /* Unit size: NNNNNNNNNNNNxNNNNNNNNNNNNuu */ *uptr; /* Pointer into unit size */ + char *(*format)(char *, size_t, int); + /* Formatting function */ - loc = localeconv(); - if ((width % 635) == 0 && (length % 635) == 0) { /* * Use inches since the size is a multiple of 1/4 inch. */ - uwidth = width / 2540.0; - ulength = length / 2540.0; units = "in"; + format = _pwgFormatInches; if (!prefix) prefix = "oe"; @@ -282,26 +344,25 @@ _pwgGenerateSize(char *keyword, /* I - Keyword buffer */ * Use millimeters since the size is not a multiple of 1/4 inch. */ - uwidth = width * 0.01; - ulength = length * 0.01; units = "mm"; + format = _pwgFormatMillimeters; if (!prefix) prefix = "om"; } uptr = usize; - _cupsStrFormatd(uptr, uptr + 12, uwidth, loc); + (*format)(uptr, sizeof(usize) - (uptr - usize), width); uptr += strlen(uptr); *uptr++ = 'x'; - _cupsStrFormatd(uptr, uptr + 12, ulength, loc); + (*format)(uptr, sizeof(usize) - (uptr - usize), length); uptr += strlen(uptr); /* * Safe because usize can hold up to 12 + 1 + 12 + 4 bytes. */ - strcpy(uptr, units); + memcpy(uptr, units, 3); if (!name) name = usize; diff --git a/cups/pwg-private.h b/cups/pwg-private.h index 13eedf62d..33361c1c0 100644 --- a/cups/pwg-private.h +++ b/cups/pwg-private.h @@ -79,6 +79,9 @@ typedef struct _pwg_size_s /**** Size element - PPD to/from PWG */ * Functions... */ +extern char *_pwgFormatInches(char *buf, size_t bufsize, int val); +extern char *_pwgFormatMillimeters(char *buf, size_t bufsize, + int val); extern void _pwgGenerateSize(char *keyword, size_t keysize, const char *prefix, const char *name, @@ -90,7 +93,6 @@ extern _pwg_media_t *_pwgMediaForPPD(const char *ppd); extern _pwg_media_t *_pwgMediaForPWG(const char *pwg); extern _pwg_media_t *_pwgMediaForSize(int width, int length); - # ifdef __cplusplus } # endif /* __cplusplus */ diff --git a/cups/request.c b/cups/request.c index 34591e374..a10852fa8 100644 --- a/cups/request.c +++ b/cups/request.c @@ -415,9 +415,8 @@ cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP ippDelete(response); response = NULL; - _cupsSetError(IPP_SERVICE_UNAVAILABLE, NULL, 0); http->status = status = HTTP_ERROR; - http->error = EIO; + http->error = EINVAL; } } else if (status != HTTP_ERROR) @@ -476,10 +475,6 @@ cupsGetResponse(http_t *http, /* I - Connection to server or @code CUPS_HTTP attr ? attr->values[0].string.text : ippErrorString(response->request.status.status_code), 0); } - else if (status == HTTP_ERROR) - _cupsSetError(IPP_INTERNAL_ERROR, strerror(http->error), 0); - else if (status != HTTP_OK) - _cupsSetHTTPError(status); return (response); } diff --git a/cups/testcups.c b/cups/testcups.c index d25a78e55..18bb39cc4 100644 --- a/cups/testcups.c +++ b/cups/testcups.c @@ -58,18 +58,107 @@ main(int argc, /* I - Number of command-line arguments */ int num_jobs; /* Number of jobs for queue */ cups_job_t *jobs; /* Jobs for queue */ + if (argc > 1) { if (!strcmp(argv[1], "enum")) { - int msec; /* Timeout in milliseconds */ - - if (argc >= 3) - msec = atoi(argv[2]) * 1000; - else - msec = 0; - - cupsEnumDests(CUPS_DEST_FLAGS_NONE, msec, NULL, 0, 0, enum_cb, NULL); + cups_ptype_t mask = CUPS_PRINTER_LOCAL, + /* Printer type mask */ + type = CUPS_PRINTER_LOCAL; + /* Printer type */ + int msec = 0; /* Timeout in milliseconds */ + + + for (i = 2; i < argc; i ++) + if (isdigit(argv[i][0] & 255) || argv[i][0] == '.') + msec = (int)(atof(argv[i]) * 1000); + else if (!_cups_strcasecmp(argv[i], "bw")) + { + mask |= CUPS_PRINTER_BW; + type |= CUPS_PRINTER_BW; + } + else if (!_cups_strcasecmp(argv[i], "color")) + { + mask |= CUPS_PRINTER_COLOR; + type |= CUPS_PRINTER_COLOR; + } + else if (!_cups_strcasecmp(argv[i], "mono")) + { + mask |= CUPS_PRINTER_COLOR; + } + else if (!_cups_strcasecmp(argv[i], "duplex")) + { + mask |= CUPS_PRINTER_DUPLEX; + type |= CUPS_PRINTER_DUPLEX; + } + else if (!_cups_strcasecmp(argv[i], "simplex")) + { + mask |= CUPS_PRINTER_DUPLEX; + } + else if (!_cups_strcasecmp(argv[i], "staple")) + { + mask |= CUPS_PRINTER_STAPLE; + type |= CUPS_PRINTER_STAPLE; + } + else if (!_cups_strcasecmp(argv[i], "copies")) + { + mask |= CUPS_PRINTER_COPIES; + type |= CUPS_PRINTER_COPIES; + } + else if (!_cups_strcasecmp(argv[i], "collate")) + { + mask |= CUPS_PRINTER_COLLATE; + type |= CUPS_PRINTER_COLLATE; + } + else if (!_cups_strcasecmp(argv[i], "punch")) + { + mask |= CUPS_PRINTER_PUNCH; + type |= CUPS_PRINTER_PUNCH; + } + else if (!_cups_strcasecmp(argv[i], "cover")) + { + mask |= CUPS_PRINTER_COVER; + type |= CUPS_PRINTER_COVER; + } + else if (!_cups_strcasecmp(argv[i], "bind")) + { + mask |= CUPS_PRINTER_BIND; + type |= CUPS_PRINTER_BIND; + } + else if (!_cups_strcasecmp(argv[i], "sort")) + { + mask |= CUPS_PRINTER_SORT; + type |= CUPS_PRINTER_SORT; + } + else if (!_cups_strcasecmp(argv[i], "mfp")) + { + mask |= CUPS_PRINTER_MFP; + type |= CUPS_PRINTER_MFP; + } + else if (!_cups_strcasecmp(argv[i], "printer")) + { + mask |= CUPS_PRINTER_MFP; + } + else if (!_cups_strcasecmp(argv[i], "large")) + { + mask |= CUPS_PRINTER_LARGE; + type |= CUPS_PRINTER_LARGE; + } + else if (!_cups_strcasecmp(argv[i], "medium")) + { + mask |= CUPS_PRINTER_MEDIUM; + type |= CUPS_PRINTER_MEDIUM; + } + else if (!_cups_strcasecmp(argv[i], "small")) + { + mask |= CUPS_PRINTER_SMALL; + type |= CUPS_PRINTER_SMALL; + } + else + fprintf(stderr, "Unknown argument \"%s\" ignored...\n", argv[i]); + + cupsEnumDests(CUPS_DEST_FLAGS_NONE, msec, NULL, type, mask, enum_cb, NULL); } else if (!strcmp(argv[1], "password")) { @@ -416,10 +505,19 @@ enum_cb(void *user_data, /* I - User data (unused) */ unsigned flags, /* I - Destination flags */ cups_dest_t *dest) /* I - Destination */ { + int i; /* Looping var */ + cups_option_t *option; /* Current option */ + + if (flags & CUPS_DEST_FLAGS_REMOVED) - printf("Removed '%s'.\n", dest->name); + printf("Removed '%s':\n", dest->name); else - printf("Added '%s'.\n", dest->name); + printf("Added '%s':\n", dest->name); + + for (i = dest->num_options, option = dest->options; i > 0; i --, option ++) + printf(" %s=\"%s\"\n", option->name, option->value); + + putchar('\n'); return (1); } diff --git a/cups/util.c b/cups/util.c index 4c5b4c43d..d029588c4 100644 --- a/cups/util.c +++ b/cups/util.c @@ -248,7 +248,8 @@ cupsCreateJob( if (title) ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title); - cupsEncodeOptions(request, num_options, options); + cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB); + cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION); /* * Send the request and get the job-id... diff --git a/doc/help/api-cups.html b/doc/help/api-cups.html index 8555764f9..1cd0e5036 100644 --- a/doc/help/api-cups.html +++ b/doc/help/api-cups.html @@ -494,6 +494,7 @@ information ">cups_dinfo_t
  • cups_job_s
  • cups_option_s
  • cups_size_s
  • +
  • pollfd
  • Variables
    • cups_size_s cups_size_t;
      Top margin in hundredths of millimeters
      +

      pollfd

      +

      User data (unused)

      +

      struct pollfd *pollfds, unsigned int num_pollfds, int timeout, void *context) {
      +    _cups_dnssd_data_t *data;
      +    else if(val 0) data - got_data;
      +    void) timeout;
      +    int val;
      +};

      +

      Members

      +
      +
      data
      +
      Enumeration data
      +
      got_data
      +
      timeout
      +
      val
      +
      Return value
      +

      Variables

      CF_RETURNS_RETAINED

      Get the Apple language identifier associated with a diff --git a/doc/help/api-ppd.html b/doc/help/api-ppd.html index d01626182..33b4077a8 100644 --- a/doc/help/api-ppd.html +++ b/doc/help/api-ppd.html @@ -434,7 +434,7 @@ conflicts.">ppdMarkOption

    • ppdOpenFd
    • ppdOpenFile
    • ppdPageLength
    • -
    • ppdPageSize
    • +
    • ppdPageSize
    • ppdPageSizeLimits
    • ppdPageWidth
    • ppdSetConformance
    • @@ -1486,7 +1486,7 @@ float ppdPageLength (

      Return Value

      Length of page in points or 0.0

      ppdPageSize

      -

      Get the page size record for the given size.

      +

      Get the page size record for the named size.

      ppd_size_t *ppdPageSize (
          ppd_file_t *ppd,
      diff --git a/doc/help/man-ipptoolfile.html b/doc/help/man-ipptoolfile.html index 24b213353..645fd4a1f 100644 --- a/doc/help/man-ipptoolfile.html +++ b/doc/help/man-ipptoolfile.html @@ -323,6 +323,11 @@ defined.

      Requires the EXPECT attribute to use the specified value tag(s).
      +
      REPEAT-LIMIT number +
      +
      +
      Specifies the maximum number of times to repeat. The default value is 1000. +
      REPEAT-MATCH
      @@ -405,6 +410,11 @@ The following predicates are understood following the STATUS test directive:
      Makes the STATUS apply only if the specified variable is not defined.
      +
      REPEAT-LIMIT number +
      +
      +
      Specifies the maximum number of times to repeat. The default value is 1000. +
      REPEAT-MATCH
      diff --git a/doc/help/ref-cupsd-conf.html.in b/doc/help/ref-cupsd-conf.html.in index ef697d5d1..8454cf78d 100644 --- a/doc/help/ref-cupsd-conf.html.in +++ b/doc/help/ref-cupsd-conf.html.in @@ -2585,6 +2585,23 @@ on for secure connections. Multiple SSLPort lines can be specified to listen on multiple ports.

      +

      CUPS 1.6StrictConformance

      + +

      Examples

      + +
      +StrictConformance No
      +StrictConformance Yes
      +
      + +

      Description

      + +

      The StrictConformance directive specifies whether the scheduler +requires strict IPP conformance for client requests, for example to not allow +document attributes in a Create-Job request. The default is +No.

      + +

      CUPS 1.5SubscriptionPrivateAccess

      Examples

      diff --git a/doc/help/spec-cmp.html b/doc/help/spec-cmp.html index e253e3c48..2c66e3202 100644 --- a/doc/help/spec-cmp.html +++ b/doc/help/spec-cmp.html @@ -11,87 +11,66 @@

      This developer guide documents the guidelines and processes we use when developing and maintaining CUPS and related software. Our goal is to provide reliable and efficient software and documentation that addresses the needs of our users.

      +

      Overview

      + +

      CUPS is developed by Apple Inc. and distributed as open source software under a combination of GNU GPL2 and GNU LGPL2 licenses with exceptions to allow linking to OpenSSL (which has a GPL-incompatible license) and for developers on Apple's operating systems to develop CUPS-based software until alternate license terms. Significant contributions to CUPS must be licensed to Apple using the Apple Contributor Agreement.

      + +

      Apple releases updates to the CUPS software approximately every three months. Each release has a version number consisting of the major version (currently 1), minor version (currently 6), and patch version (starting at 0) separated by the period, for example "1.6.0". Releases where only the patch version number changes will contain only bug fixes to the previous release, for example "1.6.1" includes bug fixes for the "1.6.0" release. New features require the major or minor version numbers to change, for example "1.6.0" release contains new features compared to the "1.5.3" release. Multiple beta and "candidate" releases generally precede each new feature release, for example "1.5b1", "1.5b2", and "1.5rc1" preceded the "1.5.0" release. Finally, we also post regular Subversion snapshot releases, for example "1.6svn-r10486", which represent a snapshot of the development for the next feature release.

      + +

      CUPS interfaces, including the C APIs and command-line arguments, environment variables, configuration files, and output format, are stable across patch versions and are generally backwards-compatible with interfaces used in prior major and minor versions. However, program interfaces such as those used by the scheduler to run filter, port monitor, and backend processes for job processing should only be considered stable from the point of view of a filter, port monitor, or backend. Software that simulates the scheduler in order to run those programs outside of CUPS must necessarily be updated when the corresponding interface is changed in a subsequent CUPS release, otherwise undefined behavior can occur.

      + +

      CUPS C APIs starting with an underscore (_) are considered to be private to CUPS and are not subject to the normal guarantees of stability between CUPS releases and must never be used in non-CUPS source code. Similarly, configuration and state files written by CUPS are considered private if a corresponding man page is not provided with the CUPS release. Never rely on undocumented files or formats when developing software for CUPS. Always use a published C API to access data stored in a file to avoid compatibility problems in the future.

      + +

      Communication

      How to Contact the Developers

      -

      The CUPS -Forums are the primary means of asking questions and -informally discussing issues and feature requests with the CUPS -developers. Table 1 shows the available forums and their -focus:

      +

      The CUPS Mailing Lists are the primary means of asking questions and informally discussing issues and feature requests with the CUPS developers and other experienced CUPS users and developers. Table 1 shows the available mailing lists and their focus:

      - + - + - - - - - - + + - - + + - - + + - - + +
      Table 1: CUPS ForumsTable 1: CUPS Mailing Lists
      ForumList Focus/Purpose
      cups.bugsDiscussion of bugs and issues in the CUPS - software
      cups.commitReport of all commits to the Subversion repository - (read-only)cupsGeneral discussion and usage questions for the CUPS software
      cups.ddkUsage and development questions for the CUPS Driver - Development Kitcups-bugsDiscussion of bugs and issues in the CUPS software
      cups.developmentDevelopment questions and discussion of new features - in the CUPS softwarecups-commitReport of all commits to the Subversion repository (read-only)
      cups.generalUsage questions for the CUPS softwarecups-devDevelopment questions and discussion of new features in the CUPS software
      +

      How to Submit a Bug Report or Feature Request

      -

      The CUPS "Bugs & -Features" page provides access to the CUPS software -trouble report database and is the formal way to submit a -bug report or feature request to the CUPS developers. Please -note, however, that we do not provide answers to usage -questions or resolve problems in third-party software on this -page - use the CUPS Forums for that instead.

      - -

      Unlike discussions that occur on the CUPS Forums, formal bug -reports and feature requests must be acted on by the CUPS -developers. This does not mean that every bug report is resolved -or every feature request is implemented, but we do respond and -keep track of them all for posterity.

      - -
      Please use the search feature of the Bugs & -Features page before submitting a new bug report or feature -request. If you see an existing report that matches your issue, -please post a message to that report ("I have this issue as -well", "I would also like to see", etc.) rather than submitting a -new report. This helps speed the resolution of your issue by -reducing the CUPS developers' work load.
      +

      The CUPS "Bugs & Features" page provides access to the CUPS Software Trouble Report (STR) database and is the formal way to submit a bug report or feature request to the CUPS developers. Please note, however, that we do not provide answers to usage questions or resolve problems in third-party software on this page - use the CUPS Mailing Lists for that instead.

      + +

      Unlike discussions that occur on the CUPS Mailing Lists, formal bug reports and feature requests must be acted on by the CUPS developers. This does not mean that every bug report is resolved or every feature request is implemented, but we do respond and keep track of them all for posterity.

      + +
      Please use the search feature of the Bugs & Features page before submitting a new bug report or feature request. If you see an existing report that matches your issue, please post a message to that report ("I have this issue as well", "I would also like to see", etc.) rather than submitting a new report. This helps speed the resolution of your issue by reducing the CUPS developers' work load and identifying popular issues.
      +

      How to Prepare a Patch

      -

      When submitting a bug report or feature request, you can -include patch files that resolve the bug or implement the feature -to speed the inclusion of that bug fix or feature in a new CUPS -release. For changes to existing files, we prefer a unified diff -against the current Subversion trunk branch, which can -be generated easily using the following Subversion command:

      +

      When submitting a bug report or feature request, you can include patch files that resolve the bug or implement the feature to speed the inclusion of that bug fix or feature in a new CUPS release. For changes to existing files, we prefer a unified diff against the current Subversion trunk branch, which can be generated easily using the following Subversion command:

       svn diff >filename.patch
       
      -

      If you produce a patch using a released source archive, use -one of the following commands instead:

      +

      If you produce a patch using a released source archive, use one of the following commands instead:

       diff -u oldfilename filename >filename.patch
      @@ -99,36 +78,22 @@ diff -u oldfilename filename >filename.patch
       diff -urN olddirectory directory >filename.patch
       
      -

      New files and files with significant changes can be submitted -in their entirety, however that may delay the adoption of your -changes.

      +

      New files and files with significant changes can be submitted in their entirety, however that may delay the adoption of your changes.

      + +
      Note: -
      Patches and files must conform to the standards outlined in the -"Coding Guidelines" and "Makefile -Guidelines" sections in this document. In addition, since Apple Inc. -provides CUPS under multiple licenses, we require that you assign the copyright -for your changes and files to us for inclusion in CUPS.
      +

      Patches and files must conform to the standards outlined in the "Coding Guidelines" and "Makefile Guidelines" sections in this document. In addition, since Apple Inc. provides CUPS under multiple licenses, we require that you license significant changes and files to us for inclusion in CUPS. The CUPS developers will inform you if licensing is required.

      Software Development Practices

      Version Numbering

      -

      CUPS uses a three-part version number separated by periods to -represent the major, minor, and patch release numbers. Major -release numbers indicate large design changes or -backwards-incompatible changes to the CUPS API or CUPS Imaging -API. Minor release numbers indicate new features and other -smaller changes which are backwards-compatible with previous CUPS -releases. Patch numbers indicate bug fixes to the previous -release.

      - -
      When we talk about compatibility, we are talking -about binary compatibility for public APIs and output format -compatibility for program interfaces. Changes to configuration -file formats or the default behavior of programs are not -generally considered incompatible as the upgrade process can -normally address such changes gracefully.
      +

      CUPS uses a three-part version number separated by periods to represent the major, minor, and patch release numbers. Major release numbers indicate large design changes or backwards-incompatible changes to the CUPS API or CUPS Imaging API. Minor release numbers indicate new features and other smaller changes which are backwards-compatible with previous CUPS releases. Patch numbers indicate bug fixes to the previous feature release.

      + +
      Note: + +

      When we talk about compatibility, we are talking about binary compatibility for public APIs and output format compatibility for program interfaces. Changes to configuration file formats or the default behavior of programs are not generally considered incompatible as the upgrade process can normally address such changes gracefully.

      Production releases use the plain version numbers:

      @@ -149,59 +114,44 @@ MAJOR.MINOR.PATCH 2.0.0 -

      The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is -called a feature release. Feature releases are the only releases that may -contain new features. Subsequent production releases in a MAJOR.MINOR series may -only contain bug fixes.

      +

      The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is called a feature release. Feature releases are the only releases that may contain new features. Subsequent production releases in a MAJOR.MINOR series may only contain bug fixes.

      + +
      Note: -
      We did not hold to this limitation in the CUPS 1.1 series for a -variety of reasons. Starting with CUPS 1.2, the "no new features in a patch -release" policy has been strictly enforced. The policy has also resulted in -fewer new features (and interactions!) to validate/test in the subsequence -feature releases.
      +

      We did not hold to this limitation in the CUPS 1.1 series for a variety of reasons. Starting with CUPS 1.2, the "no new features in a patch release" policy has been strictly enforced. The policy has also resulted in fewer new features (and interactions!) to validate/test in the subsequence feature releases.

      -

      Beta-test releases are identified by appending the letter B to the major and -minor version numbers followed by the beta release number:

      +

      Beta-test releases are identified by appending the letter B to the major and minor version numbers followed by the beta release number:

       MAJOR.MINORbNUMBER
       1.2b1
       
      -

      Release candidates are identified by appending the letters RC to the major -and minor version numbers followed by the release candidate number:

      +

      Release candidates are identified by appending the letters RC to the major and minor version numbers followed by the release candidate number:

       MAJOR.MINORrcNUMBER
       1.2rc1
       
      -

      Developer snapshots are identified by appending the letters SVN-R to the -major and minor version numbers followed by the revision number:

      +

      Developer snapshots are identified by appending the letters SVN-R to the major and minor version numbers followed by the revision number:

       MAJOR.MINORsvn-rREV
       1.2svn-r1234
       
      -

      Beta-test releases, release candidates, and developer snapshots are only -created for new minor releases. Once a production release has been made -(MAJOR.MINOR.0), subsequent patch releases are issues without preliminary beta -or release testing.

      +

      Beta-test releases, release candidates, and developer snapshots are only created for new minor releases. Once a production release has been made (MAJOR.MINOR.0), subsequent patch releases are issued without preliminary beta or release testing.

      Version Control (Subversion)

      -

      The CUPS source files are managed by the Subversion ("SVN") -software, available at:

      +

      The CUPS source files are managed by the Subversion ("SVN") software, available at:

      -subversion.tigris.org
      +subversion.apache.org
       
      -

      Source files are "checked in" with each change so that -modifications can be tracked, and each checkin must reference any -applicable STRs. The following format must be used for -commit log messages:

      +

      Source files are "checked in" with each change so that modifications can be tracked, and each checkin must reference any applicable STRs. The following format must be used for commit log messages:

       Summary of the change on one line followed by bug number (STR #NNNN)
      @@ -209,10 +159,7 @@ Summary of the change on one line followed by bug number (STR #NNNN)
       Detailed list of changes.
       
      -

      Primary development occurs on the trunk branch, -with changes merged back to release branches as needed. Table 2 -shows the URLs developers use for the various CUPS sub-projects -and branches:

      +

      Primary development occurs on the trunk branch, with changes merged back to release branches as needed. Table 2 shows the URLs developers use for the various CUPS sub-projects and branches:

      @@ -221,113 +168,55 @@ and branches:

      - + - + - + - - - - - - - - - - - -
      Table 2: CUPS Subversion URLs
      Purpose
      https://svn.easysw.com/public/cups/trunk/https://svn.cups.org/public/cups/trunk/ Primary CUPS development branch
      https://svn.easysw.com/public/cups/branches/https://svn.cups.org/public/cups/branches/ CUPS maintenance branches (merge-only)
      https://svn.easysw.com/public/cups/tags/https://svn.cups.org/public/cups/tags/ CUPS release tags (read-only)
      https://svn.easysw.com/public/windows/trunk/Primary CUPS Windows Driver development branch
      https://svn.easysw.com/public/windows/branches/CUPS Windows Driver maintenance branches (merge-only)
      https://svn.easysw.com/public/windows/tags/CUPS Windows Driver release tags (read-only)
      -

      The branch for a MAJOR.MINOR release are created when the -first production release (MAJOR.MINOR.0) is made using the name -"branch-MAJOR.MINOR". Release tags are created for every beta, -candidate, and production release using the name -"release-MAJOR.MINOR.PATCHbNUMBER", -"release-MAJOR.MINOR.PATCHrcNUMBER", or -"release-MAJOR.MINOR.PATCH", respectively. No release tags are -created for developer snapshots.

      +

      The branch for a MAJOR.MINOR release are created when the first production release (MAJOR.MINOR.0) is made using the name "branch-MAJOR.MINOR". Release tags are created for every beta, candidate, and production release using the name "release-MAJOR.MINORbNUMBER", "release-MAJOR.MINORrcNUMBER", or "release-MAJOR.MINOR.PATCH", respectively. No release tags are created for developer snapshots.

      Files and Directories

      -

      File and directory names may not exceed 16 characters in -length to ensure compatibility with older UNIX filesystems. In -addition, to avoid problems with case-insensitive filesystems, -you may not use names which differ only by case, for example -"ReadMe" and "README" are not allowed in the same directory.

      +

      File and directory names may not exceed 16 characters in length to ensure compatibility with older UNIX filesystems. In addition, to avoid problems with case-insensitive filesystems, you may not use names which differ only by case, for example "ReadMe" and "README" are not allowed in the same directory.

      -

      Source files must be documented and formatted as described in -"Coding Requirements". Make files must -follow the guidelines in "Makefile -Guidelines".

      +

      Source files must be documented and formatted as described in "Coding Requirements". Makefiles must follow the guidelines in "Makefile Guidelines".

      Build System

      -

      The CUPS build system uses GNU autoconf to -tailor the library to the local operating system. Project files -for major IDEs are also provided for Microsoft -Windows®. To improve portability, makefiles must -not make use of the unique features offered by GNU make. See the Makefile Guidelines section for a -description of the allowed make features and makefile -guidelines.

      - -

      Additional GNU build programs such as GNU automake and -GNU libtool -must not be used. GNU automake produces non-portable makefiles -which depend on GNU-specific extensions, and GNU libtool is not -portable or reliable enough for CUPS.

      +

      The CUPS build system uses GNU autoconf to tailor the library to the local operating system. Project files for the current release of Visual C++ are also provided for Microsoft Windows®. To improve portability, makefiles must not make use of features unique to GNU make. See the Makefile Guidelines section for a description of the allowed make features and makefile guidelines.

      + +

      Additional GNU build programs such as GNU automake and GNU libtool must not be used. GNU automake produces non-portable makefiles which depend on GNU-specific extensions, and GNU libtool is not portable or reliable enough for CUPS.

      Packaging

      -

      Source packages are created using the -tools/makesrcdist script in the Subversion repository. -The script optionally uses a version number argument:

      +

      Source packages are created using the tools/makesrcdist script in the Subversion repository. The script optionally uses a version number argument:

       tools/makesrcdist
       tools/makesrcdist version
       
      -

      When run with no arguments, the script creates a snapshot of -the current working copy and names it using the highest revision -number in the WC, for example -"/tmp/cups-1.3svn-r1234-source.tar.bz2" and -"/tmp/cups-1.3svn-r1234-source.tar.gz". When run with two -arguments, the script creates a release tag in the repository and -exports that tag, creating the files -"/tmp/cups-version-source.tar.bz2" and -"/tmp/cups-version-source.tar.gz".

      - -

      Binary packages are not generally distributed by the CUPS -team, however the packaging/cups.spec and -packaging/cups.list files may be used to create binary -packages on Linux, OS X, and UNIX. The -packaging/cups.spec file produces a binary package -using the rpmbuild(8) software:

      +

      When run with no arguments, the script creates a snapshot of the current working copy and names it using the highest revision number in the WC, for example "/tmp/cups-1.3svn-r1234-source.tar.bz2" and "/tmp/cups-1.3svn-r1234-source.tar.gz". When run with two arguments, the script creates a release tag in the repository and exports that tag, creating the files +"/tmp/cups-version-source.tar.bz2" and "/tmp/cups-version-source.tar.gz".

      + +

      Binary packages are not generally distributed by the CUPS team, however the packaging/cups.spec and packaging/cups.list files may be used to create binary packages on Linux, OS X, and UNIX. The packaging/cups.spec file produces a binary package using the rpmbuild(8) software:

       rpmbuild -ta cups-version-source.tar.gz
       
      -

      The cups.list file is generated by the -configure script and produces binary packages for many -platforms using the EPM software. Table 3 shows the targets that -are available for each type of binary package:

      +

      The cups.list file is generated by the configure script and produces binary packages for many platforms using the EPM software. Table 3 shows the targets that are available for each type of binary package:

      @@ -359,10 +248,6 @@ are available for each type of binary package:

      - - - - @@ -389,10 +274,7 @@ are available for each type of binary package:

      Table 3: Binary Package Targets
      inst IRIX inst/tardist
      osxOS X Install
      pkg Solaris pkgadd
      -

      Finally, the tools/testrpm and -tools/testosx scripts can be used to create binary -packages from the current working copy for testing on Linux and -OS X, respectively:

      +

      Finally, the tools/testrpm and tools/testosx scripts can be used to create binary packages from the current working copy for testing on Linux and OS X, respectively:

       tools/testrpm
      @@ -405,26 +287,18 @@ open cups.pkg
       
       

      Testing

      -

      Software testing is conducted according to the CUPS Software Test Plan. This testing is -automated via the top-level makefile test target:

      +

      Software testing is conducted according to the CUPS Software Test Plan. This testing is automated via the top-level makefile test target:

       make test
       
      -

      The test environment allows for both short-term automated -testing and long-term testing and development without the -automated test script.

      +

      The test environment allows for both short-term automated testing and long-term testing and development without the automated test script.

      Trouble Report Processing

      -

      A Software Trouble Report ("STR") must be submitted every time -a user or vendor experiences a problem with the CUPS software. -Trouble reports are maintained on the Bugs & -Features page with one of the following states:

      +

      A Software Trouble Report ("STR") must be submitted every time a user or vendor experiences a problem with the CUPS software. Trouble reports are maintained on the Bugs & Features page with one of the following states:

        @@ -442,36 +316,25 @@ Features page with one of the following states:

        Trouble reports are processed using the following steps.

        -
          +

          1. Classification

          -
        1. Classification - -

          When a trouble report is received it must be classified at one -of the following priority levels:

          +

          When a trouble report is received it must be classified at one of the following priority levels:

            -
          1. Request for enhancement, e.g. asking for a - feature +
          2. Request for enhancement, e.g. asking for a feature -
          3. Low, e.g. a documentation error or undocumented - side-effect +
          4. Low, e.g. a documentation error or undocumented side-effect -
          5. Moderate, e.g. unable to print a file or unable to - compile the software +
          6. Moderate, e.g. unable to print a file or unable to compile the software -
          7. High, e.g. unable to print to a printer or key - functionality not working +
          8. High, e.g. unable to print to a printer or key functionality not working
          9. Critical, e.g. unable to print at all
          -

          Level 4 and 5 trouble reports must be resolved in the next -software release. Level 2 and 3 trouble reports are scheduled for -resolution in a specific release at the discretion of the release -coordinator. Level 1 trouble reports are scheduled for resolution -in a future feature release.

          +

          Level 4 and 5 trouble reports must be resolved in the next software release. Level 2 and 3 trouble reports are scheduled for resolution in a specific release at the discretion of the release coordinator. Level 1 trouble reports are scheduled for resolution in a future feature release.

          The scope of the problem is also determined as:

          @@ -485,43 +348,24 @@ in a future feature release.

        -
      1. Identification - -

        Once the level and scope of the trouble report is determined -the software sub-system(s) involved with the problem are -determined. This may involve additional communication with the -user or vendor to isolate the problem to a specific cause.

        +

        2. Identification

        -

        When the sub-system(s) involved have been identified, an -engineer will then determine the change(s) needed and estimate -the time required for the change(s).

        +

        Once the level and scope of the trouble report is determined the software sub-system(s) involved with the problem are determined. This may involve additional communication with the user or vendor to isolate the problem to a specific cause.

        -
      2. Correction +

        When the sub-system(s) involved have been identified, an engineer will then determine the change(s) needed and estimate the time required for the change(s).

        -

        Corrections are scheduled based upon the severity and -complexity of the problem. Once all changes have been made, -documented, and tested successfully a new software release -snapshot is generated. Additional tests are added as necessary -for proper testing of the changes.

        +

        3. Correction

        -
      3. Notification +

        Corrections are scheduled based upon the severity and complexity of the problem. Once all changes have been made, documented, and tested successfully a new software release snapshot is generated. Additional tests are added as necessary for proper testing of the changes.

        -

        The user or vendor is notified when the fix is available or if -the problem was caused by user error.

        +

        4. Notification

        -
      +

      The user or vendor is notified when the fix is available or if the problem was caused by user error.

      Release Management

      -

      When testing has been completed successfully, a new source -package is created using the tools/makesrcdist script. -Three types of releases, beta, candidate, and production, are -created and released to the public using the basic schedule in -Table 4. At least one beta and one release candidate must be -created prior to a production release, and there must be at least -two weeks between the last beta and first candidate and last -candidate and first production release.

      +

      When testing has been completed successfully, a new source package is created using the tools/makesrcdist script. Three types of releases - beta, candidate, and production - are created and released to the public using the basic schedule in Table 4. At least one beta and one release candidate must be created prior to a production release, and there must be at least two weeks between the last beta and first candidate and last candidate and first production release.

      @@ -560,25 +404,18 @@ candidate and first production release.

      Coding Guidelines

      -

      These coding guidelines provide detailed information on source -file formatting and documentation content and must be applied to -all C and C++ source files provided with CUPS. Source code for -other languages should conform to these guidelines as allowed by -the language.

      +

      These coding guidelines provide detailed information on source file formatting and documentation content and must be applied to all C and C++ source files provided with CUPS. Source code for other languages should conform to these guidelines as allowed by the language.

      +

      Source Files

      -

      All source files names shall be 16 characters or less in -length to ensure compatibility with older UNIX filesystems. -Source files containing functions shall have an extension of ".c" -for ANSI C and ".cxx" for C++ source files. All other "include" -files shall have an extension of ".h".

      +

      All source files names must be 16 characters or less in length to ensure compatibility with older UNIX filesystems. Source files containing functions have an extension of ".c" for ANSI C and ".cxx" for C++ source files. All other "include" files have an extension of ".h". Tabs are set to 8 characters.

      -

      The top of each source file shall contain a header giving the -name of the file, the purpose or nature of the source file, the -copyright and licensing notice, and the functions contained in -the file. The file name and revision information is provided by -the Subversion "$Id$" tag:

      +
      Note: + +

      The ".cxx" extension is used because it is the only common C++ extension between Linux, OS X, UNIX, and Windows.

      + +

      The top of each source file contains a header giving the name of the file, the purpose or nature of the source file, the copyright and licensing notice, and the functions contained in the file. The file name and revision information is provided by the Subversion "$Id$" tag:

       /*
      @@ -586,7 +423,7 @@ the Subversion "$Id$" tag:

      * * Description of file contents. * - * Copyright 2010 by Apple Inc. + * Copyright 2012 by Apple Inc. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright @@ -602,19 +439,13 @@ the Subversion "$Id$" tag:

      */
      -

      For source files that are subject to the Apple OS-Developed -Software exception, the following additional comment should -appear after the contact information:

      +

      For source files that are subject to the Apple OS-Developed Software exception, the following additional comment appears after the contact information:

        *   This file is subject to the Apple OS-Developed Software exception.
       
      -

      The bottom of each source file shall contain a trailer giving -the name of the file using the Subversion "$Id$" tag. The -primary purpose of this is to mark the end of a source file; if -the trailer is missing it is possible that code has been lost -near the end of the file:

      +

      The bottom of each source file contains a trailer giving the name of the file using the Subversion "$Id$" tag. The primary purpose of this is to mark the end of a source file; if the trailer is missing it is possible that code has been lost near the end of the file:

       /*
      @@ -622,22 +453,92 @@ near the end of the file:

      */
      + +

      Comments

      + +

      All source code utilizes block comments within functions to describe the operations being performed by a group of statements; avoid putting a comment per line unless absolutely necessary, and then consider refactoring the code so that it is not necessary. C source files use the block comment format ("/* comment */") since many vendor C compilers still do not support C99/C++ comments ("// comment"):

      + +
      + /*
      +  * Clear the state array before we begin...
      +  */
      +
      +  for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
      +    array[i] = CUPS_STATE_IDLE;
      +
      + /*
      +  * Wait for state changes on another thread...
      +  */
      +
      +  do
      +  {
      +    for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
      +      if (array[i] != CUPS_STATE_IDLE)
      +        break;
      +
      +    if (i == (sizeof(array) / sizeof(array[0])))
      +      sleep(1);
      +  } while (i == (sizeof(array) / sizeof(array[0])));
      +
      + +

      Indentation

      + +

      All code blocks enclosed by brackets begin with the opening brace on a new line. The code then follows starting on a new line after the brace and is indented 2 spaces. The closing brace is then placed on a new line following the code at the original indentation:

      + +
      +{
      +  int i; /* Looping var */
      +
      +
      + /*
      +  * Process foobar values from 0 to 999...
      +  */
      +
      +  for (i = 0; i < 1000; i ++)
      +  {
      +    do_this(i);
      +    do_that(i);
      +  }
      +}
      +
      + +

      Single-line statements following "do", "else", "for", "if", and "while" are indented 2 spaces as well. Blocks of code in a "switch" block are indented 4 spaces after each "case" and "default" case:

      + +
      +switch (array[i])
      +{
      +  case CUPS_STATE_IDLE :
      +      do_this(i);
      +      do_that(i);
      +      break;
      +  default :
      +      do_nothing(i);
      +      break;
      +}
      +
      + + +

      Spacing

      + +

      A space follows each reserved word such as "if", "while", etc. Spaces are not inserted between a function name and the arguments in parenthesis.

      + + +

      Return Values

      + +

      Parenthesis surround values returned from a function:

      + +
      +return (CUPS_STATE_IDLE);
      +
      + +

      Functions

      -

      Functions with a global scope shall have a lowercase prefix -followed by capitalized words ("cupsDoThis", "cupsDoThat", -"cupsDoSomethingElse", etc.) Private global functions shall begin -with a leading underscore ("_cupsDoThis", "_cupsDoThat", -etc.)

      +

      Functions with a global scope have a lowercase prefix followed by capitalized words, e.g., "cupsDoThis", "cupsDoThat", "cupsDoSomethingElse", etc. Private global functions begin with a leading underscore, e.g., "_cupsDoThis", "_cupsDoThat", etc.

      -

      Functions with a local scope shall be declared "static" and be -lowercase with underscores between words ("do_this", "do_that", -"do_something_else", etc.)

      +

      Functions with a local scope are declared "static" with lowercase names and underscores between words, e.g., "do_this", "do_that", "do_something_else", etc.

      -

      Each function shall begin with a comment header describing -what the function does, the possible input limits (if any), and -the possible output values (if any), and any special information -needed:

      +

      Each function begins with a comment header describing what the function does, the possible input limits (if any), and the possible output values (if any), and any special information needed:

       /*
      @@ -654,100 +555,68 @@ do_this(float x)                        /* I - Power value (0.0 <= x <= 1.
       }
       
      -

      Return/output values are indicated using an "O" prefix, input -values are indicated using the "I" prefix, and values that are -both input and output use the "IO" prefix for the corresponding -in-line comment.

      +

      Return/output values are indicated using an "O" prefix, input values are indicated using the "I" prefix, and values that are both input and output use the "IO" prefix for the corresponding in-line comment.

      -

      The Mini-XML documentation generator also understands the following -special text in the function description comment:

      +

      The Mini-XML documentation generator also understands the following special text in the function description comment:

        -
      • @deprecated@ - Marks the function as - deprecated (not recommended for new development and - scheduled for removal)
      • +
      • @deprecated@ - Marks the function as deprecated (not recommended for new development and scheduled for removal)
      • -
      • @since CUPS version@ - Marks the - function as new in the specified version of CUPS.
      • +
      • @since CUPS version@ - Marks the function as new in the specified version of CUPS.
      • -
      • @private@ - Marks the function as private.
      • +
      • @private@ - Marks the function as private (same as starting the function name with an underscore)
      +

      Variables

      -

      Variables with a global scope shall be capitalized -("ThisVariable", "ThatVariable", "ThisStateVariable", etc.) The -only exception to this rule shall be the CUPS interface library -global variables which must begin with the prefix "cups" -("cupsThisVariable", "cupsThatVariable", etc.) Global variables -shall be replaced by function arguments whenever possible.

      +

      Variables with a global scope are capitalized, e.g., "ThisVariable", "ThatVariable", "ThisStateVariable", etc. Globals in CUPS libraries are either part of the per-thread global values managed by the "_cupsGlobals()" function or are suitably protected for concurrent access. Global variables should be replaced by function arguments whenever possible.

      -

      Variables with a local scope shall be lowercase with -underscores between words ("this_variable", "that_variable", -etc.) Any local variables shared by functions within a source -file shall be declared "static".

      +

      Variables with a local scope are lowercase with underscores between words, e.g., "this_variable", "that_variable", etc. Any "local global" variables shared by functions within a source file are declared "static". As for global variables, local static variables are suitably protected for concurrent access.

      -

      Each variable shall be declared on a separate line and shall -be immediately followed by a comment block describing the -variable:

      +

      Each variable is declared on a separate line and is immediately followed by a comment block describing the variable:

      -int this_variable;   /* The current state of this */
      -int that_variable;   /* The current state of that */
      +int         ThisVariable;    /* The current state of this */
      +static int  that_variable;   /* The current state of that */
       
      +

      Types

      -

      All type names shall be lowercase with underscores between -words and "_t" appended to the end of the name -("cups_this_type_t", "cups_that_type_t", etc.) Type names must -start with a prefix, typically "cups" or the name of the program, -to avoid conflicts with system types. Private type names must -start with an underscore ("_cups_this_t", "_cups_that_t", -etc.)

      +

      All type names are lowercase with underscores between words and "_t" appended to the end of the name, e.g., "cups_this_type_t", "cups_that_type_t", etc. Type names start with a prefix, typically "cups" or the name of the program, to avoid conflicts with system types. Private type names start with an underscore, e.g., "_cups_this_t", "_cups_that_t", etc.

      -

      Each type shall have a comment block immediately after the -typedef:

      +

      Each type has a comment block immediately after the typedef:

      -typedef int cups_this_type_t;           /* This type is for CUPS foobar options. */
      +typedef int cups_this_type_t;       /* This type is for CUPS foobar options. */
       
      +

      Structures

      -

      All structure names shall be lowercase with underscores -between words and "_s" appended to the end of the name -("cups_this_s", "cups_that_s", etc.) Structure names must start -with a prefix, typically "cups" or the name of the program, to -avoid conflicts with system types. Private structure names must -start with an underscore ("_cups_this_s", "_cups_that_s", -etc.)

      +

      All structure names are lowercase with underscores between words and "_s" appended to the end of the name, e.g., "cups_this_s", "cups_that_s", etc. Structure names start with a prefix, typically "cups" or the name of the program, to avoid conflicts with system types. Private structure names start with an underscore, e.g., "_cups_this_s", "_cups_that_s", etc.

      -

      Each structure shall have a comment block immediately after -the struct and each member shall be documented in accordance with -the variable naming policy above:

      +

      Each structure has a comment block immediately after the struct and each member is documented similar to the variable naming policy above:

      -struct cups_this_struct_s               /* This structure is for CUPS foobar options. */
      +struct cups_this_struct_s     /* This structure is for CUPS foobar options. */
       {
      -  int this_member;                      /* Current state for this */
      -  int that_member;                      /* Current state for that */
      +  int this_member;            /* Current state for this */
      +  int that_member;            /* Current state for that */
       };
       
      +

      Constants

      -

      All constant names shall be uppercase with underscored between -words ("CUPS_THIS_CONSTANT", "CUPS_THAT_CONSTANT", etc.) -Constants must begin with an uppercase prefix, typically "CUPS" -or the program name.

      +

      All constant names are uppercase with underscores between words, e.g., "CUPS_THIS_CONSTANT", "CUPS_THAT_CONSTANT", etc. Constants begin with an uppercase prefix, typically "CUPS" or the program name. Private constants start with an underscore, e.g., "_CUPS_THIS_CONSTANT", "_CUPS_THAT_CONSTANT", etc.

      -

      Typed enumerations shall be used whenever possible to allow -for type checking by the compiler.

      +

      Typed enumerations should be used whenever possible to allow for type checking by the compiler.

      -

      Comment blocks shall immediately follow each constant:

      +

      Comment blocks immediately follow each constant:

       enum
      @@ -757,124 +626,20 @@ enum
       };
       
      -

      Code

      - -

      All source code shall utilize block comments within functions -to describe the operations being performed by a group of -statements; avoid putting a comment per line unless absolutely -necessary, and then consider refactoring the code so that it is -not necessary:

      - -
      -/*
      - * Clear the state array before we begin...
      - */
      -
      -for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
      -  array[i] = STATE_IDLE;
      -
      -/*
      - * Wait for state changes...
      - */
      -
      -do
      -{
      -  for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
      -    if (array[i] != STATE_IDLE)
      -      break;
      -
      -  if (i == (sizeof(array) / sizeof(array[0])))
      -    sleep(1);
      -} while (i == (sizeof(array) / sizeof(array[0])));
      -
      - -

      Indentation

      - -

      All code blocks enclosed by brackets shall begin with the -opening brace on a new line. The code then follows starting on a -new line after the brace and is indented 2 spaces. The closing -brace is then placed on a new line following the code at the -original indentation:

      - -
      -{
      -  int i; /* Looping var */
      -
      - /*
      -  * Process foobar values from 0 to 999...
      -  */
      -
      -  for (i = 0; i < 1000; i ++)
      -  {
      -    do_this(i);
      -    do_that(i);
      -  }
      -}
      -
      - -

      Single-line statements following "do", "else", "for", "if", -and "while" shall be indented 2 spaces as well. Blocks of code -in a "switch" block shall be indented 4 spaces after each "case" -and "default" case:

      - -
      -switch (array[i])
      -{
      -  case STATE_IDLE :
      -      do_this(i);
      -      do_that(i);
      -      break;
      -  default :
      -      do_nothing(i);
      -      break;
      -}
      -
      - -

      Spacing

      - -

      A space shall follow each reserved word ("if", "while", etc.) -Spaces shall not be inserted between a function name and the -arguments in parenthesis.

      - -

      Return Values

      - -

      Parenthesis shall surround values returned from a function -using "return":

      - -
      -return (CUPS_STATE_IDLE);
      -
      - -

      Loops

      - -

      Whenever convenient loops should count downward to zero to -improve program performance:

      - -
      -for (i = sizeof(array) / sizeof(array[0]) - 1; i >= 0; i --)
      -  array[i] = CUPS_STATE_IDLE;
      -

      Makefile Guidelines

      -

      The following is a guide to the makefile-based build system -used by CUPS. These standards have been developed over the years -to allow CUPS to be built on as many systems and environments as -possible.

      +

      The following is a guide to the makefile-based build system used by CUPS. These standards have been developed over the years to allow CUPS to be built on as many systems and environments as possible.

      +

      General Organization

      -

      The CUPS source code is organized functionally into a -top-level makefile, include file, and subdirectories each with -their own makefile and dependencies files. The ".in" files are -template files for the autoconf software and are -used to generate a static version of the corresponding file.

      +

      The CUPS source code is organized functionally into a top-level makefile, include file, and subdirectories each with their own makefile and dependencies files. The ".in" files are template files for the autoconf software and are used to generate a static version of the corresponding file.

      +

      Makefile Documentation

      -

      Each make file must start with the standard CUPS header -containing the Subversion "$Id$" keyword, description of the -file, and CUPS copyright and license notice:

      +

      Each makefile starts with the standard CUPS header containing the Subversion "$Id$" keyword, description of the file, and CUPS copyright and license notice:

       #
      @@ -882,7 +647,7 @@ file, and CUPS copyright and license notice:

      # # Makefile for ... # -# Copyright 2007 by Apple Inc. +# Copyright 2012 by Apple Inc. # # These coded instructions, statements, and computer programs are the # property of Apple Inc. and are protected by Federal copyright @@ -892,7 +657,7 @@ file, and CUPS copyright and license notice:

      #
      -

      The end of each makefile must have a comment saying:

      +

      The end of each makefile has a comment saying:

       #
      @@ -900,28 +665,21 @@ file, and CUPS copyright and license notice:

      #
      -

      The purpose of the trailer is to indicate the end of the -makefile so that truncations are immediately obvious.

      +

      The purpose of the trailer is to indicate the end of the makefile so that truncations are immediately obvious.

      +

      Portable Makefile Construction

      -

      CUPS uses a common subset of make program syntax to ensure -that the software can be compiled "out of the box" on as many -systems as possible. The following is a list of assumptions we -follow when constructing makefiles:

      +

      CUPS uses a common subset of make program syntax to ensure that the software can be compiled "out of the box" on as many systems as possible. The following is a list of assumptions we follow when constructing makefiles:

        -
      • Targets; we assume that the make program - supports the notion of simple targets of the form - "name:" that perform tab-indented commands that follow - the target, e.g.: +
      • Targets; we assume that the make program supports the notion of simple targets of the form "name:" that perform tab-indented commands that follow the target, e.g.:
         target:
         → target commands
      • -
      • Dependencies; we assume that the make program - supports recursive dependencies on targets, e.g.: +
      • Dependencies; we assume that the make program supports recursive dependencies on targets, e.g.:
         target: foo bar
         → target commands
        @@ -935,28 +693,21 @@ bar:
         bla:
         → bla commands
      • -
      • Variable Definition; we assume that the make program - supports variable definition on the command-line or in the makefile - using the following form: +
      • Variable Definition; we assume that the make program supports variable definition on the command-line or in the makefile using the following form:
         name=value
        -
      • Variable Substitution; we assume that the make program - supports variable substitution using the following forms: +
      • Variable Substitution; we assume that the make program supports variable substitution using the following forms:
        • $(name); substitutes the value of "name",
        • -
        • ($name:.old=.new); substitutes the value of "name" - with the filename extensions ".old" changed to ".new",
        • -
        • $(MAKEFLAGS); substitutes the - command-line options passed to the program - without the leading hyphen (-),
        • +
        • ($name:.old=.new); substitutes the value of "name" with the filename extension ".old" changed to ".new",
        • +
        • $(MAKEFLAGS); substitutes the command-line options passed to the program without the leading hyphen (-),
        • $$; substitutes a single $ character,
        • $<; substitutes the current source file or dependency, and
        • $@; substitutes the current target name.
      • -
      • Suffixes; we assume that the make program - supports filename suffixes with assumed dependencies, e.g.: +
      • Suffixes; we assume that the make program supports filename suffixes with assumed dependencies, e.g.:
         .SUFFIXES: .c .o
         .c.o:
        @@ -968,64 +719,67 @@ name=value
        include ../Makedefs include Dependencies
      • -
      • Comments; we assume that comments begin with - a # character and proceed to the end of the - current line.
      • +
      • Comments; we assume that comments begin with a # character and proceed to the end of the current line.
      • -
      • Line Length; we assume that there is no - practical limit to the length of lines.
      • +
      • Line Length; we assume that there is no practical limit to the length of lines.
      • -
      • Continuation of long lines; we assume that - the \ character may be placed at the end of a - line to concatenate two or more lines in a - makefile to form a single long line.
      • +
      • Continuation of long lines; we assume that the \ character may be placed at the end of a line to concatenate two or more lines in a makefile to form a single long line.
      • -
      • Shell; we assume a POSIX-compatible shell is - present on the build system.
      • +
      • Shell; we assume a POSIX-compatible shell is present on the build system.
      +

      Standard Variables

      -

      The following variables are defined in the "Makedefs" file -generated by the autoconf software:

      +

      The following variables are defined in the "Makedefs" file generated by the autoconf software:

        -
      • AR; the library archiver command,
      • +
      • ALL_CFLAGS; the combined C compiler options,
      • -
      • ARFLAGS; options for the library archiver command,
      • - -
      • BUILDROOT; optional installation prefix,
      • +
      • ALL_CXXFLAGS; the combined C++ compiler options,
      • -
      • MAN1EXT; extension for man pages in section 1,
      • +
      • AMANDIR; the administrative man page installation directory (section 8/1m depending on the platform),
      • -
      • MAN3EXT; extension for man pages in section 3,
      • +
      • AR; the library archiver command,
      • -
      • MAN5EXT; extension for man pages in section 5,
      • +
      • ARFLAGS; options for the library archiver command,
      • -
      • MAN7EXT; extension for man pages in section 7,
      • +
      • AWK; the local awk command,
      • -
      • MAN8DIR; subdirectory for man pages in section 8,
      • +
      • BINDIR; the binary installation directory,
      • -
      • MAN8EXT; extension for man pages in section 8,
      • +
      • BUILDROOT; optional installation prefix (defaults to DSTROOT),
      • CC; the C compiler command,
      • CFLAGS; options for the C compiler command,
      • +
      • CHMOD; the chmod command,
      • +
      • CXX; the C++ compiler command,
      • CXXFLAGS; options for the C++ compiler command,
      • -
      • DSOCOMMAND; the shared library building command,
      • +
      • DATADIR; the data file installation directory,
      • + +
      • DSO; the C shared library building command,
      • + +
      • DSOXX; the C++ shared library building command,
      • DSOFLAGS; options for the shared library building command,
      • +
      • INCLUDEDIR; the public header file installation directory,
      • +
      • INSTALL; the install command,
      • INSTALL_BIN; the program installation command,
      • +
      • INSTALL_COMPDATA; the compressed data file installation command,
      • + +
      • INSTALL_CONFIG; the configuration file installation command,
      • +
      • INSTALL_DATA; the data file installation command,
      • INSTALL_DIR; the directory installation command,
      • @@ -1036,77 +790,82 @@ generated by the autoconf software:

      • INSTALL_SCRIPT; the shell script installation command,
      • +
      • LD; the linker command,
      • +
      • LDFLAGS; options for the linker,
      • +
      • LIBDIR; the library installation directory,
      • +
      • LIBS; libraries for all programs,
      • LN; the ln command,
      • -
      • OPTIM; common compiler optimization options,
      • +
      • MAN1EXT; extension for man pages in section 1,
      • -
      • RM; the rm command,
      • +
      • MAN3EXT; extension for man pages in section 3,
      • -
      • SHELL; the sh (POSIX shell) command,
      • +
      • MAN5EXT; extension for man pages in section 5,
      • -
      • STRIP; the strip command,
      • +
      • MAN7EXT; extension for man pages in section 7,
      • -
      • bindir; the binary installation directory,
      • +
      • MAN8DIR; subdirectory for man pages in section 8,
      • + +
      • MAN8EXT; extension for man pages in section 8,
      • -
      • datadir; the data file installation directory,
      • +
      • MANDIR; the man page installation directory,
      • -
      • exec_prefix; the installation prefix for executable files,
      • +
      • OPTIM; common compiler optimization options,
      • + +
      • PRIVATEINCLUDE; the private header file installation directory,
      • -
      • libdir; the library installation directory,
      • +
      • RM; the rm command,
      • -
      • mandir; the man page installation directory,
      • +
      • SHELL; the sh (POSIX shell) command,
      • -
      • prefix; the installation prefix for non-executable files, and
      • +
      • STRIP; the strip command,
      • srcdir; the source directory.
      +

      Standard Targets

      -

      The following standard targets must be defined in each -makefile:

      +

      The following standard targets are defined in each makefile:

        -
      • all; creates all target programs, - libraries, and documentation files,
      • +
      • all; creates all target programs, libraries, and documentation files,
      • + +
      • clean; removes all target programs libraries, documentation files, and object files,
      • + +
      • depend; generates automatic dependencies for any C or C++ source files (also see "Dependencies"),
      • + +
      • distclean; removes autoconf-generated files in addition to those removed by the "clean" target,
      • + +
      • install; installs all distribution files in their corresponding locations (also see "Install/Uninstall Support"),
      • -
      • clean; removes all target programs, - libraries, documentation files, and object files,
      • +
      • install-data; installs all data files in their corresponding locations (also see "Install/Uninstall Support"),
      • -
      • depend; generates automatic dependencies - for any C or C++ source files (also see "Dependencies"),
      • +
      • install-exec; installs all executable files in their corresponding locations (also see "Install/Uninstall Support"),
      • -
      • distclean; removes autoconf-generated files - in addition to those removed by the "clean" target,
      • +
      • install-headers; installs all include files in their corresponding locations (also see "Install/Uninstall Support"),
      • -
      • install; installs all distribution files in - their corresponding locations (also see "Install/Uninstall Support"),
      • +
      • install-libs; installs all library files in their corresponding locations (also see "Install/Uninstall Support"),
      • -
      • uninstall; removes all distribution files from - their corresponding locations (also see "Install/Uninstall Support"), and
      • +
      • uninstall; removes all distribution files from their corresponding locations (also see "Install/Uninstall Support"), and

      Object Files

      -

      Object files (the result of compiling a C or C++ source file) -have the extension ".o".

      +

      Object files (the result of compiling a C or C++ source file) have the extension ".o".

      +

      Programs

      -

      Program files are the result of linking object files and -libraries together to form an executable file. A typical -program target looks like:

      +

      Program files are the result of linking object files and libraries together to form an executable file. A typical program target looks like:

       program: $(OBJS)
      @@ -1114,10 +873,10 @@ program: $(OBJS)
       → $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
       
      +

      Static Libraries

      -

      Static libraries have a prefix of "lib" and the extension -".a". A typical static library target looks like:

      +

      Static libraries have a prefix of "lib" and the extension ".a". A typical static library target looks like:

       libname.a: $(OBJECTS)
      @@ -1127,12 +886,10 @@ libname.a: $(OBJECTS)
       → $(RANLIB) $@
       
      +

      Shared Libraries

      -

      Shared libraries have a prefix of "lib" and the extension -".dylib", ".sl", ".so", or "_s.a" depending on the operating -system. A typical shared library is composed of several targets -that look like:

      +

      Shared libraries have a prefix of "lib" and the extension ".dylib", ".sl", ".so", or "_s.a" depending on the operating system. A typical shared library is composed of several targets that look like:

       libname.so: $(OBJECTS)
      @@ -1173,46 +930,29 @@ libname_s.a: $(OBJECTS)
       
       

      Dependencies

      -

      Static dependencies are expressed in each makefile following the -target, for example:

      +

      Static dependencies are expressed in each makefile following the target, for example:

       foo: bar
       
      -

      Static dependencies shall only be used when it is not -possible to automatically generate them. Automatic dependencies -are stored in a file named "Dependencies" and included at the -end of the makefile. The following "depend" target rule shall be -used to create the automatic dependencies: +

      Static dependencies are only used when it is not possible to automatically generate them. Automatic dependencies are stored in a file named "Dependencies" and included at the end of the makefile. The following "depend" target rule is used to create the automatic dependencies:

       depend:
      -→ $(MAKEDEPEND) -Y -I.. -f Dependencies $(OBJS:.o=.c)
      +→ $(CC) -MM $(ALL_CFLAGS) $(OBJS:.o=.c) >Dependencies
       
      -

      We only regenerate the automatic dependencies on a Linux -system and express any non-Linux dependencies statically in the -makefile.

      +

      We regenerate the automatic dependencies on an OS X system and express any non-OS X dependencies statically in the makefile.

      +

      Install/Uninstall Support

      -

      All makefiles must contain install and uninstall rules which -install or remove the corresponding software. These rules must -use the $(BUILDROOT) variable as a prefix to any -installation directory so that CUPS can be installed in a -temporary location for packaging by programs like -rpmbuild.

      - -

      The $(INSTALL_BIN), $(INSTALL_DATA), -$(INSTALL_DIR), $(INSTALL_LIB), -$(INSTALL_MAN), and $(INSTALL_SCRIPT) -variables must be used when installing files so that the proper -ownership and permissions are set on the installed files.

      - -

      The $(RANLIB) command must be run on any static -libraries after installation since the symbol table is -invalidated when the library is copied on some platforms.

      +

      All makefiles contains install and uninstall rules which install or remove the corresponding software. These rules must use the $(BUILDROOT) variable as a prefix to any installation directory so that CUPS can be installed in a temporary location for packaging by programs like rpmbuild.

      + +

      The $(INSTALL_BIN), $(INSTALL_COMPDATA), $(INSTALL_CONFIG), $(INSTALL_DATA), $(INSTALL_DIR), $(INSTALL_LIB), $(INSTALL_MAN), and $(INSTALL_SCRIPT) variables must be used when installing files so that the proper ownership and permissions are set on the installed files.

      + +

      The $(RANLIB) command must be run on any static libraries after installation since the symbol table is invalidated when the library is copied on some platforms.

      diff --git a/doc/help/spec-ppd.html b/doc/help/spec-ppd.html index d85918978..ec0a03091 100644 --- a/doc/help/spec-ppd.html +++ b/doc/help/spec-ppd.html @@ -397,6 +397,7 @@ div.contents ul.subcontents li {
    • cupsMediaQualifier3
    • cupsMinSize
    • cupsMaxSize
    • +
    • cupsPageSizeCategory
    • General Attributes
      • cupsBackSide
      • @@ -1398,6 +1399,21 @@ are used to identify options to use for matching.

    • +

      CUPS 1.4/OS X 10.6cupsPageSizeCategory

      + +

      *cupsPageSizeCategory name/text: "name name2 ... nameN"

      + +

      This keyword lists related paper size names that should be grouped together in the Print or Page Setup dialogs. The "name" portion of the keyword specifies the root/default size for the grouping. On OS X the grouped paper sizes are shown in a submenu of the main paper size. When omitted, sizes with the same dimensions are automatically grouped together, for example "Letter" and "Letter.Borderless".

      + +

      Example:

      + +
      +*% Specify grouping of borderless/non-borderless sizes
      +*cupsPageSizeCategory Letter/US Letter: "Letter Letter.Borderless"
      +*cupsPageSizeCategory A4/A4: "A4 A4.Borderless"
      +
      + +

      General Attributes

      CUPS 1.3/OS X 10.5cupsBackSide

      @@ -1776,6 +1792,7 @@ the output for a specific model of printer.

      *cupsModelNumber: 1234 +

      CUPS 1.3/OS X 10.5cupsPJLCharset

      *cupsPJLCharset: "ISO character set name"

      @@ -2236,6 +2253,8 @@ the device.

        +
      • Added cupsPageSizeCategory keyword (originally defined in CUPS 1.4).
      • +
      • Added cupsMaxCopies keyword.
      • Documented JCLToPDFInterpreter keyword.
      • diff --git a/examples/ppdx.c b/examples/ppdx.c new file mode 100644 index 000000000..61588cad1 --- /dev/null +++ b/examples/ppdx.c @@ -0,0 +1,314 @@ +/* + * "$Id$" + * + * Example code for encoding and decoding large amounts of data in a PPD file. + * This would typically be used in a driver to save configuration/state + * information that could be used by an application. + * + * Copyright 2012 by Apple Inc. + * + * 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" + * which should have been included with this file. If this file is + * 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: + * + * ppdxReadData() - Read encoded data from a ppd_file_t *. + * ppdxWriteData() - Writes encoded data to stderr using PPD: messages. + */ + +/* + * Include necessary headers... + */ + +#include "ppdx.h" +#include +#include +#include +#include /* For compression of the data */ + + +/* + * Constants... + */ + +#define PPDX_MAX_VALUE (PPD_MAX_LINE - PPD_MAX_NAME - 4) + /* Max value length with delimiters + nul */ +#define PPDX_MAX_CHUNK (PPDX_MAX_VALUE * 3 / 4) + /* Max length of each chunk when Base64-encoded */ + + +/* + * 'ppdxReadData()' - Read encoded data from a ppd_file_t *. + * + * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns + * an allocated pointer to the data (which is nul-terminated for convenience) + * along with the length of the data in the variable pointed to by "datasize", + * which can be NULL to indicate the caller doesn't need the length. + * + * Returns NULL if no data is present in the PPD with the prefix. + */ + +void * /* O - Data or NULL */ +ppdxReadData(ppd_file_t *ppd, /* I - PPD file */ + const char *name, /* I - Keyword prefix */ + size_t *datasize) /* O - Size of data or NULL for don't care */ +{ + char keyword[PPD_MAX_NAME], /* Keyword name */ + decoded[PPDX_MAX_CHUNK + 1]; + /* Decoded string */ + unsigned chunk = 0; /* Current chunk number */ + int len; /* Length of current chunk */ + ppd_attr_t *attr; /* Keyword/value from PPD file */ + Bytef *data; /* Pointer to data */ + size_t alloc_size; /* Allocated size of data buffer */ + z_stream decomp; /* Decompressor stream */ + int error; /* Error/status from inflate() */ + + + /* + * Range check input... + */ + + if (datasize) + *datasize = 0; + + if (!ppd || !name) + return (NULL); + + /* + * First see if there are any instances of the named keyword in the PPD... + */ + + snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk); + if ((attr = ppdFindAttr(ppd, keyword, NULL)) == NULL) + return (NULL); + + /* + * Allocate some memory and start decoding... + */ + + data = malloc(257); + alloc_size = 256; + + memset(&decomp, 0, sizeof(decomp)); + decomp.next_out = data; + decomp.avail_out = 256; + + inflateInit(&decomp); + + do + { + /* + * Grab the data from the current attribute and decode it... + */ + + len = sizeof(decoded); + if (!httpDecode64_2(decoded, &len, attr->value) || len == 0) + break; + +// printf("chunk %04x has length %d\n", chunk, len); + + /* + * Decompress this chunk... + */ + + decomp.next_in = decoded; + decomp.avail_in = len; + + do + { + Bytef *temp; /* Temporary pointer */ + size_t temp_size; /* Temporary allocation size */ + +// printf("Before inflate: avail_in=%d, avail_out=%d\n", decomp.avail_in, +// decomp.avail_out); + + if ((error = inflate(&decomp, Z_NO_FLUSH)) < Z_OK) + { + fprintf(stderr, "ERROR: inflate returned %d (%s)\n", error, decomp.msg); + break; + } + +// printf("After inflate: avail_in=%d, avail_out=%d, error=%d\n", +// decomp.avail_in, decomp.avail_out, error); + + if (decomp.avail_out == 0) + { + if (alloc_size < 2048) + temp_size = alloc_size * 2; + else if (alloc_size < PPDX_MAX_DATA) + temp_size = alloc_size + 2048; + else + break; + + if ((temp = realloc(data, temp_size + 1)) == NULL) + { + free(data); + return (NULL); + } + + decomp.next_out = temp + (decomp.next_out - data); + decomp.avail_out = temp_size - alloc_size; + data = temp; + alloc_size = temp_size; + } + } + while (decomp.avail_in > 0); + + chunk ++; + snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk); + } + while ((attr = ppdFindAttr(ppd, keyword, NULL)) != NULL); + + inflateEnd(&decomp); + + /* + * Nul-terminate the data (usually a string)... + */ + + *(decomp.next_out) = '\0'; + + if (datasize) + *datasize = decomp.next_out - data; + + return (data); +} + + +/* + * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages. + * + * Writes chunked data to the PPD file using PPD: messages sent to stderr for + * cupsd. "name" must be a valid PPD keyword string whose length is less than + * 37 characters to allow for chunk numbering. "data" provides a pointer to the + * data to be written, and "datasize" provides the length. + */ + +extern void +ppdxWriteData(const char *name, /* I - Base name of keyword */ + const void *data, /* I - Data to write */ + size_t datasize) /* I - Number of bytes in data */ +{ + char buffer[PPDX_MAX_CHUNK], /* Chunk buffer */ + encoded[PPDX_MAX_VALUE + 1], + /* Encoded data */ + pair[PPD_MAX_LINE], /* name=value pair */ + line[PPDX_MAX_STATUS], /* Line buffer */ + *lineptr, /* Current position in line buffer */ + *lineend; /* End of line buffer */ + unsigned chunk = 0; /* Current chunk number */ + int len; /* Length of current chunk */ + z_stream comp; /* Compressor stream */ + int error; /* Error/status from deflate() */ + + + /* + * Range check input... + */ + + if (!name || (!data && datasize > 0) || datasize > PPDX_MAX_DATA) + return; + + strlcpy(line, "PPD:", sizeof(line)); + lineptr = line + 4; + lineend = line + sizeof(line) - 2; + + if (datasize > 0) + { + /* + * Compress and encode output... + */ + + memset(&comp, 0, sizeof(comp)); + comp.next_in = (Bytef *)data; + comp.avail_in = datasize; + + deflateInit(&comp, 9); + + do + { + /* + * Compress a chunk... + */ + + comp.next_out = buffer; + comp.avail_out = sizeof(buffer); + + if ((error = deflate(&comp, Z_FINISH)) < Z_OK) + { + fprintf(stderr, "ERROR: deflate returned %d (%s)\n", error, comp.msg); + break; + } + + /* + * Write a chunk... + */ + + len = sizeof(buffer) - comp.avail_out; + httpEncode64_2(encoded, sizeof(encoded), buffer, len); + + len = (int)snprintf(pair, sizeof(pair), " %s%04x=%s", name, chunk, + encoded); +#ifdef DEBUG + fprintf(stderr, "DEBUG: *%s%04x: \"%s\"\n", name, chunk, encoded); +#endif /* DEBUG */ + + if ((lineptr + len) >= lineend) + { + *lineptr++ = '\n'; + *lineptr = '\0'; + + fputs(line, stderr); + lineptr = line + 4; + } + + strlcpy(lineptr, pair, lineend - lineptr); + lineptr += len; + + /* + * Setup for the next one... + */ + + chunk ++; + } + while (comp.avail_out == 0); + } + + deflateEnd(&comp); + + /* + * Write a trailing empty chunk to signal EOD... + */ + + len = (int)snprintf(pair, sizeof(pair), " %s%04x=\"\"", name, chunk); +#ifdef DEBUG + fprintf(stderr, "DEBUG: *%s%04x: \"\"\n", name, chunk); +#endif /* DEBUG */ + + if ((lineptr + len) >= lineend) + { + *lineptr++ = '\n'; + *lineptr = '\0'; + + fputs(line, stderr); + lineptr = line + 4; + } + + strlcpy(lineptr, pair, lineend - lineptr); + lineptr += len; + + *lineptr++ = '\n'; + *lineptr = '\0'; + + fputs(line, stderr); +} + + +/* + * End of "$Id$". + */ diff --git a/examples/ppdx.h b/examples/ppdx.h new file mode 100644 index 000000000..1be6928da --- /dev/null +++ b/examples/ppdx.h @@ -0,0 +1,82 @@ +/* + * "$Id$" + * + * Header for PPD data encoding example code. + * + * Copyright 2012 by Apple Inc. + * + * 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" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". + * + * This file is subject to the Apple OS-Developed Software exception. + * + */ + +#ifndef _PPDX_H_ +# define _PPDX_H_ + + +/* + * Include necessary headers... + */ + +# include + + +/* + * C++ magic... + */ + +# ifdef __cplusplus +extern "C" { +# endif /* __cplusplus */ + + +/* + * Maximum amount of data to encode/decode... + */ + +# define PPDX_MAX_STATUS 1024 /* Limit on log messages in 10.6 */ +# define PPDX_MAX_DATA 16777216/* 16MiB */ + + +/* + * 'ppdxReadData()' - Read encoded data from a ppd_file_t *. + * + * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns + * an allocated pointer to the data (which is nul-terminated for convenience) + * along with the length of the data in the variable pointed to by "datasize", + * which can be NULL to indicate the caller doesn't need the length. + * + * Returns NULL if no data is present in the PPD with the prefix. + */ + +extern void *ppdxReadData(ppd_file_t *ppd, const char *name, + size_t *datasize); + + +/* + * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages. + * + * Writes chunked data to the PPD file using PPD: messages sent to stderr for + * cupsd. "name" must be a valid PPD keyword string whose length is less than + * 37 characters to allow for chunk numbering. "data" provides a pointer to the + * data to be written, and "datasize" provides the length. + */ + +extern void ppdxWriteData(const char *name, const void *data, + size_t datasize); + + +# ifdef __cplusplus +} +# endif /* __cplusplus */ + +#endif /* !_PPDX_H */ + +/* + * End of "$Id$". + */ diff --git a/examples/testppdx.c b/examples/testppdx.c new file mode 100644 index 000000000..f1c168688 --- /dev/null +++ b/examples/testppdx.c @@ -0,0 +1,117 @@ +/* + * "$Id$" + * + * Test program for PPD data encoding example code. + * + * Compile with: + * + * gcc -o testppdx -D_PPD_DEPRECATED="" -g testppdx.c ppdx.c -lcups -lz + * + * Copyright 2012 by Apple Inc. + * + * 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" + * which should have been included with this file. If this file is + * 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() - Read data from a test PPD file and write out new chunks. + */ + +/* + * Include necessary headers... + */ + +#include +#include +#include "ppdx.h" + + +/* + * 'main()' - Read data from a test PPD file and write out new chunks. + */ + +int /* O - Exit status */ +main(void) +{ + int status = 0; /* Exit status */ + FILE *fp; /* File to read */ + char contents[8193], /* Contents of file */ + *data; /* Data from PPD */ + size_t contsize, /* File size */ + datasize; /* Data size */ + ppd_file_t *ppd; /* Test PPD */ + + + /* + * Open the PPD and get the data from it... + */ + + ppd = ppdOpenFile("testppdx.ppd"); + data = ppdxReadData(ppd, "EXData", &datasize); + + /* + * Open this source file and read it... + */ + + fp = fopen("testppdx.c", "r"); + if (fp) + { + contsize = fread(contents, 1, sizeof(contents) - 1, fp); + fclose(fp); + contents[contsize] = '\0'; + } + else + { + contents[0] = '\0'; + contsize = 0; + } + + /* + * Compare data... + */ + + if (data) + { + if (contsize != datasize) + { + fprintf(stderr, "ERROR: PPD has %ld bytes, test file is %ld bytes.\n", + (long)datasize, (long)contsize); + status = 1; + } + else if (strcmp(contents, data)) + { + fputs("ERROR: PPD and test file are not the same.\n", stderr); + status = 1; + } + + if (status) + { + if ((fp = fopen("testppdx.dat", "wb")) != NULL) + { + fwrite(data, 1, datasize, fp); + fclose(fp); + fputs("ERROR: See testppdx.dat for data from PPD.\n", stderr); + } + else + perror("Unable to open 'testppdx.dat'"); + } + + free(data); + } + + printf("Encoding %ld bytes for PPD...\n", (long)contsize); + + ppdxWriteData("EXData", contents, contsize); + + return (1); +} + + +/* + * End of "$Id$". + */ diff --git a/examples/testppdx.ppd b/examples/testppdx.ppd new file mode 100644 index 000000000..179b1ec6c --- /dev/null +++ b/examples/testppdx.ppd @@ -0,0 +1,121 @@ +*PPD-Adobe: "4.3" +*% +*% "$Id$" +*% +*% Test PPD file for data encoding example. +*% +*% This file cannot be used with any known printers. +*% +*% Copyright 2007-2012 by Apple Inc. +*% Copyright 2002-2006 by Easy Software Products. +*% +*% 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" +*% which should have been included with this file. If this file is +*% file is missing or damaged, see the license at "http://www.cups.org/". +*FormatVersion: "4.3" +*FileVersion: "1.3" +*LanguageVersion: English +*LanguageEncoding: ISOLatin1 +*PCFileName: "TESTPPDX.PPD" +*Manufacturer: "Apple" +*Product: "(Test PPDX)" +*cupsVersion: 1.6 +*ModelName: "Test PPDX" +*ShortNickName: "Test PPDX" +*NickName: "Test PPDX for CUPS" +*PSVersion: "(3010.000) 0" +*LanguageLevel: "3" +*ColorDevice: True +*DefaultColorSpace: RGB +*FileSystem: False +*Throughput: "1" +*LandscapeOrientation: Plus90 +*TTRasterizer: Type42 +*cupsFilter: "application/vnd.cups-raster 0 -" +*RequiresPageRegion All: True + +*% For PageSize, we have put all of the translations in-line... +*OpenUI *PageSize/Page Size: PickOne +*OrderDependency: 10 AnySetup *PageSize +*DefaultPageSize: Letter +*PageSize Letter/US Letter: "PageSize=Letter" +*PageSize Letter.Banner/US Letter Banner: "PageSize=Letter.Banner" +*PageSize Letter.Fullbleed/US Letter Borderless: "PageSize=Letter.Fullbleed" +*PageSize A4/A4: "PageSize=A4" +*PageSize Env10/#10 Envelope: "PageSize=Env10" +*CloseUI: *PageSize + +*% For PageRegion, we have separated the translations... +*OpenUI *PageRegion/Page Region: PickOne +*OrderDependency: 10 AnySetup *PageRegion +*DefaultPageRegion: Letter +*PageRegion Letter/US Letter: "PageRegion=Letter" +*PageRegion Letter.Banner/US Letter Banner: "PageRegion=Letter.Fullbleed" +*PageRegion Letter.Fullbleed/US Letter Borderless: "PageRegion=Letter.Fullbleed" +*PageRegion A4/A4: "PageRegion=A4" +*PageRegion Env10/#10 Envelope: "PageRegion=Env10" +*CloseUI: *PageRegion + +*DefaultImageableArea: Letter +*ImageableArea Letter: "18 36 594 756" +*ImageableArea Letter.Banner: "18 0 594 792" +*ImageableArea Letter.Fullbleed: "0 0 612 792" +*ImageableArea A4: "18 36 577 806" +*ImageableArea Env10: "18 36 279 648" + +*DefaultPaperDimension: Letter +*PaperDimension Letter: "612 792" +*PaperDimension Letter.Banner: "612 792" +*PaperDimension Letter.Fullbleed: "612 792" +*PaperDimension A4: "595 842" +*PaperDimension Env10: "297 684" + +*DefaultFont: Courier +*Font AvantGarde-Book: Standard "(001.006S)" Standard ROM +*Font AvantGarde-BookOblique: Standard "(001.006S)" Standard ROM +*Font AvantGarde-Demi: Standard "(001.007S)" Standard ROM +*Font AvantGarde-DemiOblique: Standard "(001.007S)" Standard ROM +*Font Bookman-Demi: Standard "(001.004S)" Standard ROM +*Font Bookman-DemiItalic: Standard "(001.004S)" Standard ROM +*Font Bookman-Light: Standard "(001.004S)" Standard ROM +*Font Bookman-LightItalic: Standard "(001.004S)" Standard ROM +*Font Courier: Standard "(002.004S)" Standard ROM +*Font Courier-Bold: Standard "(002.004S)" Standard ROM +*Font Courier-BoldOblique: Standard "(002.004S)" Standard ROM +*Font Courier-Oblique: Standard "(002.004S)" Standard ROM +*Font Helvetica: Standard "(001.006S)" Standard ROM +*Font Helvetica-Bold: Standard "(001.007S)" Standard ROM +*Font Helvetica-BoldOblique: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow: Standard "(001.006S)" Standard ROM +*Font Helvetica-Narrow-Bold: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow-BoldOblique: Standard "(001.007S)" Standard ROM +*Font Helvetica-Narrow-Oblique: Standard "(001.006S)" Standard ROM +*Font Helvetica-Oblique: Standard "(001.006S)" Standard ROM +*Font NewCenturySchlbk-Bold: Standard "(001.009S)" Standard ROM +*Font NewCenturySchlbk-BoldItalic: Standard "(001.007S)" Standard ROM +*Font NewCenturySchlbk-Italic: Standard "(001.006S)" Standard ROM +*Font NewCenturySchlbk-Roman: Standard "(001.007S)" Standard ROM +*Font Palatino-Bold: Standard "(001.005S)" Standard ROM +*Font Palatino-BoldItalic: Standard "(001.005S)" Standard ROM +*Font Palatino-Italic: Standard "(001.005S)" Standard ROM +*Font Palatino-Roman: Standard "(001.005S)" Standard ROM +*Font Symbol: Special "(001.007S)" Special ROM +*Font Times-Bold: Standard "(001.007S)" Standard ROM +*Font Times-BoldItalic: Standard "(001.009S)" Standard ROM +*Font Times-Italic: Standard "(001.007S)" Standard ROM +*Font Times-Roman: Standard "(001.007S)" Standard ROM +*Font ZapfChancery-MediumItalic: Standard "(001.007S)" Standard ROM +*Font ZapfDingbats: Special "(001.004S)" Standard ROM +*% +*% End of "$Id$". +*% +*EXData0000: "eNqlVm1v2zYQ/hz/ipvWznLgl6T7siXrgCJ2AANBEyQpNmAtAlqiLG4SKZBU7LTIf9/dUW9Og32ZAFsieffcc88dKS2OR3AM0Zt1+ibCJxoA3EvnobJma0UJmbFwc7OEVHgBUicmVXoLci/KqpCAQznvHC9MWSmc3Smfn3WzdG2TBGYGPCJXVbqH2fIBQR+Wq5vb1cWH+9XyfRTBbNs" +*EXData0001: "GcwTaG6zIqkrh7evgzDVk1Xb3MO7k9N3sHmCDxWRWeuk53KfSxf4paC087ZOvDLaTcF54WUptcdnoVO0KavaS9um7EBYCT6XAQhnK2n9E5hsEIc9yQ6XvUw8RkEelzKVVhQI2TAMEIXYzQGWClmoTU002L1GgmwVIpraF0ozXYoOGUkZXa0vVh/vVnO/91FA2+UqycHlpi5SyMWjhI0" +*EXData0002: "lBq9kqKmbEl9BFCOETDwOuuHoFxAaQZQKueooljmVJRiK1NUSHL+UKhEaiQpPES599XZYrHb7eZUkLmx20U0lLsPAK7e/I2agDcME1S7vpst5aMsUM0U7kzmd5S03CeyIkVaqAujPdWm759SKB1PYAa3UqShDzNrShDcLdycHJgk3VnlWUnQcgdJXut/HCEvRqMFw62DSricSOeEfYI" +*EXData0003: "HFGldfN5Y/djIyT85nyqzDz//WDKolgHcxE3ah6N2hDjQHj8vxkr7Y/oWhzDNYKt9spz89aOljnKo1HpZPRtBMDGzep7ODlntxcuAJfrq9XR0XFWnQfcS6KBZbJElC2SXNijo6Spwl+/nP7685cp27aVoY3A9NEesSi9Bm3ZZUopMpxTX+WDZzx6nPZRaRggCIFG5z1Iuwh0DDxQNAQ" +*EXData0004: "OcbnJtJ9qyPpNAISHpW/rmTYOrRC4m6l53FfAuW50OzH4IBq4Y1ciVYcdUcQ/kWTc7Rib7baU0GJYIyDKUSrP2kQTeGnNgd0eEmHNoSpbSL7orPch1SyCkNkuDn0gEKC0JENLFQGcVZN8InKjYVqJCU3wovbmk3hdMoCmqybo+2Ds+h/zs5ZUhgn427c1btF/YKw488nY1p+xp8snDw" +*EXData0005: "CM3WJ0OzASUA7kH27eSgtwNteZJqmDklRnN9ajTTYf3wHjpxeTXYkGK4F30W4zaV1lI1bm+vb8+4+Llw8LagMxm1nIZt155O3fz8s46mDVh7xYXR20kbcNqMWzKNWgDdRjsNM8/8TyIxeTwmkrIaVITze0m/9i4ekqbO6JmSVNqE/nWilMwWQrL/waMTMCwdxqT5+NVOQ4LUa7tNNJk" +*EXData0006: "kOQfP11dTRqvb51GGZ9WXC3usl6nrpNe667v072jN8wgNn9mpMPj49V0n5t7043hwrezNTaOPmmxCacZ5QbjIf44mhwIhDsmpDFpmxSabopW7TdO1yftNxA2LZP6rifCSbL/g8Th06E/GPoWOLS30tdWQ3yKw+fu1bHCDsDTNXyPhbfAvwxF0gE=" +*EXData0007: "" diff --git a/filter/spec-ppd.shtml b/filter/spec-ppd.shtml index 177602790..0f54d3785 100644 --- a/filter/spec-ppd.shtml +++ b/filter/spec-ppd.shtml @@ -957,6 +957,21 @@ are used to identify options to use for matching.

        +

        CUPS 1.4/OS X 10.6cupsPageSizeCategory

        + +

        *cupsPageSizeCategory name/text: "name name2 ... nameN"

        + +

        This keyword lists related paper size names that should be grouped together in the Print or Page Setup dialogs. The "name" portion of the keyword specifies the root/default size for the grouping. On OS X the grouped paper sizes are shown in a submenu of the main paper size. When omitted, sizes with the same dimensions are automatically grouped together, for example "Letter" and "Letter.Borderless".

        + +

        Example:

        + +
        +*% Specify grouping of borderless/non-borderless sizes
        +*cupsPageSizeCategory Letter/US Letter: "Letter Letter.Borderless"
        +*cupsPageSizeCategory A4/A4: "A4 A4.Borderless"
        +
        + +

        General Attributes

        CUPS 1.3/OS X 10.5cupsBackSide

        @@ -1335,6 +1350,7 @@ the output for a specific model of printer.

        *cupsModelNumber: 1234 +

        CUPS 1.3/OS X 10.5cupsPJLCharset

        *cupsPJLCharset: "ISO character set name"

        @@ -1795,6 +1811,8 @@ the device.

          +
        • Added cupsPageSizeCategory keyword (originally defined in CUPS 1.4).
        • +
        • Added cupsMaxCopies keyword.
        • Documented JCLToPDFInterpreter keyword.
        • diff --git a/install-sh b/install-sh index bd8f459c7..41d944e3e 100755 --- a/install-sh +++ b/install-sh @@ -61,7 +61,7 @@ src="" dst="" dir_arg="" -function gzipcp { +gzipcp() { # gzipcp from to $gzipprog -9 <"$1" >"$2" } diff --git a/man/cupsd.conf.man.in b/man/cupsd.conf.man.in index b5d790f8b..fd0768fd1 100644 --- a/man/cupsd.conf.man.in +++ b/man/cupsd.conf.man.in @@ -12,7 +12,7 @@ .\" which should have been included with this file. If this file is .\" file is missing or damaged, see the license at "http://www.cups.org/". .\" -.TH cupsd.conf 5 "CUPS" "15 February 2012" "Apple Inc." +.TH cupsd.conf 5 "CUPS" "18 May 2012" "Apple Inc." .SH NAME cupsd.conf \- server configuration file for cups .SH DESCRIPTION @@ -591,6 +591,13 @@ SSLPort .br Listens on the specified port for encrypted connections. .TP 5 +StrictConformance Yes +.TP 5 +StrictConformance No +.br +Specifies whether the scheduler requires clients to strictly adhere to the IPP +specifications. The default is No. +.TP 5 SubscriptionPrivateAccess all .TP 5 SubscriptionPrivateAccess default diff --git a/man/filter.man b/man/filter.man index 50b087247..1acff4b4e 100644 --- a/man/filter.man +++ b/man/filter.man @@ -12,7 +12,7 @@ .\" which should have been included with this file. If this file is .\" file is missing or damaged, see the license at "http://www.cups.org/". .\" -.TH filter 7 "CUPS" "13 April 2012" "Apple Inc." +.TH filter 7 "CUPS" "18 May 2012" "Apple Inc." .SH NAME filter \- cups file conversion filter interface .SH SYNOPSIS @@ -169,6 +169,11 @@ CUPS_FILETYPE The type of file being printed: "job-sheet" for a banner page and "document" for a regular print file. .TP 5 +CUPS_MAX_MESSAGE +.br +The maximum size of a message sent to stderr, including any leading prefix and +the trailing newline. +.TP 5 CUPS_SERVERROOT .br The root directory of the server. diff --git a/man/ipptoolfile.man b/man/ipptoolfile.man index 00a6df7f9..0d6452ea4 100644 --- a/man/ipptoolfile.man +++ b/man/ipptoolfile.man @@ -11,7 +11,7 @@ .\" which should have been included with this file. If this file is .\" file is missing or damaged, see the license at "http://www.cups.org/". .\" -.TH ipptoolfile 5 "CUPS" "14 March 2012" "Apple Inc." +.TH ipptoolfile 5 "CUPS" "11 May 2012" "Apple Inc." .SH NAME ipptoolfile \- ipptool file format @@ -266,6 +266,10 @@ Requires the EXPECT attribute to be in the specified group tag. OF-TYPE tag[,tag,...] Requires the EXPECT attribute to use the specified value tag(s). .TP 5 +REPEAT-LIMIT number +.br +Specifies the maximum number of times to repeat. The default value is 1000. +.TP 5 REPEAT-MATCH .TP 5 REPEAT-NO-MATCH @@ -325,6 +329,10 @@ Makes the STATUS apply only if the specified variable is defined. IF-NOT-DEFINED variable-name Makes the STATUS apply only if the specified variable is not defined. .TP 5 +REPEAT-LIMIT number +.br +Specifies the maximum number of times to repeat. The default value is 1000. +.TP 5 REPEAT-MATCH .TP 5 REPEAT-NO-MATCH diff --git a/packaging/cups.list.in b/packaging/cups.list.in index 8f1f4a106..6dedcaf5b 100644 --- a/packaging/cups.list.in +++ b/packaging/cups.list.in @@ -472,6 +472,7 @@ f 0444 root sys $DATADIR/ipptool/print-job.test test/print-job.test f 0444 root sys $DATADIR/ipptool test/document-*.p* f 0444 root sys $DATADIR/ipptool test/ipp-*.test f 0444 root sys $DATADIR/ipptool test/onepage-*.p* +f 0444 root sys $DATADIR/ipptool test/testfile.* f 0444 root sys $DATADIR/ipptool/color.jpg test/color.jpg f 0444 root sys $DATADIR/ipptool/gray.jpg test/gray.jpg diff --git a/scheduler/Makefile b/scheduler/Makefile index 2fa6090f5..d6035e0aa 100644 --- a/scheduler/Makefile +++ b/scheduler/Makefile @@ -21,6 +21,7 @@ CUPSDOBJS = \ cert.o \ classes.o \ client.o \ + colorman.o \ conf.o \ dirsvc.o \ env.o \ diff --git a/scheduler/client.c b/scheduler/client.c index 31d12cc61..fcc4d814c 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -4026,9 +4026,19 @@ valid_host(cupsd_client_t *con) /* I - Client connection */ * Check if the hostname is something.local (Bonjour); if so, allow it. */ - if ((end = strrchr(host, '.')) != NULL && - (!_cups_strcasecmp(end, ".local") || !_cups_strncasecmp(end, ".local:", 7) || - !_cups_strcasecmp(end, ".local.") || !_cups_strncasecmp(end, ".local.:", 8))) + if ((end = strrchr(host, '.')) != NULL && end > host && + (!end[1] || end[1] == ':')) + { + /* + * "." on end, work back to second-to-last "."... + */ + for (end --; end > host && *end != '.'; end --); + } + + if (end && (!_cups_strcasecmp(end, ".local") || + !_cups_strncasecmp(end, ".local:", 7) || + !_cups_strcasecmp(end, ".local.") || + !_cups_strncasecmp(end, ".local.:", 8))) return (1); #endif /* HAVE_DNSSD */ diff --git a/scheduler/colorman.c b/scheduler/colorman.c new file mode 100644 index 000000000..c662a2c15 --- /dev/null +++ b/scheduler/colorman.c @@ -0,0 +1,1505 @@ +/* + * "$Id$" + * + * Color management routines for the CUPS scheduler. + * + * Copyright 2007-2012 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" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". + * + * Original DBUS/colord code is Copyright 2011 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Contents: + * + */ + +/* + * Include necessary headers... + */ + +#include "cupsd.h" +#include + +#ifdef __APPLE__ +# include +extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id); +# include +#elif defined(HAVE_DBUS) +# include + +/* + * Defines used by colord. See the reference docs for further details: + * + * http://colord.hughsie.com/api/ref-dbus.html + */ + +# define COLORD_SCOPE_NORMAL "normal" + /* System scope */ +# define COLORD_SCOPE_TEMP "temp" /* Process scope */ +# define COLORD_SCOPE_DISK "disk" /* Lives forever, as stored in DB */ + +# define COLORD_RELATION_SOFT "soft" /* Mapping is not default */ +# define COLORD_RELATION_HARD "hard" /* Explicitly mapped profile */ + +# define COLORD_SPACE_RGB "rgb" /* RGB colorspace */ +# define COLORD_SPACE_CMYK "cmyk" /* CMYK colorspace */ +# define COLORD_SPACE_GRAY "gray" /* Gray colorspace */ +# define COLORD_SPACE_UNKNOWN "unknown" + /* Unknown colorspace */ + +# define COLORD_MODE_PHYSICAL "physical" + /* Actual device */ +# define COLORD_MODE_VIRTUAL "virtual" + /* Virtual device with no hardware */ + +# define COLORD_KIND_PRINTER "printer" + /* printing output device */ + +# define COLORD_DBUS_MSG(p,m) dbus_message_new_method_call(\ + "org.freedesktop.ColorManager", (p),\ + "org.freedesktop.ColorManager", (m)) + /* Macro to make new colord messages */ +# define COLORD_DBUS_PATH "/org/freedesktop/ColorManager" + /* Path for color management system */ +# define COLORD_DBUS_TIMEOUT 5000 /* Timeout for connecting to colord in ms */ +#endif /* __APPLE__ */ + + +/* + * Local globals... + */ + +#if !defined(__APPLE__) && defined(HAVE_DBUS) +static DBusConnection *colord_con = NULL; + /* DBUS connection for colord */ +#endif /* !__APPLE__ && HAVE_DBUS */ + + +/* + * Local functions... + */ + +#ifdef __APPLE__ +static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages, + CFMutableDictionaryRef profile, + unsigned id, const char *name, + const char *text, const char *iccfile); +static void apple_register_profiles(cupsd_printer_t *p); +static void apple_unregister_profiles(cupsd_printer_t *p); + +#elif defined(HAVE_DBUS) +static void colord_create_device(cupsd_printer_t *p, ppd_file_t *ppd, + cups_array_t *profiles, + const char *colorspace, char **format, + const char *relation, const char *scope); +static void colord_create_profile(cups_array_t *profiles, + const char *printer_name, + const char *qualifier, + const char *colorspace, + char **format, const char *iccfile, + const char *scope); +static void colord_delete_device(const char *device_id); +static void colord_device_add_profile(const char *device_path, + const char *profile_path, + const char *relation); +static void colord_dict_add_strings(DBusMessageIter *dict, + const char *key, const char *value); +static char *colord_find_device(const char *device_id); +static void colord_get_qualifier_format(ppd_file_t *ppd, char *format[3]); +static void colord_register_printer(cupsd_printer_t *p); +static void colord_unregister_printer(cupsd_printer_t *p); +#endif /* __APPLE__ */ + + +/* + * 'cupsdRegisterColor()' - Register vendor color profiles in a PPD file. + */ + +void +cupsdRegisterColor(cupsd_printer_t *p) /* I - Printer */ +{ +#ifdef __APPLE__ + apple_unregister_profiles(p); + apple_register_profiles(p); + +#elif defined(HAVE_DBUS) + colord_unregister_printer(p); + colord_register_printer(p); +#endif /* __APPLE__ */ +} + + +/* + * 'cupsdStartColor()' - Initialize color management. + */ + +void +cupsdStartColor(void) +{ +#if !defined(__APPLE__) && defined(HAVE_DBUS) + cupsd_printer_t *p; /* Current printer */ + + + colord_con = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + + for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); + p; + p = (cupsd_printer_t *)cupsArrayNext(Printers)) + cupsdRegisterColor(p); +#endif /* !__APPLE__ && HAVE_DBUS */ +} + + +/* + * 'cupsdStopColor()' - Shutdown color management. + */ + +void +cupsdStopColor(void) +{ +#if !defined(__APPLE__) && defined(HAVE_DBUS) + dbus_connection_unref(colord_con); + colord_con = NULL; +#endif /* !__APPLE__ && HAVE_DBUS */ +} + + +/* + * 'cupsdUnregisterColor()' - Unregister vendor color profiles in a PPD file. + */ + +void +cupsdUnregisterColor(cupsd_printer_t *p)/* I - Printer */ +{ +#ifdef __APPLE__ + apple_unregister_profiles(p); + +#elif defined(HAVE_DBUS) + colord_unregister_printer(p); +#endif /* __APPLE__ */ +} + + +#ifdef __APPLE__ +/* + * 'apple_init_profile()' - Initialize a color profile. + */ + +static void +apple_init_profile( + ppd_file_t *ppd, /* I - PPD file */ + cups_array_t *languages, /* I - Languages in the PPD file */ + CFMutableDictionaryRef profile, /* I - Profile dictionary */ + unsigned id, /* I - Profile ID */ + const char *name, /* I - Profile name */ + const char *text, /* I - Profile UI text */ + const char *iccfile) /* I - ICC filename */ +{ + CFURLRef url; /* URL for profile filename */ + CFMutableDictionaryRef dict; /* Dictionary for name */ + char *language; /* Current language */ + ppd_attr_t *attr; /* Profile attribute */ + CFStringRef cflang, /* Language string */ + cftext; /* Localized text */ + + + (void)id; + + /* + * Build the profile name dictionary... + */ + + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".", + iccfile); + return; + } + + cftext = CFStringCreateWithCString(kCFAllocatorDefault, text, + kCFStringEncodingUTF8); + + if (cftext) + { + CFDictionarySetValue(dict, CFSTR("en_US"), cftext); + CFRelease(cftext); + } + + if (languages) + { + /* + * Find localized names for the color profiles... + */ + + cupsArraySave(ppd->sorted_attrs); + + for (language = (char *)cupsArrayFirst(languages); + language; + language = (char *)cupsArrayNext(languages)) + { + if (iccfile) + { + if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name, + language)) == NULL) + attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language); + } + else + attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language); + + if (attr && attr->text[0]) + { + cflang = CFStringCreateWithCString(kCFAllocatorDefault, language, + kCFStringEncodingUTF8); + cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text, + kCFStringEncodingUTF8); + + if (cflang && cftext) + CFDictionarySetValue(dict, cflang, cftext); + + if (cflang) + CFRelease(cflang); + + if (cftext) + CFRelease(cftext); + } + } + + cupsArrayRestore(ppd->sorted_attrs); + } + + /* + * Fill in the profile data... + */ + + if (iccfile) + { + url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, + (const UInt8 *)iccfile, + strlen(iccfile), false); + + if (url) + { + CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url); + CFRelease(url); + } + } + + CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict); + CFRelease(dict); +} + + +/* + * 'apple_register_profiles()' - Register color profiles for a printer. + */ + +static void +apple_register_profiles( + cupsd_printer_t *p) /* I - Printer */ +{ + int i; /* Looping var */ + char ppdfile[1024], /* PPD filename */ + iccfile[1024], /* ICC filename */ + selector[PPD_MAX_NAME]; + /* Profile selection string */ + ppd_file_t *ppd; /* PPD file */ + ppd_attr_t *attr, /* Profile attributes */ + *profileid_attr,/* cupsProfileID attribute */ + *q1_attr, /* ColorModel (or other) qualifier */ + *q2_attr, /* MediaType (or other) qualifier */ + *q3_attr; /* Resolution (or other) qualifier */ + char q_keyword[PPD_MAX_NAME]; + /* Qualifier keyword */ + const char *q1_choice, /* ColorModel (or other) choice */ + *q2_choice, /* MediaType (or other) choice */ + *q3_choice; /* Resolution (or other) choice */ + ppd_option_t *cm_option; /* Color model option */ + ppd_choice_t *cm_choice; /* Color model choice */ + int num_profiles; /* Number of profiles */ + OSStatus error = 0; /* Last error */ + unsigned device_id, /* Printer device ID */ + profile_id = 0, /* Profile ID */ + default_profile_id = 0; + /* Default profile ID */ + CFMutableDictionaryRef device_name; /* Printer device name dictionary */ + CFStringRef printer_name; /* Printer name string */ + cups_array_t *languages; /* Languages array */ + CFMutableDictionaryRef profiles, /* Dictionary of profiles */ + profile; /* Current profile info dictionary */ + CFStringRef dict_key; /* Key in factory profile dictionary */ + + + /* + * Make sure ColorSync is available... + */ + + if (ColorSyncRegisterDevice == NULL) + return; + + /* + * Try opening the PPD file for this printer... + */ + + snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name); + if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL) + return; + + /* + * See if we have any profiles... + */ + + for (num_profiles = 0, attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); + attr; + attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) + if (attr->spec[0] && attr->value && attr->value[0]) + { + if (attr->value[0] != '/') + snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, + attr->value); + else + strlcpy(iccfile, attr->value, sizeof(iccfile)); + + if (access(iccfile, 0)) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "%s: ICC Profile \"%s\" does not exist.", p->name, + iccfile); + cupsdSetPrinterReasons(p, "+cups-missing-filter-warning"); + continue; + } + + num_profiles ++; + } + + /* + * Create a dictionary for the factory profiles... + */ + + profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!profiles) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for factory profiles."); + ppdClose(ppd); + return; + } + + /* + * If we have profiles, add them... + */ + + if (num_profiles > 0) + { + /* + * For CUPS PPDs, figure out the default profile selector values... + */ + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL && + attr->value && attr->value[0]) + { + snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); + q1_attr = ppdFindAttr(ppd, q_keyword, NULL); + } + else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL) + q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL); + + if (q1_attr && q1_attr->value && q1_attr->value[0]) + q1_choice = q1_attr->value; + else + q1_choice = ""; + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL && + attr->value && attr->value[0]) + { + snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); + q2_attr = ppdFindAttr(ppd, q_keyword, NULL); + } + else + q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL); + + if (q2_attr && q2_attr->value && q2_attr->value[0]) + q2_choice = q2_attr->value; + else + q2_choice = NULL; + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL && + attr->value && attr->value[0]) + { + snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); + q3_attr = ppdFindAttr(ppd, q_keyword, NULL); + } + else + q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL); + + if (q3_attr && q3_attr->value && q3_attr->value[0]) + q3_choice = q3_attr->value; + else + q3_choice = NULL; + + /* + * Loop through the profiles listed in the PPD... + */ + + languages = _ppdGetLanguages(ppd); + + for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); + attr; + attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) + if (attr->spec[0] && attr->value && attr->value[0]) + { + /* + * Add this profile... + */ + + if (attr->value[0] != '/') + snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, + attr->value); + else + strlcpy(iccfile, attr->value, sizeof(iccfile)); + + if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser, + cupsdLogFCMessage, p)) + continue; + + cupsArraySave(ppd->sorted_attrs); + + if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID", + attr->spec)) != NULL && + profileid_attr->value && isdigit(profileid_attr->value[0] & 255)) + profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10); + else + profile_id = _ppdHashName(attr->spec); + + cupsArrayRestore(ppd->sorted_attrs); + + profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!profile) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for color profile."); + CFRelease(profiles); + ppdClose(ppd); + return; + } + + apple_init_profile(ppd, languages, profile, profile_id, attr->spec, + attr->text[0] ? attr->text : attr->spec, iccfile); + + dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%u"), profile_id); + if (dict_key) + { + CFDictionarySetValue(profiles, dict_key, profile); + CFRelease(dict_key); + } + + CFRelease(profile); + + /* + * See if this is the default profile... + */ + + if (!default_profile_id && q1_choice && q2_choice && q3_choice) + { + snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice, + q3_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id && q1_choice && q2_choice) + { + snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id && q1_choice && q3_choice) + { + snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id && q1_choice) + { + snprintf(selector, sizeof(selector), "%s..", q1_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id && q2_choice && q3_choice) + { + snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id && q2_choice) + { + snprintf(selector, sizeof(selector), ".%s.", q2_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + + if (!default_profile_id && q3_choice) + { + snprintf(selector, sizeof(selector), "..%s", q3_choice); + if (!strcmp(selector, attr->spec)) + default_profile_id = profile_id; + } + } + + _ppdFreeLanguages(languages); + } + else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL) + { + /* + * Extract profiles from ColorModel option... + */ + + const char *profile_name; /* Name of generic profile */ + + + num_profiles = cm_option->num_choices; + + for (i = cm_option->num_choices, cm_choice = cm_option->choices; + i > 0; + i --, cm_choice ++) + { + if (!strcmp(cm_choice->choice, "Gray") || + !strcmp(cm_choice->choice, "Black")) + profile_name = "Gray"; + else if (!strcmp(cm_choice->choice, "RGB") || + !strcmp(cm_choice->choice, "CMY")) + profile_name = "RGB"; + else if (!strcmp(cm_choice->choice, "CMYK") || + !strcmp(cm_choice->choice, "KCMY")) + profile_name = "CMYK"; + else + profile_name = "DeviceN"; + + snprintf(selector, sizeof(selector), "%s..", profile_name); + profile_id = _ppdHashName(selector); + + profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!profile) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for color profile."); + CFRelease(profiles); + ppdClose(ppd); + return; + } + + apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice, + cm_choice->text, NULL); + + dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%u"), profile_id); + if (dict_key) + { + CFDictionarySetValue(profiles, dict_key, profile); + CFRelease(dict_key); + } + + CFRelease(profile); + + if (cm_choice->marked) + default_profile_id = profile_id; + } + } + else + { + /* + * Use the default colorspace... + */ + + attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL); + + num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2; + + /* + * Add the grayscale profile first. We always have a grayscale profile. + */ + + profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (!profile) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for color profile."); + CFRelease(profiles); + ppdClose(ppd); + return; + } + + profile_id = _ppdHashName("Gray.."); + apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL); + + dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), + profile_id); + if (dict_key) + { + CFDictionarySetValue(profiles, dict_key, profile); + CFRelease(dict_key); + } + + CFRelease(profile); + + /* + * Then add the RGB/CMYK/DeviceN color profile... + */ + + profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (!profile) + { + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to allocate memory for color profile."); + CFRelease(profiles); + ppdClose(ppd); + return; + } + + switch (ppd->colorspace) + { + default : + case PPD_CS_RGB : + case PPD_CS_CMY : + profile_id = _ppdHashName("RGB.."); + apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB", + NULL); + break; + + case PPD_CS_RGBK : + case PPD_CS_CMYK : + profile_id = _ppdHashName("CMYK.."); + apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK", + NULL); + break; + + case PPD_CS_GRAY : + if (attr) + break; + + case PPD_CS_N : + profile_id = _ppdHashName("DeviceN.."); + apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN", + "DeviceN", NULL); + break; + } + + if (CFDictionaryGetCount(profile) > 0) + { + dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, + CFSTR("%u"), profile_id); + if (dict_key) + { + CFDictionarySetValue(profiles, dict_key, profile); + CFRelease(dict_key); + } + } + + CFRelease(profile); + } + + if (num_profiles > 0) + { + /* + * Make sure we have a default profile ID... + */ + + if (!default_profile_id) + default_profile_id = profile_id; /* Last profile */ + + dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), + default_profile_id); + if (dict_key) + { + CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID, + dict_key); + CFRelease(dict_key); + } + + /* + * Get the device ID hash and pathelogical name dictionary. + */ + + cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"", + p->name); + + device_id = _ppdHashName(p->name); + device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + printer_name = CFStringCreateWithCString(kCFAllocatorDefault, + p->name, kCFStringEncodingUTF8); + + if (device_name && printer_name) + { + /* + * Register the device with ColorSync... + */ + + CFTypeRef deviceDictKeys[] = + { /* Device keys */ + kColorSyncDeviceDescriptions, + kColorSyncFactoryProfiles, + kColorSyncDeviceUserScope, + kColorSyncDeviceHostScope + }; + CFTypeRef deviceDictVals[] = + { /* Device values */ + device_name, + profiles, + kCFPreferencesAnyUser, + kCFPreferencesCurrentHost + }; + CFDictionaryRef deviceDict; /* Device dictionary */ + CFUUIDRef deviceUUID; /* Device UUID */ + + CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name); + + deviceDict = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)deviceDictKeys, + (const void **)deviceDictVals, + sizeof(deviceDictKeys) / + sizeof(deviceDictKeys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id); + + if (!deviceDict || !deviceUUID || + !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID, + deviceDict)) + error = 1001; + + if (deviceUUID) + CFRelease(deviceUUID); + + if (deviceDict) + CFRelease(deviceDict); + } + else + error = 1000; + + /* + * Clean up... + */ + + if (error != noErr) + cupsdLogMessage(CUPSD_LOG_ERROR, + "Unable to register ICC color profiles for \"%s\": %d", + p->name, (int)error); + + if (printer_name) + CFRelease(printer_name); + + if (device_name) + CFRelease(device_name); + } + + /* + * Free any memory we used... + */ + + CFRelease(profiles); + + ppdClose(ppd); +} + + +/* + * 'apple_unregister_profiles()' - Remove color profiles for the specified + * printer. + */ + +static void +apple_unregister_profiles( + cupsd_printer_t *p) /* I - Printer */ +{ + /* + * Make sure ColorSync is available... + */ + + if (ColorSyncUnregisterDevice != NULL) + { + CFUUIDRef deviceUUID; /* Device UUID */ + + deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name)); + if (deviceUUID) + { + ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID); + CFRelease(deviceUUID); + } + } +} + + +#elif defined(HAVE_DBUS) +/* + * 'colord_create_device()' - Create a device and register profiles. + */ + +static void +colord_create_device( + cupsd_printer_t *p, /* I - Printer */ + ppd_file_t *ppd, /* I - PPD file */ + cups_array_t *profiles, /* I - Profiles array */ + const char *colorspace, /* I - Device colorspace, e.g. 'rgb' */ + char **format, /* I - Device qualifier format */ + const char *relation, /* I - Profile relation, either 'soft' + or 'hard' */ + const char *scope) /* I - The scope of the device, e.g. + 'normal', 'temp' or 'disk' */ +{ + DBusMessage *message = NULL; /* D-Bus request */ + DBusMessage *reply = NULL; /* D-Bus reply */ + DBusMessageIter args; /* D-Bus method arguments */ + DBusMessageIter dict; /* D-Bus method arguments */ + DBusError error; /* D-Bus error */ + const char *device_path; /* Device object path */ + const char *profile_path; /* Profile path */ + char *default_profile_path = NULL; + /* Default profile path */ + char device_id[1024]; /* Device ID as understood by colord */ + char format_str[1024]; /* Qualifier format as a string */ + + + /* + * Create the device... + */ + + snprintf(device_id, sizeof(device_id), "cups-%s", p->name); + device_path = device_id; + + message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "CreateDevice"); + + dbus_message_iter_init_append(message, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_path); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope); + + snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1], + format[2]); + + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict); + colord_dict_add_strings(&dict, "Colorspace", colorspace); + colord_dict_add_strings(&dict, "Mode", COLORD_MODE_PHYSICAL); + if (ppd->manufacturer) + colord_dict_add_strings(&dict, "Vendor", ppd->manufacturer); + if (ppd->modelname) + colord_dict_add_strings(&dict, "Model", ppd->modelname); + if (p->sanitized_device_uri) + colord_dict_add_strings(&dict, "Serial", p->sanitized_device_uri); + colord_dict_add_strings(&dict, "Format", format_str); + colord_dict_add_strings(&dict, "Kind", COLORD_KIND_PRINTER); + dbus_message_iter_close_container(&args, &dict); + + /* + * Send the CreateDevice request synchronously... + */ + + dbus_error_init(&error); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateDevice(%s,%s)", device_id, + scope); + reply = dbus_connection_send_with_reply_and_block(colord_con, message, + COLORD_DBUS_TIMEOUT, + &error); + if (!reply) + { + cupsdLogMessage(CUPSD_LOG_WARN, "CreateDevice failed: %s:%s", error.name, + error.message); + dbus_error_free(&error); + goto out; + } + + /* + * Get reply data... + */ + + dbus_message_iter_init(reply, &args); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) + { + cupsdLogMessage(CUPSD_LOG_WARN, + "CreateDevice failed: Incorrect reply type."); + goto out; + } + + dbus_message_iter_get_basic(&args, &device_path); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Created device \"%s\".", device_path); + + /* + * Add profiles... + */ + + for (profile_path = cupsArrayFirst(profiles); + profile_path; + profile_path = cupsArrayNext(profiles)) + { + colord_device_add_profile(device_path, profile_path, relation); + } + +out: + + if (default_profile_path) + free(default_profile_path); + + if (message) + dbus_message_unref(message); + + if (reply) + dbus_message_unref(reply); +} + + +/* + * 'colord_create_profile()' - Create a color profile for a printer. + */ + +static void +colord_create_profile( + cups_array_t *profiles, /* I - Profiles array */ + const char *printer_name, /* I - Printer name */ + const char *qualifier, /* I - Profile qualifier */ + const char *colorspace, /* I - Profile colorspace */ + char **format, /* I - Profile qualifier format */ + const char *iccfile, /* I - ICC filename */ + const char *scope) /* I - The scope of the profile, e.g. + 'normal', 'temp' or 'disk' */ +{ + DBusMessage *message = NULL; /* D-Bus request */ + DBusMessage *reply = NULL; /* D-Bus reply */ + DBusMessageIter args; /* D-Bus method arguments */ + DBusMessageIter dict; /* D-Bus method arguments */ + DBusError error; /* D-Bus error */ + char *idstr; /* Profile ID string */ + size_t idstrlen; /* Profile ID allocated length */ + const char *profile_path; /* Device object path */ + char format_str[1024]; /* Qualifier format as a string */ + + + /* + * Create the profile... + */ + + message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "CreateProfile"); + + idstrlen = strlen(printer_name) + 1 + strlen(qualifier) + 1; + if ((idstr = malloc(idstrlen)) == NULL) + goto out; + snprintf(idstr, idstrlen, "%s-%s", printer_name, qualifier); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Using profile ID \"%s\".", idstr); + + dbus_message_iter_init_append(message, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &idstr); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &scope); + + snprintf(format_str, sizeof(format_str), "%s.%s.%s", format[0], format[1], + format[2]); + + dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{ss}", &dict); + colord_dict_add_strings(&dict, "Qualifier", qualifier); + colord_dict_add_strings(&dict, "Format", format_str); + colord_dict_add_strings(&dict, "Colorspace", colorspace); + if (iccfile) + colord_dict_add_strings(&dict, "Filename", iccfile); + dbus_message_iter_close_container(&args, &dict); + + /* + * Send the CreateProfile request synchronously... + */ + + dbus_error_init(&error); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling CreateProfile(%s,%s)", idstr, + scope); + reply = dbus_connection_send_with_reply_and_block(colord_con, message, + COLORD_DBUS_TIMEOUT, + &error); + if (!reply) + { + cupsdLogMessage(CUPSD_LOG_WARN, "CreateProfile failed: %s:%s", error.name, + error.message); + dbus_error_free(&error); + goto out; + } + + /* + * Get reply data... + */ + + dbus_message_iter_init(reply, &args); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) + { + cupsdLogMessage(CUPSD_LOG_WARN, + "CreateProfile failed: Incorrect reply type."); + goto out; + } + + dbus_message_iter_get_basic(&args, &profile_path); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Created profile \"%s\".", profile_path); + cupsArrayAdd(profiles, strdup(profile_path)); + +out: + + if (message) + dbus_message_unref(message); + + if (reply) + dbus_message_unref(reply); + + if (idstr) + free(idstr); +} + + +/* + * 'colord_delete_device()' - Delete a device + */ + +static void +colord_delete_device( + const char *device_id) /* I - Device ID string */ +{ + DBusMessage *message = NULL; /* D-Bus request */ + DBusMessage *reply = NULL; /* D-Bus reply */ + DBusMessageIter args; /* D-Bus method arguments */ + DBusError error; /* D-Bus error */ + char *device_path; /* Device object path */ + + + /* + * Find the device... + */ + + if ((device_path = colord_find_device(device_id)) == NULL) + goto out; + + /* + * Delete the device... + */ + + message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "DeleteDevice"); + + dbus_message_iter_init_append(message, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id); + + /* + * Send the DeleteDevice request synchronously... + */ + + dbus_error_init(&error); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling DeleteDevice(%s)", device_id); + reply = dbus_connection_send_with_reply_and_block(colord_con, message, + COLORD_DBUS_TIMEOUT, + &error); + if (!reply) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "DeleteDevice failed: %s:%s", error.name, + error.message); + dbus_error_free(&error); + goto out; + } + +out: + + if (device_path) + free(device_path); + + if (message) + dbus_message_unref(message); + + if (reply) + dbus_message_unref(reply); +} + + +/* + * 'colord_device_add_profile()' - Assign a profile to a device. + */ + +static void +colord_device_add_profile( + const char *device_path, /* I - Device object path */ + const char *profile_path, /* I - Profile object path */ + const char *relation) /* I - Device relation, either + 'soft' or 'hard' */ +{ + DBusMessage *message = NULL; /* D-Bus request */ + DBusMessage *reply = NULL; /* D-Bus reply */ + DBusMessageIter args; /* D-Bus method arguments */ + DBusError error; /* D-Bus error */ + + + message = COLORD_DBUS_MSG(device_path, "AddProfile"); + + dbus_message_iter_init_append(message, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &relation); + dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &profile_path); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling %s:AddProfile(%s) [%s]", + device_path, profile_path, relation); + + /* + * Send the AddProfile request synchronously... + */ + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(colord_con, message, + COLORD_DBUS_TIMEOUT, + &error); + if (!reply) + { + cupsdLogMessage(CUPSD_LOG_WARN, "AddProfile failed: %s:%s", error.name, + error.message); + dbus_error_free(&error); + goto out; + } + +out: + + if (message) + dbus_message_unref(message); + + if (reply) + dbus_message_unref(reply); +} + + +/* + * 'colord_dict_add_strings()' - Add two strings to a dictionary. + */ + +static void +colord_dict_add_strings( + DBusMessageIter *dict, /* I - Dictionary */ + const char *key, /* I - Key string */ + const char *value) /* I - Value string */ +{ + DBusMessageIter entry; /* Entry to add */ + + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &value); + dbus_message_iter_close_container(dict, &entry); +} + + +/* + * 'colord_find_device()' - Finds a device + */ + +static char * /* O - Device path or NULL */ +colord_find_device( + const char *device_id) /* I - Device ID string */ +{ + DBusMessage *message = NULL; /* D-Bus request */ + DBusMessage *reply = NULL; /* D-Bus reply */ + DBusMessageIter args; /* D-Bus method arguments */ + DBusError error; /* D-Bus error */ + const char *device_path_tmp; /* Device object path */ + char *device_path = NULL; /* Device object path */ + + + message = COLORD_DBUS_MSG(COLORD_DBUS_PATH, "FindDeviceById"); + + dbus_message_iter_init_append(message, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &device_id); + + /* + * Send the FindDeviceById request synchronously... + */ + + dbus_error_init(&error); + cupsdLogMessage(CUPSD_LOG_DEBUG, "Calling FindDeviceById(%s)", device_id); + reply = dbus_connection_send_with_reply_and_block(colord_con, message, + COLORD_DBUS_TIMEOUT, + &error); + if (!reply) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "FindDeviceById failed: %s:%s", + error.name, error.message); + dbus_error_free(&error); + goto out; + } + + /* + * Get reply data... + */ + + dbus_message_iter_init(reply, &args); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) + { + cupsdLogMessage(CUPSD_LOG_WARN, + "FindDeviceById failed: Incorrect reply type."); + goto out; + } + + dbus_message_iter_get_basic(&args, &device_path_tmp); + if (device_path_tmp) + device_path = strdup(device_path_tmp); + +out: + + if (message) + dbus_message_unref(message); + + if (reply) + dbus_message_unref(reply); + + return (device_path); +} + + +/* + * 'colord_get_qualifier_format()' - Get the qualifier format. + * + * Note: Returns a value of "ColorSpace.MediaType.Resolution" by default. + */ + +static void +colord_get_qualifier_format( + ppd_file_t *ppd, /* I - PPD file data */ + char *format[3]) /* I - Format tuple */ +{ + const char *tmp; /* Temporary string */ + ppd_attr_t *attr; /* Profile attributes */ + + + /* + * Get 1st section... + */ + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL) + tmp = attr->value; + else if (ppdFindAttr(ppd, "DefaultColorModel", NULL)) + tmp = "ColorModel"; + else if (ppdFindAttr(ppd, "DefaultColorSpace", NULL)) + tmp = "ColorSpace"; + else + tmp = ""; + + format[0] = strdup(tmp); + + /* + * Get 2nd section... + */ + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL) + tmp = attr->value; + else + tmp = "MediaType"; + + format[1] = strdup(tmp); + + /* + * Get 3rd section... + */ + + if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL) + tmp = attr->value; + else + tmp = "Resolution"; + + format[2] = strdup(tmp); +} + + +/* + * 'colord_register_printer()' - Register profiles for a printer. + */ + +static void +colord_register_printer( + cupsd_printer_t *p) /* I - printer */ +{ + char ppdfile[1024], /* PPD filename */ + iccfile[1024]; /* ICC filename */ + ppd_file_t *ppd; /* PPD file */ + cups_array_t *profiles; /* Profile paths array */ + ppd_attr_t *attr; /* Profile attributes */ + const char *device_colorspace; /* Device colorspace */ + char *format[3]; /* Qualifier format tuple */ + + + /* + * Ensure we have a D-Bus connection... + */ + + if (!colord_con) + return; + + /* + * Try opening the PPD file for this printer... + */ + + snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name); + if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL) + return; + + /* + * Find out the qualifier format + */ + + colord_get_qualifier_format(ppd, format); + + /* + * See if we have any embedded profiles... + */ + + profiles = cupsArrayNew3(NULL, NULL, NULL, 0, (cups_acopy_func_t)strdup, + (cups_afree_func_t)free); + for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); + attr; + attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) + if (attr->spec[0] && attr->value && attr->value[0]) + { + if (attr->value[0] != '/') + snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, + attr->value); + else + strlcpy(iccfile, attr->value, sizeof(iccfile)); + + if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser, + cupsdLogFCMessage, p)) + continue; + + colord_create_profile(profiles, p->name, attr->spec, COLORD_SPACE_UNKNOWN, + format, iccfile, COLORD_SCOPE_TEMP); + } + + /* + * Add the grayscale profile first. We always have a grayscale profile. + */ + + colord_create_profile(profiles, p->name, "Gray..", COLORD_SPACE_GRAY, + format, NULL, COLORD_SCOPE_TEMP); + + /* + * Then add the RGB/CMYK/DeviceN color profile... + */ + + device_colorspace = "unknown"; + switch (ppd->colorspace) + { + case PPD_CS_RGB : + case PPD_CS_CMY : + device_colorspace = COLORD_SPACE_RGB; + colord_create_profile(profiles, p->name, "RGB..", COLORD_SPACE_RGB, + format, NULL, COLORD_SCOPE_TEMP); + break; + + case PPD_CS_RGBK : + case PPD_CS_CMYK : + device_colorspace = COLORD_SPACE_CMYK; + colord_create_profile(profiles, p->name, "CMYK..", COLORD_SPACE_CMYK, + format, NULL, COLORD_SCOPE_TEMP); + break; + + case PPD_CS_GRAY : + device_colorspace = COLORD_SPACE_GRAY; + break; + + case PPD_CS_N : + colord_create_profile(profiles, p->name, "DeviceN..", + COLORD_SPACE_UNKNOWN, format, NULL, + COLORD_SCOPE_TEMP); + break; + } + + /* + * Register the device with colord. + */ + + cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\".", + p->name); + colord_create_device(p, ppd, profiles, device_colorspace, format, + COLORD_RELATION_SOFT, COLORD_SCOPE_TEMP); + + /* + * Free any memory we used... + */ + + cupsArrayDelete(profiles); + + free(format[0]); + free(format[1]); + free(format[2]); + + ppdClose(ppd); +} + + +/* + * 'colord_unregister_printer()' - Unregister profiles for a printer. + */ + +static void +colord_unregister_printer( + cupsd_printer_t *p) /* I - printer */ +{ + char device_id[1024]; /* Device ID as understood by colord */ + + + /* + * Ensure we have a D-Bus connection... + */ + + if (!colord_con) + return; + + /* + * Just delete the device itself, and leave the profiles registered + */ + + snprintf(device_id, sizeof(device_id), "cups-%s", p->name); + colord_delete_device(device_id); +} +#endif /* __APPLE__ */ + + +/* + * End of "$Id$". + */ diff --git a/scheduler/colorman.h b/scheduler/colorman.h new file mode 100644 index 000000000..e912a92a3 --- /dev/null +++ b/scheduler/colorman.h @@ -0,0 +1,28 @@ +/* + * "$Id$" + * + * Color management definitions for the CUPS scheduler. + * + * Copyright 2007-2012 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" + * which should have been included with this file. If this file is + * file is missing or damaged, see the license at "http://www.cups.org/". + */ + +/* + * Prototypes... + */ + +extern void cupsdRegisterColor(cupsd_printer_t *p); +extern void cupsdStartColor(void); +extern void cupsdStopColor(void); +extern void cupsdUnregisterColor(cupsd_printer_t *p); + + +/* + * End of "$Id$". + */ diff --git a/scheduler/conf.c b/scheduler/conf.c index 4d392452b..a7ec4151c 100644 --- a/scheduler/conf.c +++ b/scheduler/conf.c @@ -169,6 +169,7 @@ static const cupsd_var_t variables[] = { "ServerRoot", &ServerRoot, CUPSD_VARTYPE_PATHNAME }, { "SMBConfigFile", &SMBConfigFile, CUPSD_VARTYPE_STRING }, { "StateDir", &StateDir, CUPSD_VARTYPE_STRING }, + { "StrictConformance", &StrictConformance, CUPSD_VARTYPE_BOOLEAN }, #ifdef HAVE_AUTHORIZATION_H { "SystemGroupAuthKey", &SystemGroupAuthKey, CUPSD_VARTYPE_STRING }, #endif /* HAVE_AUTHORIZATION_H */ @@ -722,10 +723,11 @@ cupsdReadConfiguration(void) MaxLogSize = 1024 * 1024; MaxRequestSize = 0; MultipleOperationTimeout = DEFAULT_TIMEOUT; + NumSystemGroups = 0; ReloadTimeout = DEFAULT_KEEPALIVE; RootCertDuration = 300; + StrictConformance = FALSE; Timeout = DEFAULT_TIMEOUT; - NumSystemGroups = 0; WebInterface = CUPS_DEFAULT_WEBIF; BrowseLocalProtocols = parse_protocols(CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS); diff --git a/scheduler/conf.h b/scheduler/conf.h index 5edfde429..741855ae3 100644 --- a/scheduler/conf.h +++ b/scheduler/conf.h @@ -169,6 +169,8 @@ VAR int ClassifyOverride VALUE(0), /* Amount of automatic debug history */ FatalErrors VALUE(CUPSD_FATAL_CONFIG), /* Which errors are fatal? */ + StrictConformance VALUE(FALSE), + /* Require strict IPP conformance? */ LogFilePerm VALUE(0644); /* Permissions for log files */ VAR cupsd_loglevel_t LogLevel VALUE(CUPSD_LOG_WARN); diff --git a/scheduler/cups-driverd.cxx b/scheduler/cups-driverd.cxx index 3c23d6247..1a270b372 100644 --- a/scheduler/cups-driverd.cxx +++ b/scheduler/cups-driverd.cxx @@ -671,7 +671,7 @@ cat_tar(const char *name, /* I - PPD name */ for (total = 0; total < curinfo.st_size; total += bytes) { - if ((bytes = (curinfo.st_size - total)) > sizeof(buffer)) + if ((size_t)(bytes = (curinfo.st_size - total)) > sizeof(buffer)) bytes = sizeof(buffer); if ((bytes = cupsFileRead(fp, buffer, bytes)) < 0) @@ -1997,7 +1997,7 @@ load_ppd(const char *filename, /* I - Real filename */ cups_array_t *products, /* Product array */ *psversions, /* PSVersion array */ *cups_languages; /* cupsLanguages array */ - int new_ppd = !ppd; /* Is this a new PPD? */ + int new_ppd; /* Is this a new PPD? */ struct /* LanguageVersion translation table */ { const char *version, /* LanguageVersion string */ diff --git a/scheduler/cupsd.h b/scheduler/cupsd.h index aaf9b13c0..b15336b51 100644 --- a/scheduler/cupsd.h +++ b/scheduler/cupsd.h @@ -3,7 +3,7 @@ * * Main header file for the CUPS scheduler. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2007 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -116,6 +116,7 @@ extern const char *cups_hstrerror(int); #include "printers.h" #include "classes.h" #include "job.h" +#include "colorman.h" #include "conf.h" #include "banners.h" #include "dirsvc.h" diff --git a/scheduler/dirsvc.c b/scheduler/dirsvc.c index e57b3f13d..af9099809 100644 --- a/scheduler/dirsvc.c +++ b/scheduler/dirsvc.c @@ -52,12 +52,8 @@ #if defined(HAVE_DNSSD) && defined(__APPLE__) # include -# ifdef HAVE_COREFOUNDATION -# include -# endif /* HAVE_COREFOUNDATION */ -# ifdef HAVE_SYSTEMCONFIGURATION -# include -# endif /* HAVE_SYSTEMCONFIGURATION */ +# include +# include #endif /* HAVE_DNSSD && __APPLE__ */ @@ -77,10 +73,10 @@ static void update_smb(int onoff); #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) -# ifdef HAVE_COREFOUNDATION +# ifdef __APPLE__ static void dnssdAddAlias(const void *key, const void *value, void *context); -# endif /* HAVE_COREFOUNDATION */ +# endif /* __APPLE__ */ static cupsd_txt_t dnssdBuildTxtRecord(cupsd_printer_t *p, int for_lpd); static void dnssdDeregisterInstance(cupsd_srv_t *srv); static void dnssdDeregisterPrinter(cupsd_printer_t *p, @@ -140,8 +136,10 @@ cupsdDeregisterPrinter( * Announce the deletion... */ +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (removeit && (BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster) dnssdDeregisterPrinter(p, 1); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ } @@ -160,8 +158,10 @@ cupsdRegisterPrinter(cupsd_printer_t *p)/* I - Printer */ (p->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_SCANNER))) return; +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if ((BrowseLocalProtocols & BROWSE_DNSSD) && DNSSDMaster) dnssdRegisterPrinter(p); +#endif /* HAVE_DNSSD || HAVE_AVAHI */ } @@ -342,13 +342,13 @@ void cupsdUpdateDNSSDName(void) { char webif[1024]; /* Web interface share name */ -# ifdef HAVE_SYSTEMCONFIGURATION +# ifdef __APPLE__ SCDynamicStoreRef sc; /* Context for dynamic store */ CFDictionaryRef btmm; /* Back-to-My-Mac domains */ CFStringEncoding nameEncoding; /* Encoding of computer name */ CFStringRef nameRef; /* Host name CFString */ char nameBuffer[1024]; /* C-string buffer */ -# endif /* HAVE_SYSTEMCONFIGURATION */ +# endif /* __APPLE__ */ /* @@ -363,7 +363,7 @@ cupsdUpdateDNSSDName(void) * Get the computer name as a c-string... */ -# ifdef HAVE_SYSTEMCONFIGURATION +# ifdef __APPLE__ sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), NULL, NULL); if (sc) @@ -454,7 +454,7 @@ cupsdUpdateDNSSDName(void) CFRelease(sc); } else -# endif /* HAVE_SYSTEMCONFIGURATION */ +# endif /* __APPLE__ */ # ifdef HAVE_AVAHI { cupsdSetString(&DNSSDComputerName, avahi_client_get_host_name(DNSSDClient)); @@ -486,7 +486,7 @@ cupsdUpdateDNSSDName(void) } -# ifdef HAVE_COREFOUNDATION +# ifdef __APPLE__ /* * 'dnssdAddAlias()' - Add a DNS-SD alias name. */ @@ -524,7 +524,7 @@ dnssdAddAlias(const void *key, /* I - Key */ cupsdLogMessage(CUPSD_LOG_ERROR, "Bad Back to My Mac domain in dynamic store!"); } -# endif /* HAVE_COREFOUNDATION */ +# endif /* __APPLE__ */ /* diff --git a/scheduler/env.c b/scheduler/env.c index ec12d155f..5fbe52c55 100644 --- a/scheduler/env.c +++ b/scheduler/env.c @@ -62,7 +62,7 @@ cupsdInitEnv(void) #if defined(__APPLE__) /* - * Add special voodoo magic for MacOS X - this allows MacOS X + * Add special voodoo magic for MacOS X - this allows MacOS X * programs to access their bundle resources properly... * * This string is replaced in cupsdStartProcess()... @@ -227,6 +227,8 @@ cupsdUpdateEnv(void) set_if_undefined("TZ", NULL); set_if_undefined("USER", "root"); set_if_undefined("VG_ARGS", NULL); + + cupsdSetEnvf("CUPS_MAX_MESSAGE", "%d", CUPSD_SB_BUFFER_SIZE - 1); } diff --git a/scheduler/ipp.c b/scheduler/ipp.c index ce0ccc9f0..fb0e47dc1 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -30,10 +30,6 @@ * based upon the printer state... * add_queued_job_count() - Add the "queued-job-count" attribute for the * specified printer or class. - * apple_init_profile() - Initialize a color profile. - * apple_register_profiles() - Register color profiles for a printer. - * apple_unregister_profiles() - Remove color profiles for the specified - * printer. * apply_printer_defaults() - Apply printer default options to a job. * authenticate_job() - Set job authentication info. * cancel_all_jobs() - Cancel all or selected print jobs. @@ -111,9 +107,9 @@ #include #ifdef __APPLE__ -# include +/*# include extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id); -# include +# include */ # ifdef HAVE_MEMBERSHIP_H # include # endif /* HAVE_MEMBERSHIP_H */ @@ -143,14 +139,6 @@ static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri); static void add_printer_state_reasons(cupsd_client_t *con, cupsd_printer_t *p); static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p); -#ifdef __APPLE__ -static void apple_init_profile(ppd_file_t *ppd, cups_array_t *languages, - CFMutableDictionaryRef profile, - unsigned id, const char *name, - const char *text, const char *iccfile); -static void apple_register_profiles(cupsd_printer_t *p); -static void apple_unregister_profiles(cupsd_printer_t *p); -#endif /* __APPLE__ */ static void apply_printer_defaults(cupsd_printer_t *printer, cupsd_job_t *job); static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri); @@ -2738,17 +2726,11 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ cupsdSetPrinterReasons(printer, "none"); -#ifdef __APPLE__ /* * (Re)register color profiles... */ - if (!RunUser) - { - apple_unregister_profiles(printer); - apple_register_profiles(printer); - } -#endif /* __APPLE__ */ + cupsdRegisterColor(printer); } /* @@ -2882,711 +2864,6 @@ add_queued_job_count( } -#ifdef __APPLE__ -/* - * 'apple_init_profile()' - Initialize a color profile. - */ - -static void -apple_init_profile( - ppd_file_t *ppd, /* I - PPD file */ - cups_array_t *languages, /* I - Languages in the PPD file */ - CFMutableDictionaryRef profile, /* I - Profile dictionary */ - unsigned id, /* I - Profile ID */ - const char *name, /* I - Profile name */ - const char *text, /* I - Profile UI text */ - const char *iccfile) /* I - ICC filename */ -{ - CFURLRef url; /* URL for profile filename */ - CFMutableDictionaryRef dict; /* Dictionary for name */ - char *language; /* Current language */ - ppd_attr_t *attr; /* Profile attribute */ - CFStringRef cflang, /* Language string */ - cftext; /* Localized text */ - - - (void)id; - - /* - * Build the profile name dictionary... - */ - - dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (!dict) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".", - iccfile); - return; - } - - cftext = CFStringCreateWithCString(kCFAllocatorDefault, text, - kCFStringEncodingUTF8); - - if (cftext) - { - CFDictionarySetValue(dict, CFSTR("en_US"), cftext); - CFRelease(cftext); - } - - if (languages) - { - /* - * Find localized names for the color profiles... - */ - - cupsArraySave(ppd->sorted_attrs); - - for (language = (char *)cupsArrayFirst(languages); - language; - language = (char *)cupsArrayNext(languages)) - { - if (iccfile) - { - if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name, - language)) == NULL) - attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language); - } - else - attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language); - - if (attr && attr->text[0]) - { - cflang = CFStringCreateWithCString(kCFAllocatorDefault, language, - kCFStringEncodingUTF8); - cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text, - kCFStringEncodingUTF8); - - if (cflang && cftext) - CFDictionarySetValue(dict, cflang, cftext); - - if (cflang) - CFRelease(cflang); - - if (cftext) - CFRelease(cftext); - } - } - - cupsArrayRestore(ppd->sorted_attrs); - } - - /* - * Fill in the profile data... - */ - - if (iccfile) - { - url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, - (const UInt8 *)iccfile, - strlen(iccfile), false); - - if (url) - { - CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url); - CFRelease(url); - } - } - - CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict); - CFRelease(dict); -} - - -/* - * 'apple_register_profiles()' - Register color profiles for a printer. - */ - -static void -apple_register_profiles( - cupsd_printer_t *p) /* I - Printer */ -{ - int i; /* Looping var */ - char ppdfile[1024], /* PPD filename */ - iccfile[1024], /* ICC filename */ - selector[PPD_MAX_NAME]; - /* Profile selection string */ - ppd_file_t *ppd; /* PPD file */ - ppd_attr_t *attr, /* Profile attributes */ - *profileid_attr,/* cupsProfileID attribute */ - *q1_attr, /* ColorModel (or other) qualifier */ - *q2_attr, /* MediaType (or other) qualifier */ - *q3_attr; /* Resolution (or other) qualifier */ - char q_keyword[PPD_MAX_NAME]; - /* Qualifier keyword */ - const char *q1_choice, /* ColorModel (or other) choice */ - *q2_choice, /* MediaType (or other) choice */ - *q3_choice; /* Resolution (or other) choice */ - const char *profile_key; /* Profile keyword */ - ppd_option_t *cm_option; /* Color model option */ - ppd_choice_t *cm_choice; /* Color model choice */ - int num_profiles; /* Number of profiles */ - OSStatus error = 0; /* Last error */ - unsigned device_id, /* Printer device ID */ - profile_id = 0, /* Profile ID */ - default_profile_id = 0; - /* Default profile ID */ - CFMutableDictionaryRef device_name; /* Printer device name dictionary */ - CFStringRef printer_name; /* Printer name string */ - cups_array_t *languages; /* Languages array */ - CFMutableDictionaryRef profiles, /* Dictionary of profiles */ - profile; /* Current profile info dictionary */ - CFStringRef dict_key; /* Key in factory profile dictionary */ - - - /* - * Make sure ColorSync is available... - */ - - if (ColorSyncRegisterDevice == NULL) - return; - - /* - * Try opening the PPD file for this printer... - */ - - snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name); - if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL) - return; - - /* - * See if we have any profiles... - */ - - if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL) - profile_key = "APTiogaProfile"; - else - { - attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); - profile_key = "cupsICCProfile"; - } - - for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL)) - if (attr->spec[0] && attr->value && attr->value[0]) - { - if (attr->value[0] != '/') - snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, - attr->value); - else - strlcpy(iccfile, attr->value, sizeof(iccfile)); - - if (access(iccfile, 0)) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "%s: ICC Profile \"%s\" does not exist.", p->name, - iccfile); - cupsdSetPrinterReasons(p, "+cups-missing-filter-warning"); - continue; - } - - num_profiles ++; - } - - /* - * Create a dictionary for the factory profiles... - */ - - profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (!profiles) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to allocate memory for factory profiles."); - ppdClose(ppd); - return; - } - - /* - * If we have profiles, add them... - */ - - if (num_profiles > 0) - { - if (profile_key[0] == 'A') - { - /* - * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile - * attribute... - */ - - if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL && - attr->value) - default_profile_id = atoi(attr->value); - - q1_choice = q2_choice = q3_choice = NULL; - } - else - { - /* - * For CUPS PPDs, figure out the default profile selector values... - */ - - if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL && - attr->value && attr->value[0]) - { - snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); - q1_attr = ppdFindAttr(ppd, q_keyword, NULL); - } - else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL) - q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL); - - if (q1_attr && q1_attr->value && q1_attr->value[0]) - q1_choice = q1_attr->value; - else - q1_choice = ""; - - if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL && - attr->value && attr->value[0]) - { - snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); - q2_attr = ppdFindAttr(ppd, q_keyword, NULL); - } - else - q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL); - - if (q2_attr && q2_attr->value && q2_attr->value[0]) - q2_choice = q2_attr->value; - else - q2_choice = NULL; - - if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL && - attr->value && attr->value[0]) - { - snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value); - q3_attr = ppdFindAttr(ppd, q_keyword, NULL); - } - else - q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL); - - if (q3_attr && q3_attr->value && q3_attr->value[0]) - q3_choice = q3_attr->value; - else - q3_choice = NULL; - } - - /* - * Loop through the profiles listed in the PPD... - */ - - languages = _ppdGetLanguages(ppd); - - for (attr = ppdFindAttr(ppd, profile_key, NULL); - attr; - attr = ppdFindNextAttr(ppd, profile_key, NULL)) - if (attr->spec[0] && attr->value && attr->value[0]) - { - /* - * Add this profile... - */ - - if (attr->value[0] != '/') - snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir, - attr->value); - else - strlcpy(iccfile, attr->value, sizeof(iccfile)); - - if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser, - cupsdLogFCMessage, p)) - continue; - - if (profile_key[0] == 'c') - { - cupsArraySave(ppd->sorted_attrs); - - if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID", - attr->spec)) != NULL && - profileid_attr->value && isdigit(profileid_attr->value[0] & 255)) - profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10); - else - profile_id = _ppdHashName(attr->spec); - - cupsArrayRestore(ppd->sorted_attrs); - } - else - profile_id = atoi(attr->spec); - - profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (!profile) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to allocate memory for color profile."); - CFRelease(profiles); - ppdClose(ppd); - return; - } - - apple_init_profile(ppd, languages, profile, profile_id, attr->spec, - attr->text[0] ? attr->text : attr->spec, iccfile); - - dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("%u"), profile_id); - if (dict_key) - { - CFDictionarySetValue(profiles, dict_key, profile); - CFRelease(dict_key); - } - - CFRelease(profile); - - /* - * See if this is the default profile... - */ - - if (!default_profile_id && q1_choice && q2_choice && q3_choice) - { - snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice, - q3_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - - if (!default_profile_id && q1_choice && q2_choice) - { - snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - - if (!default_profile_id && q1_choice && q3_choice) - { - snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - - if (!default_profile_id && q1_choice) - { - snprintf(selector, sizeof(selector), "%s..", q1_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - - if (!default_profile_id && q2_choice && q3_choice) - { - snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - - if (!default_profile_id && q2_choice) - { - snprintf(selector, sizeof(selector), ".%s.", q2_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - - if (!default_profile_id && q3_choice) - { - snprintf(selector, sizeof(selector), "..%s", q3_choice); - if (!strcmp(selector, attr->spec)) - default_profile_id = profile_id; - } - } - - _ppdFreeLanguages(languages); - } - else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL) - { - /* - * Extract profiles from ColorModel option... - */ - - const char *profile_name; /* Name of generic profile */ - - - num_profiles = cm_option->num_choices; - - for (i = cm_option->num_choices, cm_choice = cm_option->choices; - i > 0; - i --, cm_choice ++) - { - if (!strcmp(cm_choice->choice, "Gray") || - !strcmp(cm_choice->choice, "Black")) - profile_name = "Gray"; - else if (!strcmp(cm_choice->choice, "RGB") || - !strcmp(cm_choice->choice, "CMY")) - profile_name = "RGB"; - else if (!strcmp(cm_choice->choice, "CMYK") || - !strcmp(cm_choice->choice, "KCMY")) - profile_name = "CMYK"; - else - profile_name = "DeviceN"; - - snprintf(selector, sizeof(selector), "%s..", profile_name); - profile_id = _ppdHashName(selector); - - profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (!profile) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to allocate memory for color profile."); - CFRelease(profiles); - ppdClose(ppd); - return; - } - - apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice, - cm_choice->text, NULL); - - dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("%u"), profile_id); - if (dict_key) - { - CFDictionarySetValue(profiles, dict_key, profile); - CFRelease(dict_key); - } - - CFRelease(profile); - - if (cm_choice->marked) - default_profile_id = profile_id; - } - } - else - { - /* - * Use the default colorspace... - */ - - attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL); - - num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2; - - /* - * Add the grayscale profile first. We always have a grayscale profile. - */ - - profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (!profile) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to allocate memory for color profile."); - CFRelease(profiles); - ppdClose(ppd); - return; - } - - profile_id = _ppdHashName("Gray.."); - apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL); - - dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), - profile_id); - if (dict_key) - { - CFDictionarySetValue(profiles, dict_key, profile); - CFRelease(dict_key); - } - - CFRelease(profile); - - /* - * Then add the RGB/CMYK/DeviceN color profile... - */ - - profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (!profile) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to allocate memory for color profile."); - CFRelease(profiles); - ppdClose(ppd); - return; - } - - switch (ppd->colorspace) - { - default : - case PPD_CS_RGB : - case PPD_CS_CMY : - profile_id = _ppdHashName("RGB.."); - apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB", - NULL); - break; - - case PPD_CS_RGBK : - case PPD_CS_CMYK : - profile_id = _ppdHashName("CMYK.."); - apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK", - NULL); - break; - - case PPD_CS_GRAY : - if (attr) - break; - - case PPD_CS_N : - profile_id = _ppdHashName("DeviceN.."); - apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN", - "DeviceN", NULL); - break; - } - - if (CFDictionaryGetCount(profile) > 0) - { - dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, - CFSTR("%u"), profile_id); - if (dict_key) - { - CFDictionarySetValue(profiles, dict_key, profile); - CFRelease(dict_key); - } - } - - CFRelease(profile); - } - - if (num_profiles > 0) - { - /* - * Make sure we have a default profile ID... - */ - - if (!default_profile_id) - default_profile_id = profile_id; /* Last profile */ - - dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), - default_profile_id); - if (dict_key) - { - CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID, - dict_key); - CFRelease(dict_key); - } - - /* - * Get the device ID hash and pathelogical name dictionary. - */ - - cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"", - p->name); - - device_id = _ppdHashName(p->name); - device_name = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - printer_name = CFStringCreateWithCString(kCFAllocatorDefault, - p->name, kCFStringEncodingUTF8); - - if (device_name && printer_name) - { - /* - * Register the device with ColorSync... - */ - - CFTypeRef deviceDictKeys[] = - { /* Device keys */ - kColorSyncDeviceDescriptions, - kColorSyncFactoryProfiles, - kColorSyncDeviceUserScope, - kColorSyncDeviceHostScope - }; - CFTypeRef deviceDictVals[] = - { /* Device values */ - device_name, - profiles, - kCFPreferencesAnyUser, - kCFPreferencesCurrentHost - }; - CFDictionaryRef deviceDict; /* Device dictionary */ - CFUUIDRef deviceUUID; /* Device UUID */ - - CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name); - - deviceDict = CFDictionaryCreate(kCFAllocatorDefault, - (const void **)deviceDictKeys, - (const void **)deviceDictVals, - sizeof(deviceDictKeys) / - sizeof(deviceDictKeys[0]), - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id); - - if (!deviceDict || !deviceUUID || - !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID, - deviceDict)) - error = 1001; - - if (deviceUUID) - CFRelease(deviceUUID); - - if (deviceDict) - CFRelease(deviceDict); - } - else - error = 1000; - - /* - * Clean up... - */ - - if (error != noErr) - cupsdLogMessage(CUPSD_LOG_ERROR, - "Unable to register ICC color profiles for \"%s\": %d", - p->name, (int)error); - - if (printer_name) - CFRelease(printer_name); - - if (device_name) - CFRelease(device_name); - } - - /* - * Free any memory we used... - */ - - CFRelease(profiles); - - ppdClose(ppd); -} - - -/* - * 'apple_unregister_profiles()' - Remove color profiles for the specified - * printer. - */ - -static void -apple_unregister_profiles( - cupsd_printer_t *p) /* I - Printer */ -{ - /* - * Make sure ColorSync is available... - */ - - if (ColorSyncUnregisterDevice != NULL) - { - /* - * Because we may have registered the printer profiles using a prior device - * ID-based UUID, remove both the old style UUID and current UUID for the - * printer. - */ - - CFUUIDRef deviceUUID; /* Device UUID */ - - deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name)); - if (deviceUUID) - { - ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID); - CFRelease(deviceUUID); - } - } -} -#endif /* __APPLE__ */ - - /* * 'apply_printer_defaults()' - Apply printer default options to a job. */ @@ -5795,8 +5072,16 @@ static void create_job(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - Printer URI */ { + int i; /* Looping var */ cupsd_printer_t *printer; /* Printer */ cupsd_job_t *job; /* New job */ + static const char * const forbidden_attrs[] = + { /* List of forbidden attributes */ + "compression", + "document-format", + "document-name", + "document-natural-language" + }; cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con, @@ -5817,6 +5102,29 @@ create_job(cupsd_client_t *con, /* I - Client connection */ return; } + /* + * Check for invalid Create-Job attributes and log a warning or error depending + * on whether cupsd is running in "strict conformance" mode... + */ + + for (i = 0; + i < (int)(sizeof(forbidden_attrs) / sizeof(forbidden_attrs[0])); + i ++) + if (ippFindAttribute(con->request, forbidden_attrs[i], IPP_TAG_ZERO)) + { + if (StrictConformance) + { + send_ipp_status(con, IPP_BAD_REQUEST, + _("The '%s' operation attribute cannot be supplied in a " + "Create-Job request."), forbidden_attrs[i]); + return; + } + + cupsdLogMessage(CUPSD_LOG_WARN, + "Unexpected '%s' operation attribute in a Create-Job " + "request.", forbidden_attrs[i]); + } + /* * Create the job object... */ @@ -6539,13 +5847,11 @@ delete_printer(cupsd_client_t *con, /* I - Client connection */ snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name); unlink(filename); -#ifdef __APPLE__ /* * Unregister color profiles... */ - apple_unregister_profiles(printer); -#endif /* __APPLE__ */ + cupsdUnregisterColor(printer); if (dtype & CUPS_PRINTER_CLASS) { diff --git a/scheduler/job.c b/scheduler/job.c index 6f834e319..9390e472e 100644 --- a/scheduler/job.c +++ b/scheduler/job.c @@ -287,16 +287,25 @@ cupsdCheckJobs(void) time_t curtime; /* Current time */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d", - cupsArrayCount(ActiveJobs), Sleeping, NeedReload); - curtime = time(NULL); + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d, " + "curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping, + NeedReload, (long)curtime); + for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) { + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdCheckJobs: Job %d - dest=\"%s\", printer=%p, " + "state=%d, cancel_time=%ld, hold_until=%ld, kill_time=%ld, " + "pending_cost=%d, pending_timeout=%ld", job->id, job->dest, + job->printer, job->state_value, (long)job->cancel_time, + (long)job->hold_until, (long)job->kill_time, + job->pending_cost, (long)job->pending_timeout); + /* * Kill jobs if they are unresponsive... */ @@ -339,7 +348,6 @@ cupsdCheckJobs(void) cupsd_client_t *con; /* Current client connection */ - for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; con = (cupsd_client_t *)cupsArrayNext(Clients)) @@ -431,7 +439,7 @@ cupsdCheckJobs(void) cupsdMarkDirty(CUPSD_DIRTY_JOBS); } - if (printer->state == IPP_PRINTER_IDLE) + if (!printer->job && printer->state == IPP_PRINTER_IDLE) { /* * Start the job... @@ -3354,6 +3362,21 @@ finalize_job(cupsd_job_t *job, /* I - Job */ cupsArrayRemove(PrintingJobs, job); + /* + * Apply any PPD updates... + */ + + if (job->num_keywords) + { + if (cupsdUpdatePrinterPPD(job->printer, job->num_keywords, job->keywords)) + cupsdSetPrinterAttrs(job->printer); + + cupsFreeOptions(job->num_keywords, job->keywords); + + job->num_keywords = 0; + job->keywords = NULL; + } + /* * Clear the printer <-> job association... */ @@ -4819,18 +4842,10 @@ update_job(cupsd_job_t *job) /* I - Job to check */ * Set attribute(s)... */ - int num_keywords; /* Number of keywords */ - cups_option_t *keywords; /* Keywords */ - - cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message); - num_keywords = cupsParseOptions(message, 0, &keywords); - - if (cupsdUpdatePrinterPPD(job->printer, num_keywords, keywords)) - cupsdSetPrinterAttrs(job->printer); - - cupsFreeOptions(num_keywords, keywords); + job->num_keywords = cupsParseOptions(message, job->num_keywords, + &job->keywords); } else { diff --git a/scheduler/job.h b/scheduler/job.h index e0b3a763e..e0250afee 100644 --- a/scheduler/job.h +++ b/scheduler/job.h @@ -80,6 +80,8 @@ struct cupsd_job_s /**** Job request ****/ void *profile; /* Security profile */ cups_array_t *history; /* Debug log history */ int progress; /* Printing progress */ + int num_keywords; /* Number of PPD keywords */ + cups_option_t *keywords; /* PPD keywords */ }; typedef struct cupsd_joblog_s /**** Job log message ****/ diff --git a/scheduler/printers.c b/scheduler/printers.c index 011934232..434c732ff 100644 --- a/scheduler/printers.c +++ b/scheduler/printers.c @@ -4005,6 +4005,41 @@ load_ppd(cupsd_printer_t *p) /* I - Printer */ } } + /* + * 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 */ @@ -4797,7 +4832,8 @@ load_ppd(cupsd_printer_t *p) /* I - Printer */ else if (!strncmp(p->device_uri, "ipp://", 6) && (strstr(p->device_uri, "/printers/") != NULL || strstr(p->device_uri, "/classes/") != NULL || - (strstr(p->device_uri, "._ipp.") != NULL && + ((strstr(p->device_uri, "._ipp.") != NULL || + strstr(p->device_uri, "._ipps.") != NULL) && !strcmp(p->device_uri + strlen(p->device_uri) - 5, "/cups")))) { @@ -4918,6 +4954,8 @@ log_ipp_conformance( 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/scheduler/printers.h b/scheduler/printers.h index 833d2332b..2242ddd11 100644 --- a/scheduler/printers.h +++ b/scheduler/printers.h @@ -172,7 +172,8 @@ extern int cupsdSetPrinterReasons(cupsd_printer_t *p, const char *s); extern void cupsdSetPrinterState(cupsd_printer_t *p, ipp_pstate_t s, int update); -#define cupsdStartPrinter(p,u) cupsdSetPrinterState((p), IPP_PRINTER_IDLE, (u)) +#define cupsdStartPrinter(p,u) cupsdSetPrinterState((p), \ + IPP_PRINTER_IDLE, (u)) extern void cupsdStopPrinter(cupsd_printer_t *p, int update); extern int cupsdUpdatePrinterPPD(cupsd_printer_t *p, int num_keywords, diff --git a/scheduler/server.c b/scheduler/server.c index 5e200eb71..a5a31c59b 100644 --- a/scheduler/server.c +++ b/scheduler/server.c @@ -3,7 +3,7 @@ * * Server start/stop routines for the CUPS scheduler. * - * Copyright 2007-2011 by Apple Inc. + * Copyright 2007-2012 by Apple Inc. * Copyright 1997-2006 by Easy Software Products, all rights reserved. * * These coded instructions, statements, and computer programs are the @@ -44,6 +44,12 @@ static int started = 0; /* Did we start the server already? */ void cupsdStartServer(void) { + /* + * Start color management (as needed)... + */ + + cupsdStartColor(); + /* * Create the default security profile... */ @@ -94,7 +100,13 @@ cupsdStopServer(void) return; /* - * Close all network clients and stop all jobs... + * Stop color management (as needed)... + */ + + cupsdStopColor(); + + /* + * Close all network clients... */ cupsdCloseAllClients(); diff --git a/scheduler/sysman.c b/scheduler/sysman.c index 6638db5ea..945149f72 100644 --- a/scheduler/sysman.c +++ b/scheduler/sysman.c @@ -974,11 +974,13 @@ sysUpdate(void) p = (cupsd_printer_t *)cupsArrayNext(Printers)) cupsdDeregisterPrinter(p, 1); +# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) /* * Update the computer name and BTMM domain list... */ cupsdUpdateDNSSDName(); +# endif /* HAVE_DNSSD || HAVE_AVAHI */ /* * Now re-register them... diff --git a/scheduler/tls-darwin.c b/scheduler/tls-darwin.c index 6d53d63a5..1f138c7a1 100644 --- a/scheduler/tls-darwin.c +++ b/scheduler/tls-darwin.c @@ -173,7 +173,9 @@ copy_cdsa_certificate( /* Server name */ CFMutableDictionaryRef query = NULL; /* Query qualifiers */ CFArrayRef list = NULL; /* Keychain list */ +# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) char localname[1024];/* Local hostname */ +# endif /* HAVE_DNSSD || HAVE_AVAHI */ # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY) SecPolicyRef policy = NULL; /* Policy ref */ SecPolicySearchRef policy_search = NULL; @@ -232,6 +234,7 @@ copy_cdsa_certificate( err = SecItemCopyMatching(query, (CFTypeRef *)&identity); +# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (err && DNSSDHostName) { /* @@ -265,6 +268,7 @@ copy_cdsa_certificate( err = SecItemCopyMatching(query, (CFTypeRef *)&identity); } +# endif /* HAVE_DNSSD || HAVE_AVAHI */ if (err) { @@ -320,6 +324,7 @@ copy_cdsa_certificate( err = SecIdentitySearchCopyNext(search, &identity); +# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (err && DNSSDHostName) { /* @@ -357,6 +362,7 @@ copy_cdsa_certificate( err = SecIdentitySearchCopyNext(search, &identity); } +# endif /* HAVE_DNSSD || HAVE_AVAHI */ if (err) { @@ -439,18 +445,22 @@ make_certificate(cupsd_client_t *con) /* I - Client connection */ *envp[MAX_ENV + 1], /* Environment variables */ keychain[1024], /* Keychain argument */ infofile[1024], /* Type-in information for cert */ +# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) localname[1024], /* Local hostname */ +# endif /* HAVE_DNSSD || HAVE_AVAHI */ *servername; /* Name of server in cert */ cups_file_t *fp; /* Seed/info file */ int infofd; /* Info file descriptor */ +# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName) { snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName); servername = localname; } else +# endif /* HAVE_DNSSD || HAVE_AVAHI */ servername = con->servername; /* diff --git a/systemv/lp.c b/systemv/lp.c index d100a3056..03a25ec16 100644 --- a/systemv/lp.c +++ b/systemv/lp.c @@ -46,7 +46,7 @@ main(int argc, /* I - Number of command-line arguments */ int i, j; /* Looping vars */ int job_id; /* Job ID */ char *printer, /* Printer name */ - *instance, /* Instance name */ + *instance, /* Instance name */ *val, /* Option value */ *title; /* Job title */ int priority; /* Job priority (1-100) */ @@ -121,7 +121,7 @@ main(int argc, /* I - Number of command-line arguments */ cupsSetUser(argv[i]); } break; - + case 'c' : /* Copy to spool dir (always enabled) */ break; @@ -629,8 +629,9 @@ main(int argc, /* I - Number of command-line arguments */ if (cupsFinishDocument(CUPS_HTTP_DEFAULT, printer) != IPP_OK) { + _cupsLangPrintf(stderr, "%s: %s", argv[0], cupsLastErrorString()); cupsCancelJob2(CUPS_HTTP_DEFAULT, printer, job_id, 0); - job_id = 0; + return (1); } } diff --git a/templates/jobs.tmpl b/templates/jobs.tmpl index 142fbb243..541e786a6 100644 --- a/templates/jobs.tmpl +++ b/templates/jobs.tmpl @@ -6,7 +6,7 @@
      {[job_id] - + diff --git a/test/5.1-lpadmin.sh b/test/5.1-lpadmin.sh index 6dfd6f50e..e4a8ed4ac 100644 --- a/test/5.1-lpadmin.sh +++ b/test/5.1-lpadmin.sh @@ -4,7 +4,7 @@ # # Test the lpadmin command. # -# Copyright 2007-2009 by Apple Inc. +# Copyright 2007-2012 by Apple Inc. # Copyright 1997-2005 by Easy Software Products, all rights reserved. # # These coded instructions, statements, and computer programs are the @@ -50,6 +50,18 @@ else fi echo "" +echo "Add Shared Printer Test" +echo "" +echo " lpadmin -p Test3 -E -v ipp://localhost:8631/printers/Test2 -m raw" +$VALGRIND ../systemv/lpadmin -p Test3 -E -v ipp://localhost:8631/printers/Test2 -m raw 2>&1 +if test $? != 0; then + echo " FAILED" + exit 1 +else + echo " PASSED" +fi +echo "" + # # End of "$Id: 5.1-lpadmin.sh 7494 2008-04-25 18:36:46Z mike $". # diff --git a/test/5.5-lp.sh b/test/5.5-lp.sh index f74e79ba9..aa326a8fc 100644 --- a/test/5.5-lp.sh +++ b/test/5.5-lp.sh @@ -4,7 +4,7 @@ # # Test the lp command. # -# Copyright 2007-2011 by Apple Inc. +# Copyright 2007-2012 by Apple Inc. # Copyright 1997-2005 by Easy Software Products, all rights reserved. # # These coded instructions, statements, and computer programs are the @@ -28,8 +28,8 @@ echo "" echo "LP Destination Test" echo "" -echo " lp -d Test2 testfile.jpg" -$VALGRIND ../systemv/lp -d Test2 testfile.jpg 2>&1 +echo " lp -d Test3 -o fit-to-page testfile.jpg" +$VALGRIND ../systemv/lp -d Test3 -o fit-to-page testfile.jpg 2>&1 if test $? != 0; then echo " FAILED" exit 1 diff --git a/test/5.6-lpr.sh b/test/5.6-lpr.sh index 17646aa88..9a26e75cb 100644 --- a/test/5.6-lpr.sh +++ b/test/5.6-lpr.sh @@ -4,7 +4,7 @@ # # Test the lpr command. # -# Copyright 2007-2011 by Apple Inc. +# Copyright 2007-2012 by Apple Inc. # Copyright 1997-2005 by Easy Software Products, all rights reserved. # # These coded instructions, statements, and computer programs are the @@ -28,8 +28,8 @@ echo "" echo "LPR Destination Test" echo "" -echo " lpr -P Test2 testfile.jpg" -$VALGRIND ../berkeley/lpr -P Test2 testfile.jpg 2>&1 +echo " lpr -P Test3 -o fit-to-page testfile.jpg" +$VALGRIND ../berkeley/lpr -P Test3 -o fit-to-page testfile.jpg 2>&1 if test $? != 0; then echo " FAILED" exit 1 diff --git a/test/Makefile b/test/Makefile index cd6f5f2d5..ad85720cc 100644 --- a/test/Makefile +++ b/test/Makefile @@ -32,6 +32,7 @@ DATAFILES = \ onepage-letter.pdf \ onepage-letter.ps \ testfile.jpg \ + testfile.pcl \ testfile.pdf \ testfile.ps \ testfile.txt diff --git a/test/ipp-1.1.test b/test/ipp-1.1.test index 23f2a0bde..5d67ff678 100644 --- a/test/ipp-1.1.test +++ b/test/ipp-1.1.test @@ -156,7 +156,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS client-error-document-format-not-supported STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -671,7 +671,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" ATTR name requesting-user-name $user STATUS successful-ok - EXPECT job-state OF-TYPE unknown|enum IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >6 REPEAT-NO-MATCH + EXPECT job-state OF-TYPE unknown|enum IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >6 REPEAT-NO-MATCH REPEAT-LIMIT 30 DISPLAY job-state } @@ -833,7 +833,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS client-error-document-format-not-supported STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -945,7 +945,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1262,7 +1262,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS client-error-document-format-not-supported STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1300,7 +1300,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1338,7 +1338,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1374,7 +1374,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1412,7 +1412,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1450,7 +1450,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1488,7 +1488,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1524,7 +1524,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1562,7 +1562,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1600,7 +1600,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1636,7 +1636,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1672,7 +1672,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1708,7 +1708,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1744,7 +1744,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1780,7 +1780,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1820,7 +1820,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1858,7 +1858,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1896,7 +1896,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1934,7 +1934,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -1974,7 +1974,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2012,7 +2012,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2050,7 +2050,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2088,7 +2088,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2128,7 +2128,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2166,7 +2166,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2204,7 +2204,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2242,7 +2242,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2280,7 +2280,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag @@ -2315,7 +2315,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/" STATUS successful-ok STATUS server-error-job-canceled - STATUS server-error-busy REPEAT-MATCH + STATUS server-error-busy REPEAT-MATCH REPEAT-LIMIT 10 EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME" EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag diff --git a/test/ipptool.c b/test/ipptool.c index 1663da770..696a8922f 100644 --- a/test/ipptool.c +++ b/test/ipptool.c @@ -101,7 +101,8 @@ typedef struct _cups_expect_s /**** Expected attribute info ****/ *define_match, /* Variable to define on match */ *define_no_match, /* Variable to define on no-match */ *define_value; /* Variable to define with value */ - int repeat_match, /* Repeat test on match */ + int repeat_limit, /* Maximum number of times to repeat */ + repeat_match, /* Repeat test on match */ repeat_no_match, /* Repeat test on no match */ with_flags, /* WITH flags */ count; /* Expected count if > 0 */ @@ -113,7 +114,8 @@ typedef struct _cups_status_s /**** Status info ****/ ipp_status_t status; /* Expected status code */ char *if_defined, /* Only if variable is defined */ *if_not_defined; /* Only if variable is not defined */ - int repeat_match, /* Repeat the test when it does not match */ + int repeat_limit, /* Maximum number of times to repeat */ + repeat_match, /* Repeat the test when it does not match */ repeat_no_match; /* Repeat the test when it matches */ } _cups_status_t; @@ -456,6 +458,9 @@ main(int argc, /* I - Number of command-line args */ set_variable(&vars, "filetype", "text/html"); else if (!_cups_strcasecmp(ext, ".jpg")) set_variable(&vars, "filetype", "image/jpeg"); + else if (!_cups_strcasecmp(ext, ".pcl") || + !_cups_strcasecmp(ext, ".pcl.gz")) + set_variable(&vars, "filetype", "application/vnd.hp-PCL"); else if (!_cups_strcasecmp(ext, ".pdf")) set_variable(&vars, "filetype", "application/pdf"); else if (!_cups_strcasecmp(ext, ".png")) @@ -750,6 +755,9 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ show_header = 1, /* Show the test header? */ ignore_errors, /* Ignore test failures? */ skip_previous = 0, /* Skip on previous test failure? */ + repeat_count, /* Repeat count */ + repeat_interval, /* Repeat interval */ + repeat_prev, /* Previous repeat interval */ repeat_test; /* Repeat a test? */ http_t *http = NULL; /* HTTP connection to server */ FILE *fp = NULL; /* Test file */ @@ -1168,6 +1176,7 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ _cups_strcasecmp(token, "IF-NOT-DEFINED") && _cups_strcasecmp(token, "IN-GROUP") && _cups_strcasecmp(token, "OF-TYPE") && + _cups_strcasecmp(token, "REPEAT-LIMIT") && _cups_strcasecmp(token, "REPEAT-MATCH") && _cups_strcasecmp(token, "REPEAT-NO-MATCH") && _cups_strcasecmp(token, "SAME-COUNT-AS") && @@ -1177,6 +1186,7 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ if (_cups_strcasecmp(token, "IF-DEFINED") && _cups_strcasecmp(token, "IF-NOT-DEFINED") && + _cups_strcasecmp(token, "REPEAT-LIMIT") && _cups_strcasecmp(token, "REPEAT-MATCH") && _cups_strcasecmp(token, "REPEAT-NO-MATCH")) last_status = NULL; @@ -1796,6 +1806,7 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ last_status->if_defined = NULL; last_status->if_not_defined = NULL; + last_status->repeat_limit = 1000; last_status->repeat_match = 0; last_status->repeat_no_match = 0; } @@ -1823,6 +1834,7 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ num_expects ++; memset(last_expect, 0, sizeof(_cups_expect_t)); + last_expect->repeat_limit = 1000; if (token[0] == '!') { @@ -1968,6 +1980,33 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ goto test_exit; } } + else if (!_cups_strcasecmp(token, "REPEAT-LIMIT")) + { + if (!get_token(fp, token, sizeof(token), &linenum)) + { + print_fatal_error("Missing REPEAT-LIMIT value on line %d.", linenum); + pass = 0; + goto test_exit; + } + else if (atoi(token) <= 0) + { + print_fatal_error("Bad REPEAT-LIMIT value on line %d.", linenum); + pass = 0; + goto test_exit; + } + + if (last_status) + last_status->repeat_limit = atoi(token); + else if (last_expect) + last_expect->repeat_limit = atoi(token); + else + { + print_fatal_error("REPEAT-LIMIT without a preceding EXPECT or STATUS " + "on line %d.", linenum); + pass = 0; + goto test_exit; + } + } else if (!_cups_strcasecmp(token, "REPEAT-MATCH")) { if (last_status) @@ -2210,8 +2249,14 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ goto skip_error; } + repeat_count = 0; + repeat_interval = 1; + repeat_prev = 1; + do { + repeat_count ++; + status = HTTP_OK; if (transfer == _CUPS_TRANSFER_CHUNKED || @@ -2292,7 +2337,7 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ status = httpGetStatus(http); } - if (!Cancel && status == HTTP_ERROR && + if (!Cancel && status == HTTP_ERROR && http->error != EINVAL && #ifdef WIN32 http->error != WSAETIMEDOUT) #else @@ -2315,7 +2360,7 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ } } - if (!Cancel && status == HTTP_ERROR && + if (!Cancel && status == HTTP_ERROR && http->error != EINVAL && #ifdef WIN32 http->error != WSAETIMEDOUT) #else @@ -2592,12 +2637,14 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ if (response->request.status.status_code == statuses[i].status) { - if (statuses[i].repeat_match) + if (statuses[i].repeat_match && + repeat_count < statuses[i].repeat_limit) repeat_test = 1; break; } - else if (statuses[i].repeat_no_match) + else if (statuses[i].repeat_no_match && + repeat_count < statuses[i].repeat_limit) repeat_test = 1; } @@ -2663,7 +2710,8 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ } } - if (expect->repeat_no_match) + if (expect->repeat_no_match && + repeat_count < expect->repeat_limit) repeat_test = 1; continue; @@ -2697,7 +2745,8 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ buffer, sizeof(buffer)); } - if (expect->repeat_no_match) + if (expect->repeat_no_match && + repeat_count < expect->repeat_limit) repeat_test = 1; continue; @@ -2714,7 +2763,8 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ expect->count, found->num_values); } - if (expect->repeat_no_match) + if (expect->repeat_no_match && + repeat_count < expect->repeat_limit) repeat_test = 1; continue; @@ -2743,7 +2793,8 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ expect->same_count_as, attrptr->num_values); } - if (expect->repeat_no_match) + if (expect->repeat_no_match && + repeat_count < expect->repeat_limit) repeat_test = 1; continue; @@ -2756,7 +2807,8 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ if (found && expect->define_value) set_variable(vars, expect->define_value, buffer); - if (found && expect->repeat_match) + if (found && expect->repeat_match && + repeat_count < expect->repeat_limit) repeat_test = 1; } } @@ -2767,7 +2819,22 @@ do_tests(_cups_vars_t *vars, /* I - Variables */ */ if (repeat_test) - sleep(1); + { + if (Output == _CUPS_OUTPUT_TEST) + { + printf("%04d]\n", repeat_count); + fflush(stdout); + } + + sleep(repeat_interval); + repeat_interval = _cupsNextDelay(repeat_interval, &repeat_prev); + + if (Output == _CUPS_OUTPUT_TEST) + { + printf(" %-68.68s [", name); + fflush(stdout); + } + } } while (repeat_test); diff --git a/test/run-stp-tests.sh b/test/run-stp-tests.sh index 03e803102..ff589e9e0 100755 --- a/test/run-stp-tests.sh +++ b/test/run-stp-tests.sh @@ -5,7 +5,7 @@ # Perform the complete set of IPP compliance tests specified in the # CUPS Software Test Plan. # -# Copyright 2007-2011 by Apple Inc. +# Copyright 2007-2012 by Apple Inc. # Copyright 1997-2007 by Easy Software Products, all rights reserved. # # These coded instructions, statements, and computer programs are the @@ -17,6 +17,21 @@ argcount=$# +# +# Don't allow "make check" or "make test" to be run by root... +# + +if test "x`id -u`" = x0; then + echo Please run this as a normal user. Not supported when run as root. + exit 1 +fi + +# +# Force the permissions of the files we create... +# + +umask 022 + # # Make the IPP test program... # @@ -368,10 +383,11 @@ else fi cat >/tmp/cups-$user/cupsd.conf < -Order Deny,Allow -Deny from all -Allow from 127.0.0.1 +Order Allow,Deny $encryption @@ -605,11 +619,18 @@ echo `date "+%Y-%m-%d"` by $user on `hostname`. >>$strfile echo "
      " >>$strfile
       
       fail=0
      -for file in 4*.test; do
      +for file in 4*.test ipp-2.1.test; do
       	echo $ac_n "Performing $file: $ac_c"
       	echo "" >>$strfile
       
      -	$VALGRIND ./ipptool -tI ipp://localhost:$port/printers $file >> $strfile
      +	if test $file = ipp-2.1.test; then
      +		uri="ipp://localhost:$port/printers/Test1"
      +		options="-V 2.1 -d NOPRINT=1 -f testfile.ps"
      +	else
      +		uri="ipp://localhost:$port/printers"
      +		options=""
      +	fi
      +	$VALGRIND ./ipptool -tI $options $uri $file >> $strfile
       	status=$?
       
       	if test $status != 0; then
      @@ -710,9 +731,21 @@ else
       	echo "

      PASS: Printer 'Test2' correctly produced $count page(s).

      " >>$strfile fi +# Paged printed on Test3 +count=`$GREP '^Test3 ' /tmp/cups-$user/log/page_log | grep -v total | awk 'BEGIN{count=0}{count=count+$7}END{print count}'` +expected=2 +if test $count != $expected; then + echo "FAIL: Printer 'Test3' produced $count page(s), expected $expected." + echo "

      FAIL: Printer 'Test3' produced $count page(s), expected $expected.

      " >>$strfile + fail=`expr $fail + 1` +else + echo "PASS: Printer 'Test3' correctly produced $count page(s)." + echo "

      PASS: Printer 'Test3' correctly produced $count page(s).

      " >>$strfile +fi + # Requests logged count=`wc -l /tmp/cups-$user/log/access_log | awk '{print $1}'` -expected=`expr 37 + 18 + $pjobs \* 8 + $pprinters \* $pjobs \* 4` +expected=`expr 37 + 18 + 28 + $pjobs \* 8 + $pprinters \* $pjobs \* 4` if test $count != $expected; then echo "FAIL: $count requests logged, expected $expected." echo "

      FAIL: $count requests logged, expected $expected.

      " >>$strfile @@ -782,10 +815,10 @@ fi # Error log messages count=`$GREP '^E ' /tmp/cups-$user/log/error_log | wc -l | awk '{print $1}'` -if test $count != 18; then - echo "FAIL: $count error messages, expected 18." +if test $count != 33; then + echo "FAIL: $count error messages, expected 33." $GREP '^E ' /tmp/cups-$user/log/error_log - echo "

      FAIL: $count error messages, expected 18.

      " >>$strfile + echo "

      FAIL: $count error messages, expected 33.

      " >>$strfile echo "
      " >>$strfile
       	$GREP '^E ' /tmp/cups-$user/log/error_log | sed -e '1,$s/&/&/g' -e '1,$s/>$strfile
       	echo "
      " >>$strfile diff --git a/test/testfile.pcl b/test/testfile.pcl new file mode 100644 index 000000000..4b8659767 --- /dev/null +++ b/test/testfile.pcl @@ -0,0 +1,60 @@ +EAll work and no play makes Johhny a dull boy. All work and no +play makes Johhny a dull boy. All work and no play makes Johhny +a dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. All +work and no play makes Johhny a dull boy. All work and no play +makes Johhny a dull boy. All work and no play makes Johhny a +dull boy. All work and no play makes Johhny a dull boy. E diff --git a/tools/testbtmm.c b/tools/testbtmm.c new file mode 100644 index 000000000..bd6379f84 --- /dev/null +++ b/tools/testbtmm.c @@ -0,0 +1,65 @@ +/* + * Simple test program that lists the Back to My Mac domains on a Mac. + * + * Compile with: + * + * clang -o testbtmm -g testbtmm.c -framework SystemConfiguration -framework CoreFoundation + */ + +#include +#include +#include + + +/* + * 'dnssdAddAlias()' - Add a DNS-SD alias name. + */ + +static void +show_domain(const void *key, /* I - Key */ + const void *value, /* I - Value (domain) */ + void *context) /* I - Unused */ +{ + char valueStr[1024]; /* Domain string */ + + + (void)key; + (void)context; + + if (CFGetTypeID((CFStringRef)value) == CFStringGetTypeID() && + CFStringGetCString((CFStringRef)value, valueStr, sizeof(valueStr), + kCFStringEncodingUTF8)) + printf("Back to My Mac domain: \"%s\"\n", valueStr); + else + puts("Bad Back to My Mac domain in dynamic store."); +} + + +int +main(void) +{ + SCDynamicStoreRef sc; /* Context for dynamic store */ + CFDictionaryRef btmm; /* Back-to-My-Mac domains */ + + + sc = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cups"), NULL, NULL); + + if (!sc) + { + puts("Unable to open dynamic store."); + exit(1); + } + + btmm = SCDynamicStoreCopyValue(sc, CFSTR("Setup:/Network/BackToMyMac")); + if (btmm && CFGetTypeID(btmm) == CFDictionaryGetTypeID()) + { + printf("%d Back to My Mac domains.\n", (int)CFDictionaryGetCount(btmm)); + CFDictionaryApplyFunction(btmm, show_domain, NULL); + } + else if (btmm) + puts("Bad Back to My Mac data in dynamic store."); + else + puts("No Back to My Mac domains."); + + return (1); +} diff --git a/xcode/CUPS.xcodeproj/project.pbxproj b/xcode/CUPS.xcodeproj/project.pbxproj index 429561178..4bc8343ba 100644 --- a/xcode/CUPS.xcodeproj/project.pbxproj +++ b/xcode/CUPS.xcodeproj/project.pbxproj @@ -1303,8 +1303,8 @@ 728FB7EC1536161C005426E1 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = ../../../../../usr/lib/libz.dylib; sourceTree = ""; }; 728FB7EF1536167A005426E1 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = ../../../../../usr/lib/libiconv.dylib; sourceTree = ""; }; 728FB7F01536167A005426E1 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = ../../../../../usr/lib/libresolv.dylib; sourceTree = ""; }; + 72A4332F155844CF002E172D /* libcups_static.a */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcups_static.a; sourceTree = BUILT_PRODUCTS_DIR; }; 72C16CB8137B195D007E4BF4 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = file.c; path = ../scheduler/file.c; sourceTree = SOURCE_ROOT; }; - 72F75A4C1336F31B004BB496 /* libcups_static.a */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; name = libcups_static.a; path = "/Users/msweet/c/cups-trunk/xcode/build/Release/libcups_static.a"; sourceTree = ""; }; 72F75A521336F950004BB496 /* cupstestppd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cupstestppd; sourceTree = BUILT_PRODUCTS_DIR; }; 72F75A5B1336F988004BB496 /* cupstestppd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cupstestppd.c; path = ../systemv/cupstestppd.c; sourceTree = ""; }; 72F75A611336F9A3004BB496 /* libcupsimage.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcupsimage.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1694,6 +1694,7 @@ 270CCDA7135E3C9E00007BE2 /* testmime */, 726AD6F7135E88F0002C930D /* ippserver */, 278C58CB136B640300836530 /* testhttp */, + 72A4332F155844CF002E172D /* libcups_static.a */, ); name = Products; sourceTree = ""; @@ -2205,7 +2206,7 @@ ); name = libcups_static; productName = libcups; - productReference = 72F75A4C1336F31B004BB496 /* libcups_static.a */; + productReference = 72A4332F155844CF002E172D /* libcups_static.a */; productType = "com.apple.product-type.library.dynamic"; }; 2766835B1337A9B6000D33D0 /* cupsctl */ = { @@ -2617,7 +2618,7 @@ 72BF96371333042100B1EAD7 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0420; + LastUpgradeCheck = 0440; ORGANIZATIONNAME = "Apple Inc."; }; buildConfigurationList = 72BF963A1333042100B1EAD7 /* Build configuration list for PBXProject "CUPS" */; -- 2.39.2
      Table: CUPS Basic Release Schedule
      {job_printer_name}-{job_id}{?phone? ({phone})} {job_printer_name}-{job_id}{?phone? ({phone}):}  {?job_name=?Unknown:{job_name}}  {?job_originating_user_name=?Withheld:{job_originating_user_name}}  {job_k_octets}k