Update libcups to v2.2.9 RESTRICT AUTOMERGE

Bug: 111210196
Test: adb shell am instrument -w android.print.cts/android.support.test.runner.AndroidJUnitRunner

Change-Id: Idb1ecb93551bf6f77527513ddfb3de4bfd70c092
diff --git a/config.h b/config.h
index 2decf31..03e12da 100644
--- a/config.h
+++ b/config.h
@@ -17,8 +17,8 @@
 #ifndef _CUPS_CONFIG_H_
 #define _CUPS_CONFIG_H_
 
-#define CUPS_SVERSION "CUPS v2.2.3"
-#define CUPS_MINIMAL "CUPS/2.2.3"
+#define CUPS_SVERSION "CUPS v2.2.9"
+#define CUPS_MINIMAL "CUPS/2.2.9"
 #define CUPS_DEFAULT_PRINTOPERATOR_AUTH "@SYSTEM"
 #define CUPS_DEFAULT_LOG_LEVEL "warn"
 #define CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS "dnssd"
diff --git a/cups/Makefile b/cups/Makefile
index 1df7d85..f83c937 100644
--- a/cups/Makefile
+++ b/cups/Makefile
@@ -1,8 +1,8 @@
 #
-# API library Makefile for CUPS.
+# Library Makefile for CUPS.
 #
-# Copyright 2007-2016 by Apple Inc.
-# Copyright 1997-2006 by Easy Software Products, all rights reserved.
+# Copyright © 2007-2018 by Apple Inc.
+# Copyright © 1997-2006 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
@@ -89,6 +89,7 @@
 		testcups.o \
 		testdest.o \
 		testfile.o \
+		testgetdests.o \
 		testhttp.o \
 		testi18n.o \
 		testipp.o \
@@ -158,6 +159,7 @@
 		testcups \
 		testdest \
 		testfile \
+		testgetdests \
 		testhttp \
 		testi18n \
 		testipp \
@@ -310,8 +312,9 @@
 
 libcups.so.2:	$(LIBOBJS)
 	echo Linking $@...
-	$(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS) $(LIBGSSAPI) \
+	$(DSO) $(ARCHFLAGS) $(LDFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS) $(LIBGSSAPI) \
 		$(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	$(RM) `basename $@ .2`
 	$(LN) $@ `basename $@ .2`
 
@@ -327,13 +330,14 @@
 		grep -v -E -e '^(_cupsConnect|_cupsCharset|_cupsEncodingName|_cupsSetDefaults|_cupsSetHTTPError|_cupsUserDefault)$$' | \
 		sort >t.exp
 	echo Linking $@...
-	$(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ \
+	$(DSO) $(ARCHFLAGS) $(LDFLAGS) $(DSOFLAGS) -o $@ \
 		-install_name $(libdir)/$@ \
-		-current_version 2.12.0 \
+		-current_version 2.13.0 \
 		-compatibility_version 2.0.0 \
 		-exported_symbols_list t.exp \
 		$(LIBOBJS) $(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) \
 		$(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	$(RM) libcups.dylib t.exp
 	$(LN) $@ libcups.dylib
 
@@ -344,8 +348,8 @@
 
 libcups.la:    $(LIBOBJS)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS:.o=.lo) \
-		-rpath $(LIBDIR) -version-info 2:12 $(LIBGSSAPI) $(SSLLIBS) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) $(DSOFLAGS) -o $@ $(LIBOBJS:.o=.lo) \
+		-rpath $(LIBDIR) -version-info 2:13 $(LIBGSSAPI) $(SSLLIBS) \
 		$(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
 
 
@@ -367,12 +371,12 @@
 libcups2.def: $(LIBOBJS) Makefile
 	echo Generating $@...
 	echo "LIBRARY libcups2" >libcups2.def
-	echo "VERSION 2.12" >>libcups2.def
+	echo "VERSION 2.13" >>libcups2.def
 	echo "EXPORTS" >>libcups2.def
 	(nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}'; \
 	 echo __cups_strcpy; echo __cups_strlcat; echo __cups_strlcpy) | \
 		grep -v -E \
-		    -e 'cups_debug|Apple|BackChannel|Backend|FileCheck|Filter|GSSService|SetNegotiate|SideChannel' \
+		    -e 'cups_debug|Apple|BackChannel|Backend|FileCheck|Filter|GSSService|SetNegotiate|SideChannel|SNMP' \
 		    -e 'Block$$' | \
 		sed -e '1,$$s/^_//' | sort >>libcups2.def
 
@@ -383,8 +387,9 @@
 
 testadmin:	testadmin.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testadmin.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testadmin.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -393,8 +398,9 @@
 
 testarray:	testarray.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testarray.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testarray.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running array API tests...
 	./testarray
 
@@ -405,8 +411,9 @@
 
 testcache:	testcache.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testcache.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testcache.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -415,8 +422,9 @@
 
 testconflicts:	testconflicts.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testconflicts.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testconflicts.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -425,8 +433,9 @@
 
 testcreds:	testcreds.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testcreds.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testcreds.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -435,8 +444,9 @@
 
 testcups:	testcups.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testcups.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testcups.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -445,8 +455,9 @@
 
 testdest:	testdest.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testdest.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testdest.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -455,20 +466,33 @@
 
 testfile:	testfile.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testfile.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testfile.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running file API tests...
 	./testfile
 
 
 #
+# testgetdests (dependency on static CUPS library is intentional)
+#
+
+testgetdests:	testgetdests.o $(LIBCUPSSTATIC)
+	echo Linking $@...
+	$(LD_CC) $(LDFLAGS) -o $@ testgetdests.o $(LIBCUPSSTATIC) \
+		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
+
+
+#
 # testhttp (dependency on static CUPS library is intentional)
 #
 
 testhttp:	testhttp.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testhttp.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testhttp.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running HTTP API tests...
 	./testhttp
 
@@ -479,8 +503,9 @@
 
 testipp:	testipp.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testipp.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testipp.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running IPP API tests...
 	./testipp
 
@@ -491,8 +516,9 @@
 
 testi18n:	testi18n.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testi18n.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testi18n.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running internationalization API tests...
 	./testi18n
 
@@ -503,10 +529,23 @@
 
 testlang:	testlang.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testlang.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testlang.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
+	echo Creating locale directory structure...
+	$(RM) -r locale
+	$(MKDIR) locale/en
+	echo 'msgid "No"' > locale/en/cups_en.po
+	echo 'msgstr "No"' >> locale/en/cups_en.po
+	echo 'msgid "Yes"' >> locale/en/cups_en.po
+	echo 'msgstr "Yes"' >> locale/en/cups_en.po
+	for po in ../locale/cups_*.po; do \
+		lang=`basename $$po .po | sed -e '1,$$s/^cups_//'`; \
+		$(MKDIR) locale/$$lang; \
+		$(LN) ../../$$po locale/$$lang; \
+	done
 	echo Running language API tests...
-	./testlang
+	LOCALEDIR=locale ./testlang
 
 
 #
@@ -515,8 +554,9 @@
 
 testoptions:	testoptions.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testoptions.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testoptions.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running option API tests...
 	./testoptions
 
@@ -527,8 +567,9 @@
 
 testppd:	testppd.o $(LIBCUPSSTATIC) test.ppd test2.ppd
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testppd.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testppd.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running PPD API tests...
 	./testppd
 
@@ -539,8 +580,9 @@
 
 testpwg:	testpwg.o $(LIBCUPSSTATIC) test.ppd
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testpwg.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testpwg.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running PWG API tests...
 	./testpwg test.ppd
 
@@ -551,8 +593,9 @@
 
 testsnmp:	testsnmp.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ testsnmp.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(LDFLAGS) -o $@ testsnmp.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -561,8 +604,9 @@
 
 tlscheck:	tlscheck.o $(LIBCUPSSTATIC)
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ tlscheck.o $(LIBCUPSSTATIC) \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ tlscheck.o $(LIBCUPSSTATIC) \
 		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -571,56 +615,27 @@
 
 apihelp:
 	echo Generating CUPS API help files...
-	mxmldoc --section "Programming" \
-		--title "Introduction to CUPS Programming" \
-		--css ../doc/cups-printable.css \
-		--header api-overview.header --intro api-overview.shtml \
-		>../doc/help/api-overview.html
+	$(RM) cupspm.xml
+	mxmldoc --section "Programming" --body cupspm.md \
+		cupspm.xml \
+		auth.c cups.h dest*.c encode.c http.h http*.c ipp.h ipp*.c \
+		options.c tls-darwin.c usersys.c util.c \
+		--coverimage cupspm.png \
+		--epub ../doc/help/cupspm.epub
+	mxmldoc --section "Programming" --body cupspm.md \
+		cupspm.xml > ../doc/help/cupspm.html
+	$(RM) cupspm.xml
 	mxmldoc --section "Programming" --title "Administration APIs" \
 		--css ../doc/cups-printable.css \
 		--header api-admin.header --intro api-admin.shtml \
 		api-admin.xml \
 		adminutil.c adminutil.h getdevices.c >../doc/help/api-admin.html
-	mxmldoc --tokens help/api-admin.html api-admin.xml >../doc/help/api-admin.tokens
 	$(RM) api-admin.xml
-	mxmldoc --section "Programming" --title "Array API" \
-		--css ../doc/cups-printable.css \
-		--header api-array.header --intro api-array.shtml \
-		api-array.xml \
-		array.h array.c >../doc/help/api-array.html
-	mxmldoc --tokens help/api-array.html api-array.xml >../doc/help/api-array.tokens
-	$(RM) api-array.xml
-	mxmldoc --section "Programming" --title "CUPS API" \
-		--css ../doc/cups-printable.css \
-		--header api-cups.header --intro api-cups.shtml \
-		api-cups.xml \
-		cups.h pwg.h adminutil.c dest*.c language.c notify.c \
-		options.c pwg-media.c tempfile.c usersys.c \
-		util.c >../doc/help/api-cups.html
-	mxmldoc --tokens help/api-cups.html api-cups.xml >../doc/help/api-cups.tokens
-	$(RM) api-cups.xml
-	mxmldoc --section "Programming" --title "File and Directory APIs" \
-		--css ../doc/cups-printable.css \
-		--header api-filedir.header --intro api-filedir.shtml \
-		api-filedir.xml \
-		file.h file.c dir.h dir.c >../doc/help/api-filedir.html
-	mxmldoc --tokens api-filedir.xml >../doc/help/api-filedir.tokens
-	$(RM) api-filedir.xml
 	mxmldoc --section "Programming" --title "PPD API (DEPRECATED)" \
 		--css ../doc/cups-printable.css \
 		--header api-ppd.header --intro api-ppd.shtml \
 		api-ppd.xml ppd.h ppd-*.c >../doc/help/api-ppd.html
-	mxmldoc --tokens help/api-ppd.html api-ppd.xml >../doc/help/api-ppd.tokens
 	$(RM) api-ppd.xml
-	mxmldoc --section "Programming" --title "HTTP and IPP APIs" \
-		--css ../doc/cups-printable.css \
-		--header api-httpipp.header --intro api-httpipp.shtml \
-		api-httpipp.xml \
-		http.h ipp.h auth.c getdevices.c getputfile.c encode.c \
-		http.c http-addr.c http-support.c ipp.c ipp-support.c \
-		md5passwd.c request.c >../doc/help/api-httpipp.html
-	mxmldoc --tokens help/api-httpipp.html api-httpipp.xml >../doc/help/api-httpipp.tokens
-	$(RM) api-httpipp.xml
 	mxmldoc --section "Programming" \
 		--title "Filter and Backend Programming" \
 		--css ../doc/cups-printable.css \
@@ -628,7 +643,6 @@
 		api-filter.xml \
 		backchannel.c backend.h backend.c sidechannel.c sidechannel.h \
 		>../doc/help/api-filter.html
-	mxmldoc --tokens help/api-filter.html api-filter.xml >../doc/help/api-filter.tokens
 	$(RM) api-filter.xml
 
 
@@ -637,8 +651,6 @@
 #
 
 sloc:
-	echo "libcupslite: \c"
-	sloccount $(LITEOBJS:.o=.c) 2>/dev/null | grep "Total Physical" | awk '{print $$9}'
 	echo "libcups: \c"
 	sloccount $(LIBOBJS:.o=.c) 2>/dev/null | grep "Total Physical" | awk '{print $$9}'
 
diff --git a/cups/adminutil.c b/cups/adminutil.c
index adb1f7a..375b905 100644
--- a/cups/adminutil.c
+++ b/cups/adminutil.c
@@ -22,11 +22,11 @@
 #include "adminutil.h"
 #include <fcntl.h>
 #include <sys/stat.h>
-#ifdef WIN32
+#ifdef _WIN32
 #else
 #  include <unistd.h>
 #  include <sys/wait.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -2087,7 +2087,7 @@
 		 const char *authfile,	/* I - Samba authentication file */
 		 FILE *logfile)		/* I - Optional log file */
 {
-#ifdef WIN32
+#ifdef _WIN32
   return (1);				/* Always fail on Windows... */
 
 #else
@@ -2154,7 +2154,7 @@
     return (WEXITSTATUS(status));
   else
     return (-WTERMSIG(status));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -2172,9 +2172,9 @@
     int             *remote)		/* O - Remote file? */
 {
   int		fd;			/* Temporary file descriptor */
-#ifndef WIN32
+#ifndef _WIN32
   struct stat	info;			/* cupsd.conf file information */
-#endif /* WIN32 */
+#endif /* _WIN32 */
   http_status_t	status;			/* Status of getting cupsd.conf */
   char		host[HTTP_MAX_HOST];	/* Hostname for connection */
 
@@ -2191,7 +2191,7 @@
   snprintf(name, namesize, "%s/cupsd.conf", cg->cups_serverroot);
   *remote = 0;
 
-#ifndef WIN32
+#ifndef _WIN32
   if (!_cups_strcasecmp(host, "localhost") && !access(name, R_OK))
   {
    /*
@@ -2218,7 +2218,7 @@
       status = HTTP_STATUS_OK;
   }
   else
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
   {
    /*
     * Read cupsd.conf via a HTTP GET request...
diff --git a/cups/api-array.header b/cups/api-array.header
deleted file mode 100644
index 557833e..0000000
--- a/cups/api-array.header
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
-  Array API header for CUPS.
-
-  Copyright 2008-2011 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/".
--->
-
-<h1 class='title'>Array API</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Header</th>
-	<th>cups/array.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html' target='_top'>Introduction to CUPS Programming</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-array.shtml b/cups/api-array.shtml
deleted file mode 100644
index 374ef5b..0000000
--- a/cups/api-array.shtml
+++ /dev/null
@@ -1,194 +0,0 @@
-<!--
-  Array API introduction for CUPS.
-
-  Copyright 2007-2011 by Apple Inc.
-  Copyright 1997-2006 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/".
--->
-
-<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
-
-<p>The CUPS array API provides a high-performance generic array container.
-The contents of the array container can be sorted and the container itself is
-designed for optimal speed and memory usage under a wide variety of conditions.
-Sorted arrays use a binary search algorithm from the last found or inserted
-element to quickly find matching elements in the array. Arrays created with the
-optional hash function can often find elements with a single lookup. The
-<a href='#cups_array_t'><code>cups_array_t</code></a> type is used when
-referring to a CUPS array.</p>
-
-<p>The CUPS scheduler (<tt>cupsd</tt>) and many of the CUPS API
-functions use the array API to efficiently manage large lists of
-data.</p>
-
-<h3><a name='MANAGING_ARRAYS'>Managing Arrays</a></h3>
-
-<p>Arrays are created using either the
-<a href='#cupsArrayNew'><code>cupsArrayNew</code></a>,
-<a href='#cupsArrayNew2'><code>cupsArrayNew2</code></a>, or
-<a href='#cupsArrayNew2'><code>cupsArrayNew3</code></a> functions. The
-first function creates a new array with the specified callback function
-and user data pointer:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-static int compare_func(void *first, void *second, void *user_data);
-
-void *user_data;
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>(compare_func, user_data);
-</pre>
-
-<p>The comparison function (type
-<a href="#cups_arrayfunc_t"><code>cups_arrayfunc_t</code></a>) is called
-whenever an element is added to the array and can be <code>NULL</code> to
-create an unsorted array. The function returns -1 if the first element should
-come before the second, 0 if the first and second elements should have the same
-ordering, and 1 if the first element should come after the second.</p>
-
-<p>The "user_data" pointer is passed to your comparison function. Pass
-<code>NULL</code> if you do not need to associate the elements in your array
-with additional information.</p>
-
-<p>The <a href='#cupsArrayNew2'><code>cupsArrayNew2</code></a> function adds
-two more arguments to support hashed lookups, which can potentially provide
-instantaneous ("O(1)") lookups in your array:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-#define HASH_SIZE 512 /* Size of hash table */
-
-static int compare_func(void *first, void *second, void *user_data);
-static int hash_func(void *element, void *user_data);
-
-void *user_data;
-<a href='#cups_array_t'>cups_array_t</a> *hash_array = <a href='#cupsArrayNew2'>cupsArrayNew2</a>(compare_func, user_data, hash_func, HASH_SIZE);
-</pre>
-
-<p>The hash function (type
-<a href="#cups_ahash_func_t"><code>cups_ahash_func_t</code></a>) should return a
-number from 0 to (hash_size-1) that (hopefully) uniquely identifies the
-element and is called whenever you look up an element in the array with
-<a href='#cupsArrayFind'><code>cupsArrayFind</code></a>. The hash size is
-only limited by available memory, but generally should not be larger than
-16384 to realize any performance improvement.</p>
-
-<p>The <a href='#cupsArrayNew3'><code>cupsArrayNew3</code></a> function adds
-copy and free callbacks to support basic memory management of elements:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-#define HASH_SIZE 512 /* Size of hash table */
-
-static int compare_func(void *first, void *second, void *user_data);
-static void *copy_func(void *element, void *user_data);
-static void free_func(void *element, void *user_data);
-static int hash_func(void *element, void *user_data);
-
-void *user_data;
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew3'>cupsArrayNew3</a>(compare_func, user_data, NULL, 0, copy_func, free_func);
-
-<a href='#cups_array_t'>cups_array_t</a> *hash_array = <a href='#cupsArrayNew3'>cupsArrayNew3</a>(compare_func, user_data, hash_func, HASH_SIZE, copy_func, free_func);
-</pre>
-
-<p>Once you have created the array, you add elements using the
-<a href='#cupsArrayAdd'><code>cupsArrayAdd</code></a>
-<a href='#cupsArrayInsert'><code>cupsArrayInsert</code></a> functions.
-The first function adds an element to the array, adding the new element
-after any elements that have the same order, while the second inserts the
-element before others with the same order. For unsorted arrays,
-<a href='#cupsArrayAdd'><code>cupsArrayAdd</code></a> appends the element to
-the end of the array while
-<a href='#cupsArrayInsert'><code>cupsArrayInsert</code></a> inserts the
-element at the beginning of the array. For example, the following code
-creates a sorted array of character strings:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-/* Use strcmp() to compare strings - it will ignore the user_data pointer */
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
-
-/* Add four strings to the array */
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
-</pre>
-
-<p>Elements are removed using the
-<a href='#cupsArrayRemove'><code>cupsArrayRemove</code></a> function, for
-example:</p>
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-/* Use strcmp() to compare strings - it will ignore the user_data pointer */
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
-
-/* Add four strings to the array */
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
-
-/* Remove "Red Fish" */
-<a href='#cupsArrayRemove'>cupsArrayRemove</a>(array, "Red Fish");
-</pre>
-
-<p>Finally, you free the memory used by the array using the
-<a href='#cupsArrayDelete'><code>cupsArrayDelete</code></a> function. All
-of the memory for the array and hash table (if any) is freed, however <em>CUPS
-does not free the elements unless you provide copy and free functions</em>.</p>
-
-<h3><a name='FINDING_AND_ENUMERATING'>Finding and Enumerating Elements</a></h3>
-
-<p>CUPS provides several functions to find and enumerate elements in an
-array. Each one sets or updates a "current index" into the array, such that
-future lookups will start where the last one left off:</p>
-
-<dl>
-	<dt><a href='#cupsArrayFind'><code>cupsArrayFind</code></a></dt>
-	<dd>Returns the first matching element.</dd>
-	<dt><a href='#cupsArrayFirst'><code>cupsArrayFirst</code></a></dt>
-	<dd>Returns the first element in the array.</dd>
-	<dt><a href='#cupsArrayIndex'><code>cupsArrayIndex</code></a></dt>
-	<dd>Returns the Nth element in the array, starting at 0.</dd>
-	<dt><a href='#cupsArrayLast'><code>cupsArrayLast</code></a></dt>
-	<dd>Returns the last element in the array.</dd>
-	<dt><a href='#cupsArrayNext'><code>cupsArrayNext</code></a></dt>
-	<dd>Returns the next element in the array.</dd>
-	<dt><a href='#cupsArrayPrev'><code>cupsArrayPrev</code></a></dt>
-	<dd>Returns the previous element in the array.</dd>
-</dl>
-
-<p>Each of these functions returns <code>NULL</code> when there is no
-corresponding element.  For example, a simple <code>for</code> loop using the
-<a href='#cupsArrayFirst'><code>cupsArrayFirst</code></a> and
-<a href='#cupsArrayNext'><code>cupsArrayNext</code></a> functions will
-enumerate all of the strings in our previous example:</p> 
-
-<pre class='example'>
-#include &lt;cups/array.h&gt;
-
-/* Use strcmp() to compare strings - it will ignore the user_data pointer */
-<a href='#cups_array_t'>cups_array_t</a> *array = <a href='#cupsArrayNew'>cupsArrayNew</a>((<a href='#cups_array_func_t'>cups_array_func_t</a>)strcmp, NULL);
-
-/* Add four strings to the array */
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "One Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Two Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Red Fish");
-<a href='#cupsArrayAdd'>cupsArrayAdd</a>(array, "Blue Fish");
-
-/* Show all of the strings in the array */
-char *s;
-for (s = (char *)<a href='#cupsArrayFirst'>cupsArrayFirst</a>(array); s != NULL; s = (char *)<a href='#cupsArrayNext'>cupsArrayNext</a>(array))
-  puts(s);
-</pre>
diff --git a/cups/api-cups.header b/cups/api-cups.header
deleted file mode 100644
index 23b3794..0000000
--- a/cups/api-cups.header
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
-  CUPS API header for CUPS.
-
-  Copyright 2008-2011 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/".
--->
-
-<h1 class='title'>CUPS API</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Header</th>
-	<th>cups/cups.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html' target='_top'>Introduction to CUPS Programming</a><br>
-	Programming: <a href='api-array.html' target='_top'>Array API</a><br>
-	Programming: <a href='api-filedir.html' target='_top'>File and Directory APIs</a><br>
-	Programming: <a href='api-filter.html' target='_top'>Filter and Backend Programming</a><br>
-	Programming: <a href='api-httpipp.html' target='_top'>HTTP and IPP APIs</a><br>
-	Programming: <a href='api-ppd.html' target='_top'>PPD API</a><br>
-	Programming: <a href='api-raster.html' target='_top'>Raster API</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-cups.shtml b/cups/api-cups.shtml
deleted file mode 100644
index 918efe7..0000000
--- a/cups/api-cups.shtml
+++ /dev/null
@@ -1,441 +0,0 @@
-<!--
-  API introduction for CUPS.
-
-  Copyright 2007-2013 by Apple Inc.
-  Copyright 1997-2006 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/".
--->
-
-<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
-
-<p>The CUPS API provides the convenience functions needed to support
-applications, filters, printer drivers, and backends that need to interface
-with the CUPS scheduler.</p>
-
-<h3><a name='CLIENTS_AND_SERVERS'>Clients and Servers</a></h3>
-
-<p>CUPS is based on the Internet Printing Protocol ("IPP"), which allows
-clients (applications) to communicate with a server (the scheduler) to get a
-list of printers, send print jobs, and so forth. You identify which server
-you want to communicate with using a pointer to the opaque structure
-<code>http_t</code>. All of the examples in this document use the
-<code>CUPS_HTTP_DEFAULT</code> constant, referring to the default connection
-to the scheduler. The <a href='api-httpipp.html' target='_top'>HTTP and IPP
-APIs</a> document provides more information on server connections.</p>
-
-<h3><a name='PRINTERS_AND_CLASSES'>Printers and Classes</a></h3>
-
-<p>Printers and classes (collections of printers) are accessed through
-the <a href="#cups_dest_t"><code>cups_dest_t</code></a> structure which
-includes the name (<code>name</code>), instance (<code>instance</code> -
-a way of selecting certain saved options/settings), and the options and
-attributes associated with that destination (<code>num_options</code> and
-<code>options</code>). Destinations are created using the
-<a href="#cupsGetDests"><code>cupsGetDests</code></a> function and freed
-using the <a href='#cupsFreeDests'><code>cupsFreeDests</code></a> function.
-The <a href='#cupsGetDest'><code>cupsGetDest</code></a> function finds a
-specific destination for printing:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dests;
-int num_dests = <a href='#cupsGetDests'>cupsGetDests</a>(&amp;dests);
-<a href='#cups_dest_t'>cups_dest_t</a> *dest = <a href='#cupsGetDest'>cupsGetDest</a>("name", NULL, num_dests, dests);
-
-/* do something with dest */
-
-<a href='#cupsFreeDests'>cupsFreeDests</a>(num_dests, dests);
-</pre>
-
-<p>Passing <code>NULL</code> to
-<a href='#cupsGetDest'><code>cupsGetDest</code></a> for the destination name
-will return the default destination. Similarly, passing a <code>NULL</code>
-instance will return the default instance for that destination.</p>
-
-<div class='table'><table summary='Table 1: Printer Attributes' width='80%'>
-<caption>Table 1: <a name='TABLE1'>Printer Attributes</a></caption>
-<thead>
-<tr>
-	<th>Attribute Name</th>
-	<th>Description</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<td>"auth-info-required"</td>
-	<td>The type of authentication required for printing to this
-	destination: "none", "username,password", "domain,username,password",
-	or "negotiate" (Kerberos)</td>
-</tr>
-<tr>
-	<td>"printer-info"</td>
-	<td>The human-readable description of the destination such as "My
-	Laser Printer".</td>
-</tr>
-<tr>
-	<td>"printer-is-accepting-jobs"</td>
-	<td>"true" if the destination is accepting new jobs, "false" if
-	not.</td>
-</tr>
-<tr>
-	<td>"printer-is-shared"</td>
-	<td>"true" if the destination is being shared with other computers,
-	"false" if not.</td>
-</tr>
-<tr>
-	<td>"printer-location"</td>
-	<td>The human-readable location of the destination such as "Lab 4".</td>
-</tr>
-<tr>
-	<td>"printer-make-and-model"</td>
-	<td>The human-readable make and model of the destination such as "HP
-	LaserJet 4000 Series".</td>
-</tr>
-<tr>
-	<td>"printer-state"</td>
-	<td>"3" if the destination is idle, "4" if the destination is printing
-	a job, and "5" if the destination is stopped.</td>
-</tr>
-<tr>
-	<td>"printer-state-change-time"</td>
-	<td>The UNIX time when the destination entered the current state.</td>
-</tr>
-<tr>
-	<td>"printer-state-reasons"</td>
-	<td>Additional comma-delimited state keywords for the destination
-	such as "media-tray-empty-error" and "toner-low-warning".</td>
-</tr>
-<tr>
-	<td>"printer-type"</td>
-	<td>The <a href='#cups_printer_t'><code>cups_printer_t</code></a>
-	value associated with the destination.</td>
-</tr>
-</tbody>
-</table></div>
-
-<h3><a name='OPTIONS'>Options</a></h3>
-
-<p>Options are stored in arrays of
-<a href='#cups_option_t'><code>cups_option_t</code></a> structures. Each
-option has a name (<code>name</code>) and value (<code>value</code>)
-associated with it. The <a href='#cups_dest_t'><code>cups_dest_t</code></a>
-<code>num_options</code> and <code>options</code> members contain the
-default options for a particular destination, along with several informational
-attributes about the destination as shown in <a href='#TABLE1'>Table 1</a>.
-The <a href='#cupsGetOption'><code>cupsGetOption</code></a> function gets
-the value for the named option. For example, the following code lists the
-available destinations and their human-readable descriptions:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dests;
-int num_dests = <a href='#cupsGetDests'>cupsGetDests</a>(&amp;dests);
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int i;
-const char *value;
-
-for (i = num_dests, dest = dests; i > 0; i --, dest ++)
-  if (dest->instance == NULL)
-  {
-    value = <a href='#cupsGetOption'>cupsGetOption</a>("printer-info", dest->num_options, dest->options);
-    printf("%s (%s)\n", dest->name, value ? value : "no description");
-  }
-
-<a href='#cupsFreeDests'>cupsFreeDests</a>(num_dests, dests);
-</pre>
-
-<p>You can create your own option arrays using the
-<a href='#cupsAddOption'><code>cupsAddOption</code></a> function, which
-adds a single named option to an array:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-int num_options = 0;
-<a href='#cups_option_t'>cups_option_t</a> *options = NULL;
-
-/* The returned num_options value is updated as needed */
-num_options = <a href='#cupsAddOption'>cupsAddOption</a>("first", "value", num_options, &amp;options);
-
-/* This adds a second option value */
-num_options = <a href='#cupsAddOption'>cupsAddOption</a>("second", "value", num_options, &amp;options);
-
-/* This replaces the first option we added */
-num_options = <a href='#cupsAddOption'>cupsAddOption</a>("first", "new value", num_options, &amp;options);
-</pre>
-
-<p>Use a <code>for</code> loop to copy the options from a destination:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-int i;
-int num_options = 0;
-<a href='#cups_option_t'>cups_option_t</a> *options = NULL;
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-
-for (i = 0; i &lt; dest->num_options; i ++)
-  num_options = <a href='#cupsAddOption'>cupsAddOption</a>(dest->options[i].name, dest->options[i].value,
-                              num_options, &amp;options);
-</pre>
-
-<p>Use the <a href='#cupsFreeOptions'><code>cupsFreeOptions</code></a>
-function to free the options array when you are done using it:</p>
-
-<pre class='example'>
-<a href='#cupsFreeOptions'>cupsFreeOptions</a>(num_options, options);
-</pre>
-
-<h3><a name='PRINT_JOBS'>Print Jobs</a></h3>
-
-<p>Print jobs are identified by a locally-unique job ID number from 1 to
-2<sup>31</sup>-1 and have options and one or more files for printing to a
-single destination. The <a href='#cupsPrintFile'><code>cupsPrintFile</code></a>
-function creates a new job with one file. The following code prints the CUPS
-test page file:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int num_options;
-<a href='#cups_option_t'>cups_option_t</a> *options;
-int job_id;
-
-/* Print a single file */
-job_id = <a href='#cupsPrintFile'>cupsPrintFile</a>(dest->name, "/usr/share/cups/data/testprint.ps",
-                        "Test Print", num_options, options);
-</pre>
-
-<p>The <a href='#cupsPrintFiles'><code>cupsPrintFiles</code></a> function
-creates a job with multiple files. The files are provided in a
-<code>char *</code> array:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int num_options;
-<a href='#cups_option_t'>cups_option_t</a> *options;
-int job_id;
-char *files[3] = { "file1.pdf", "file2.pdf", "file3.pdf" };
-
-/* Print three files */
-job_id = <a href='#cupsPrintFiles'>cupsPrintFiles</a>(dest->name, 3, files, "Test Print", num_options, options);
-</pre>
-
-<p>Finally, the <a href='#cupsCreateJob'><code>cupsCreateJob</code></a>
-function creates a new job with no files in it. Files are added using the
-<a href='#cupsStartDocument'><code>cupsStartDocument</code></a>,
-<a href='api-httpipp.html#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>,
-and <a href='#cupsFinishDocument'><code>cupsFinishDocument</code></a> functions.
-The following example creates a job with 10 text files for printing:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int num_options;
-<a href='#cups_option_t'>cups_option_t</a> *options;
-int job_id;
-int i;
-char buffer[1024];
-
-/* Create the job */
-job_id = <a href='#cupsCreateJob'>cupsCreateJob</a>(CUPS_HTTP_DEFAULT, dest->name, "10 Text Files",
-                       num_options, options);
-
-/* If the job is created, add 10 files */
-if (job_id > 0)
-{
-  for (i = 1; i &lt;= 10; i ++)
-  {
-    snprintf(buffer, sizeof(buffer), "file%d.txt", i);
-
-    <a href='#cupsStartDocument'>cupsStartDocument</a>(CUPS_HTTP_DEFAULT, dest->name, job_id, buffer,
-                      CUPS_FORMAT_TEXT, i == 10);
-
-    snprintf(buffer, sizeof(buffer),
-             "File %d\n"
-             "\n"
-             "One fish,\n"
-             "Two fish,\n
-             "Red fish,\n
-             "Blue fish\n", i);
-
-    /* cupsWriteRequestData can be called as many times as needed */
-    <a href='#cupsWriteRequestData'>cupsWriteRequestData</a>(CUPS_HTTP_DEFAULT, buffer, strlen(buffer));
-
-    <a href='#cupsFinishDocument'>cupsFinishDocument</a>(CUPS_HTTP_DEFAULT, dest->name);
-  }
-}
-</pre>
-
-<p>Once you have created a job, you can monitor its status using the
-<a href='#cupsGetJobs'><code>cupsGetJobs</code></a> function, which returns
-an array of <a href='#cups_job_t'><code>cups_job_t</code></a> structures.
-Each contains the job ID (<code>id</code>), destination name
-(<code>dest</code>), title (<code>title</code>), and other information
-associated with the job. The job array is freed using the
-<a href='#cupsFreeJobs'><code>cupsFreeJobs</code></a> function. The following
-example monitors a specific job ID, showing the current job state once every
-5 seconds until the job is completed:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int job_id;
-int num_jobs;
-<a href='#cups_job_t'>cups_job_t</a> *jobs;
-int i;
-ipp_jstate_t job_state = IPP_JOB_PENDING;
-
-while (job_state &lt; IPP_JOB_STOPPED)
-{
-  /* Get my jobs (1) with any state (-1) */
-  num_jobs = <a href='#cupsGetJobs'>cupsGetJobs</a>(&amp;jobs, dest->name, 1, -1);
-
-  /* Loop to find my job */
-  job_state = IPP_JOB_COMPLETED;
-
-  for (i = 0; i &lt; num_jobs; i ++)
-    if (jobs[i].id == job_id)
-    {
-      job_state = jobs[i].state;
-      break;
-    }
-
-  /* Free the job array */
-  <a href='#cupsFreeJobs'>cupsFreeJobs</a>(num_jobs, jobs);
-
-  /* Show the current state */
-  switch (job_state)
-  {
-    case IPP_JOB_PENDING :
-        printf("Job %d is pending.\n", job_id);
-        break;
-    case IPP_JOB_HELD :
-        printf("Job %d is held.\n", job_id);
-        break;
-    case IPP_JOB_PROCESSING :
-        printf("Job %d is processing.\n", job_id);
-        break;
-    case IPP_JOB_STOPPED :
-        printf("Job %d is stopped.\n", job_id);
-        break;
-    case IPP_JOB_CANCELED :
-        printf("Job %d is canceled.\n", job_id);
-        break;
-    case IPP_JOB_ABORTED :
-        printf("Job %d is aborted.\n", job_id);
-        break;
-    case IPP_JOB_COMPLETED :
-        printf("Job %d is completed.\n", job_id);
-        break;
-  }
-
-  /* Sleep if the job is not finished */
-  if (job_state &lt; IPP_JOB_STOPPED)
-    sleep(5);
-}
-</pre>
-
-<p>To cancel a job, use the
-<a href='#cupsCancelJob'><code>cupsCancelJob</code></a> function with the
-job ID:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-<a href='#cups_dest_t'>cups_dest_t</a> *dest;
-int job_id;
-
-<a href='#cupsCancelJob'>cupsCancelJob</a>(dest->name, job_id);
-</pre>
-
-<h3><a name='ERROR_HANDLING'>Error Handling</a></h3>
-
-<p>If any of the CUPS API printing functions returns an error, the reason for
-that error can be found by calling the
-<a href='#cupsLastError'><code>cupsLastError</code></a> and
-<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> functions.
-<a href='#cupsLastError'><code>cupsLastError</code></a> returns the last IPP
-error code
-(<a href='api-httpipp.html#ipp_status_t'><code>ipp_status_t</code></a>)
-that was encountered, while
-<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> returns
-a (localized) human-readable string that can be shown to the user. For example,
-if any of the job creation functions returns a job ID of 0, you can use
-<a href='#cupsLastErrorString'><code>cupsLastErrorString</code></a> to show
-the reason why the job could not be created:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-int job_id;
-
-if (job_id == 0)
-  puts(cupsLastErrorString());
-</pre>
-
-<h3><a name='PASSWORDS_AND_AUTHENTICATION'>Passwords and Authentication</a></h3>
-
-<p>CUPS supports authentication of any request, including submission of print
-jobs. The default mechanism for getting the username and password is to use the
-login user and a password from the console.</p>
-
-<p>To support other types of applications, in particular Graphical User
-Interfaces ("GUIs"), the CUPS API provides functions to set the default
-username and to register a callback function that returns a password string.</p>
-
-<p>The <a href="#cupsSetPasswordCB"><code>cupsSetPasswordCB</code></a>
-function is used to set a password callback in your program. Only one
-function can be used at any time.</p>
-
-<p>The <a href="#cupsSetUser"><code>cupsSetUser</code></a> function sets the
-current username for authentication. This function can be called by your
-password callback function to change the current username as needed.</p>
-
-<p>The following example shows a simple password callback that gets a
-username and password from the user:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-const char *
-my_password_cb(const char *prompt)
-{
-  char	user[65];
-
-
-  puts(prompt);
-
-  /* Get a username from the user */
-  printf("Username: ");
-  if (fgets(user, sizeof(user), stdin) == NULL)
-    return (NULL);
-
-  /* Strip the newline from the string and set the user */
-  user[strlen(user) - 1] = '\0';
-
-  <a href='#cupsSetUser'>cupsSetUser</a>(user);
-
-  /* Use getpass() to ask for the password... */
-  return (getpass("Password: "));
-}
-
-<a href='#cupsSetPasswordCB'>cupsSetPasswordCB</a>(my_password_cb);
-</pre>
-
-<p>Similarly, a GUI could display the prompt string in a window with input
-fields for the username and password. The username should default to the
-string returned by the <a href="#cupsUser"><code>cupsUser</code></a>
-function.</p>
diff --git a/cups/api-filedir.header b/cups/api-filedir.header
deleted file mode 100644
index 87744ee..0000000
--- a/cups/api-filedir.header
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-  File and Directory API header for CUPS.
-
-  Copyright 2008-2011 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/".
--->
-
-<h1 class='title'>File and Directory APIs</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Headers</th>
-	<th>cups/file.h<br>
-	cups/dir.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html' target='_top'>Introduction to CUPS Programming</a><br>
-	Programming: <a href='api-cups.html' target='_top'>CUPS API</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-filedir.shtml b/cups/api-filedir.shtml
deleted file mode 100644
index 8fdbee6..0000000
--- a/cups/api-filedir.shtml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!--
-  File and directory API introduction for CUPS.
-
-  Copyright 2007-2011 by Apple Inc.
-  Copyright 1997-2005 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/".
--->
-
-<h2 class='title'><a name="OVERVIEW">Overview</a></h2>
-
-<p>The CUPS file and directory APIs provide portable interfaces
-for manipulating files and listing files and directories. Unlike
-stdio <code>FILE</code> streams, the <code>cupsFile</code> functions
-allow you to open more than 256 files at any given time. They
-also manage the platform-specific details of locking, large file
-support, line endings (CR, LF, or CR LF), and reading and writing
-files using Flate ("gzip") compression. Finally, you can also
-connect, read from, and write to network connections using the
-<code>cupsFile</code> functions.</p>
-
-<p>The <code>cupsDir</code> functions manage the platform-specific
-details of directory access/listing and provide a convenient way
-to get both a list of files and the information (permissions,
-size, timestamp, etc.) for each of those files.</p>
diff --git a/cups/api-httpipp.header b/cups/api-httpipp.header
deleted file mode 100644
index cbede8f..0000000
--- a/cups/api-httpipp.header
+++ /dev/null
@@ -1,37 +0,0 @@
-<!--
-  HTTP and IPP API header for CUPS.
-
-  Copyright 2007-2016 by Apple Inc.
-  Copyright 1997-2006 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/".
--->
-
-<h1 class='title'>HTTP and IPP APIs</h1>
-
-<div class='summary'><table summary='General Information'>
-<thead>
-<tr>
-	<th>Headers</th>
-	<th>cups/cups.h<br>
-	cups/http.h<br>
-	cups/ipp.h</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-	<th>Library</th>
-	<td>-lcups</td>
-</tr>
-<tr>
-	<th>See Also</th>
-	<td>Programming: <a href='api-overview.html'>Introduction to CUPS Programming</a><br>
-	Programming: <a href='api-cups.html'>CUPS API</a><br>
-	References: <a href='spec-ipp.html'>CUPS Implementation of IPP</a></td>
-</tr>
-</tbody>
-</table></div>
diff --git a/cups/api-httpipp.shtml b/cups/api-httpipp.shtml
deleted file mode 100644
index 33bf5ad..0000000
--- a/cups/api-httpipp.shtml
+++ /dev/null
@@ -1,315 +0,0 @@
-<!--
-  HTTP and IPP API introduction for CUPS.
-
-  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
-  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/".
--->
-
-<h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
-
-<p>The CUPS HTTP and IPP APIs provide low-level access to the HTTP and IPP
-protocols and CUPS scheduler. They are typically used by monitoring and
-administration programs to perform specific functions not supported by the
-high-level CUPS API functions.</p>
-
-<p>The HTTP APIs use an opaque structure called
-<a href='#http_t'><code>http_t</code></a> to manage connections to
-a particular HTTP or IPP server. The
-<a href='#httpConnectEncrypt'><code>httpConnectEncrypt</code></a> function is
-used to create an instance of this structure for a particular server.
-The constant <code>CUPS_HTTP_DEFAULT</code> can be used with all of the
-<code>cups</code> functions to refer to the default CUPS server - the functions
-create a per-thread <a href='#http_t'><code>http_t</code></a> as needed.</p>
-
-<p>The IPP APIs use two opaque structures for requests (messages sent to the CUPS scheduler) and responses (messages sent back to your application from the scheduler). The <a href='#ipp_t'><code>ipp_t</code></a> type holds a complete request or response and is allocated using the <a href='#ippNew'><code>ippNew</code></a> or <a href='#ippNewRequest'><code>ippNewRequest</code></a> functions and freed using the <a href='#ippDelete'><code>ippDelete</code></a> function.</p>
-
-<p>The second opaque structure is called <a href='#ipp_attribute_t'><code>ipp_attribute_t</code></a> and holds a single IPP attribute which consists of a group tag (<a href='#ippGetGroupTag'><code>ippGetGroupTag</code></a>), a value type tag (<a href='#ippGetValueTag'><code>ippGetValueTag</code></a>), the attribute name (<a href='#ippGetName'><code>ippGetName</code></a>), and 1 or more values (<a href='#ippGetCount'><code>ippGetCount</code></a>, <a href='#ippGetBoolean'><code>ippGetBoolean</code></a>, <a href='#ippGetCollection'><code>ippGetCollection</code></a>, <a href='#ippGetDate'><code>ippGetDate</code></a>, <a href='#ippGetInteger'><code>ippGetInteger</code></a>, <a href='#ippGetRange'><code>ippGetRange</code></a>, <a href='#ippGetResolution'><code>ippGetResolution</code></a>, and <a href='#ippGetString'><code>ippGetString</code></a>). Attributes are added to an <a href='#ipp_t'><code>ipp_t</code></a> pointer using one of the <code>ippAdd</code> functions. For example, use <a href='#ippAddString'><code>ippAddString</code></a> to add the "printer-uri" and "requesting-user-name" string attributes to a request:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-             NULL, "ipp://localhost/printers/");
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-             NULL, cupsUser());
-</pre>
-
-<p>Once you have created an IPP request, use the <code>cups</code> functions to send the request to and read the response from the server. For example, the <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function can be used for simple query operations that do not involve files:</p>
-
-<pre class='example'>
-#include &lt;cups/cups.h&gt;
-
-
-<a href='#ipp_t'>ipp_t</a> *<a name='get_jobs'>get_jobs</a>(void)
-{
-  <a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_GET_JOBS);
-
-  <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, "ipp://localhost/printers/");
-  <a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-               NULL, cupsUser());
-
-  return (<a href='#cupsDoRequest'>cupsDoRequest</a>(CUPS_HTTP_DEFAULT, request, "/"));
-}
-</pre>
-
-<p>The <a href='#cupsDoRequest'><code>cupsDoRequest</code></a> function frees the request and returns an IPP response or <code>NULL</code> pointer if the request could not be sent to the server. Once you have a response from the server, you can either use the <a href='#ippFindAttribute'><code>ippFindAttribute</code></a> and <a href='#ippFindNextAttribute'><code>ippFindNextAttribute</code></a> functions to find specific attributes, for example:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *response;
-<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
-
-attr = <a href='#ippFindAttribute'>ippFindAttribute</a>(response, "printer-state", IPP_TAG_ENUM);
-</pre>
-
-<p>You can also walk the list of attributes with a simple <code>for</code> loop like this:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *response;
-<a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
-
-for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
-  if (ippGetName(attr) == NULL)
-    puts("--SEPARATOR--");
-  else
-    puts(ippGetName(attr));
-</pre>
-
-<p>The <code>for</code> loop approach is normally used when collecting attributes for multiple objects (jobs, printers, etc.) in a response. Attributes with <code>NULL</code> names indicate a separator between the attributes of each object. For example, the following code will list the jobs returned from our previous <a href='#get_jobs'><code>get_jobs</code></a> example code:</p>
-
-<pre class='example'>
-<a href='#ipp_t'>ipp_t</a> *response = <a href='#get_jobs'>get_jobs</a>();
-
-if (response != NULL)
-{
-  <a href='#ipp_attribute_t'>ipp_attribute_t</a> *attr;
-  const char *attrname;
-  int job_id = 0;
-  const char *job_name = NULL;
-  const char *job_originating_user_name = NULL;
-
-  puts("Job ID  Owner             Title");
-  puts("------  ----------------  ---------------------------------");
-
-  for (attr = <a href='#ippFirstAttribute'>ippFirstAttribute</a>(response); attr != NULL; attr = <a href='#ippNextAttribute'>ippNextAttribute</a>(response))
-  {
-   /* Attributes without names are separators between jobs */
-    attrname = ippGetName(attr);
-    if (attrname == NULL)
-    {
-      if (job_id > 0)
-      {
-        if (job_name == NULL)
-          job_name = "(withheld)";
-
-        if (job_originating_user_name == NULL)
-          job_originating_user_name = "(withheld)";
-
-        printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
-      }
-
-      job_id = 0;
-      job_name = NULL;
-      job_originating_user_name = NULL;
-      continue;
-    }
-    else if (!strcmp(attrname, "job-id") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_INTEGER)
-      job_id = ippGetInteger(attr, 0);
-    else if (!strcmp(attrname, "job-name") &amp;&amp; ippGetValueTag(attr) == IPP_TAG_NAME)
-      job_name = ippGetString(attr, 0, NULL);
-    else if (!strcmp(attrname, "job-originating-user-name") &amp;&amp;
-             ippGetValueTag(attr) == IPP_TAG_NAME)
-      job_originating_user_name = ippGetString(attr, 0, NULL);
-  }
-
-  if (job_id > 0)
-  {
-    if (job_name == NULL)
-      job_name = "(withheld)";
-
-    if (job_originating_user_name == NULL)
-      job_originating_user_name = "(withheld)";
-
-    printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
-  }
-}
-</pre>
-
-<h3><a name='CREATING_URI_STRINGS'>Creating URI Strings</a></h3>
-
-<p>To ensure proper encoding, the
-<a href='#httpAssembleURIf'><code>httpAssembleURIf</code></a> function must be
-used to format a "printer-uri" string for all printer-based requests:</p>
-
-<pre class='example'>
-const char *name = "Foo";
-char uri[1024];
-<a href='#ipp_t'>ipp_t</a> *request;
-
-<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
-                 ippPort(), "/printers/%s", name);
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
-</pre>
-
-<h3><a name='SENDING_REQUESTS_WITH_FILES'>Sending Requests with Files</a></h3>
-
-<p>The <a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> and
-<a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> functions are
-used for requests involving files. The
-<a href='#cupsDoFileRequest'><code>cupsDoFileRequest</code></a> function
-attaches the named file to a request and is typically used when sending a print
-file or changing a printer's PPD file:</p>
-
-<pre class='example'>
-const char *filename = "/usr/share/cups/data/testprint.ps";
-const char *name = "Foo";
-char uri[1024];
-char resource[1024];
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(IPP_PRINT_JOB);
-<a href='#ipp_t'>ipp_t</a> *response;
-
-/* Use httpAssembleURIf for the printer-uri string */
-<a href='#httpAssembleURIf'>httpAssembleURIf</a>(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, cupsServer(),
-                 ippPort(), "/printers/%s", name);
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-             NULL, cupsUser());
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
-             NULL, "testprint.ps");
-
-/* Use snprintf for the resource path */
-snprintf(resource, sizeof(resource), "/printers/%s", name);
-
-response = <a href='#cupsDoFileRequest'>cupsDoFileRequest</a>(CUPS_HTTP_DEFAULT, request, resource, filename);
-</pre>
-
-<p>The <a href='#cupsDoIORequest'><code>cupsDoIORequest</code></a> function
-optionally attaches a file to the request and optionally saves a file in the
-response from the server. It is used when using a pipe for the request
-attachment or when using a request that returns a file, currently only
-<code>CUPS_GET_DOCUMENT</code> and <code>CUPS_GET_PPD</code>. For example,
-the following code will download the PPD file for the sample HP LaserJet
-printer driver:</p>
-
-<pre class='example'>
-char tempfile[1024];
-int tempfd;
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
-<a href='#ipp_t'>ipp_t</a> *response;
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
-             NULL, "laserjet.ppd");
-
-tempfd = cupsTempFd(tempfile, sizeof(tempfile));
-
-response = <a href='#cupsDoIORequest'>cupsDoIORequest</a>(CUPS_HTTP_DEFAULT, request, "/", -1, tempfd);
-</pre>
-
-<p>The example passes <code>-1</code> for the input file descriptor to specify
-that no file is to be attached to the request. The PPD file attached to the
-response is written to the temporary file descriptor we created using the
-<code>cupsTempFd</code> function.</p>
-
-<h3><a name='ASYNCHRONOUS_REQUEST_PROCESSING'>Asynchronous Request Processing</a></h3>
-
-<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> and
-<a href='#cupsGetResponse'><code>cupsGetResponse</code></a> support
-asynchronous communications with the server. Unlike the other request
-functions, the IPP request is not automatically freed, so remember to
-free your request with the <a href='#ippDelete'><code>ippDelete</code></a>
-function.</p>
-
-<p>File data is attached to the request using the
-<a href='#cupsWriteRequestData'><code>cupsWriteRequestData</code></a>
-function, while file data returned from the server is read using the
-<a href='#cupsReadResponseData'><code>cupsReadResponseData</code></a>
-function. We can rewrite the previous <code>CUPS_GET_PPD</code> example
-to use the asynchronous functions quite easily:</p>
-
-<pre class='example'>
-char tempfile[1024];
-int tempfd;
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
-<a href='#ipp_t'>ipp_t</a> *response;
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
-             NULL, "laserjet.ppd");
-
-tempfd = cupsTempFd(tempfile, sizeof(tempfile));
-
-if (<a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/") == HTTP_CONTINUE)
-{
-  response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
-
-  if (response != NULL)
-  {
-    ssize_t bytes;
-    char buffer[8192];
-
-    while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
-      write(tempfd, buffer, bytes);
-  }
-}
-
-/* Free the request! */
-<a href='#ippDelete'>ippDelete</a>(request);
-</pre>
-
-<p>The <a href='#cupsSendRequest'><code>cupsSendRequest</code></a> function
-returns the initial HTTP request status, typically either
-<code>HTTP_CONTINUE</code> or <code>HTTP_UNAUTHORIZED</code>. The latter status
-is returned when the request requires authentication of some sort. The
-<a href='#cupsDoAuthentication'><code>cupsDoAuthentication</code></a> function
-must be called when your see <code>HTTP_UNAUTHORIZED</code> and the request
-re-sent. We can add authentication support to our example code by using a
-<code>do ... while</code> loop:</p>
-
-<pre class='example'>
-char tempfile[1024];
-int tempfd;
-<a href='#ipp_t'>ipp_t</a> *request = <a href='#ippNewRequest'>ippNewRequest</a>(CUPS_GET_PPD);
-<a href='#ipp_t'>ipp_t</a> *response;
-http_status_t status;
-
-<a href='#ippAddString'>ippAddString</a>(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name",
-             NULL, "laserjet.ppd");
-
-tempfd = cupsTempFd(tempfile, sizeof(tempfile));
-
-/* Loop for authentication */
-do
-{
-  status = <a href='#cupsSendRequest'>cupsSendRequest</a>(CUPS_HTTP_DEFAULT, request, "/");
-
-  if (status == HTTP_UNAUTHORIZED)
-  {
-    /* Try to authenticate, break out of the loop if that fails */
-    if (<a href='#cupsDoAuthentication'>cupsDoAuthentication</a>(CUPS_HTTP_DEFAULT, "POST", "/"))
-      break;
-  }
-}
-while (status != HTTP_CONTINUE &amp;&amp; status != HTTP_UNAUTHORIZED);
-
-if (status == HTTP_CONTINUE)
-{
-  response = <a href='#cupsGetResponse'>cupsGetResponse</a>(CUPS_HTTP_DEFAULT, "/");
-
-  if (response != NULL)
-  {
-    ssize_t bytes;
-    char buffer[8192];
-
-    while ((bytes = <a href='#cupsReadResponseData'>cupsReadResponseData</a>(CUPS_HTTP_DEFAULT, buffer, sizeof(buffer))) > 0)
-      write(tempfd, buffer, bytes);
-  }
-}
-
-/* Free the request! */
-<a href='#ippDelete'>ippDelete</a>(request);
-</pre>
diff --git a/cups/api-ppd.shtml b/cups/api-ppd.shtml
index 50c4850..4940946 100644
--- a/cups/api-ppd.shtml
+++ b/cups/api-ppd.shtml
@@ -1,7 +1,7 @@
 <!--
   PPD API introduction for CUPS.
 
-  Copyright 2007-2012 by Apple Inc.
+  Copyright 2007-2018 by Apple Inc.
   Copyright 1997-2006 by Easy Software Products, all rights reserved.
 
   These coded instructions, statements, and computer programs are the
@@ -13,7 +13,7 @@
 
 <h2 class='title'><a name='OVERVIEW'>Overview</a></h2>
 
-<blockquote>The PPD API is deprecated starting in CUPS 1.6/macOS 10.8. Please use the new Job Ticket APIs in the <a href="api-cups.html">CUPS API</a> documentation. These functions will be removed in a future release of CUPS.</blockquote>
+<blockquote>The PPD API is deprecated starting in CUPS 1.6/macOS 10.8. Please use the new Job Ticket APIs in the <a href="cupspm.html">CUPS API</a> documentation. These functions will be removed in a future release of CUPS.</blockquote>
 
 <p>The CUPS PPD API provides read-only access the data in PostScript Printer
 Description ("PPD") files which are used for all printers with a driver. With
diff --git a/cups/auth.c b/cups/auth.c
index 8348a2c..a1f5042 100644
--- a/cups/auth.c
+++ b/cups/auth.c
@@ -1,8 +1,8 @@
 /*
  * Authentication functions for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2007 by Easy Software Products.
  *
  * This file contains Kerberos support code, copyright 2006 by
  * Jelmer Vernooij.
@@ -23,11 +23,11 @@
 #include "cups-private.h"
 #include <fcntl.h>
 #include <sys/stat.h>
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 
 #if HAVE_AUTHORIZATION_H
 #  include <Security/Authorization.h>
@@ -47,6 +47,10 @@
  * Local functions...
  */
 
+static const char	*cups_auth_find(const char *www_authenticate, const char *scheme);
+static const char	*cups_auth_param(const char *scheme, const char *name, char *value, size_t valsize);
+static const char	*cups_auth_scheme(const char *www_authenticate, char *scheme, size_t schemesize);
+
 #ifdef HAVE_GSSAPI
 #  ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
 #    ifdef HAVE_GSS_GSSAPI_SPI_H
@@ -54,7 +58,7 @@
 #    else
 #      define GSS_AUTH_IDENTITY_TYPE_1 1
 #      define gss_acquire_cred_ex_f __ApplePrivate_gss_acquire_cred_ex_f
-typedef struct gss_auth_identity
+typedef struct gss_auth_identity /* @private@ */
 {
   uint32_t type;
   uint32_t flags;
@@ -112,10 +116,10 @@
     const char *resource)		/* I - Resource path */
 {
   const char	*password,		/* Password string */
-		*www_auth;		/* WWW-Authenticate header */
-  char		prompt[1024],		/* Prompt for user */
-		realm[HTTP_MAX_VALUE],	/* realm="xyz" string */
-		nonce[HTTP_MAX_VALUE];	/* nonce="xyz" string */
+		*www_auth,		/* WWW-Authenticate header */
+		*schemedata;		/* Scheme-specific data */
+  char		scheme[256],		/* Scheme name */
+		prompt[1024];		/* Prompt for user */
   int		localauth;		/* Local authentication result */
   _cups_globals_t *cg;			/* Global data */
 
@@ -163,122 +167,129 @@
   }
 
  /*
-  * Nope, see if we should retry the current username:password...
+  * Nope, loop through the authentication schemes to find the first we support.
   */
 
-  www_auth = http->fields[HTTP_FIELD_WWW_AUTHENTICATE];
+  www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
 
-  if ((http->digest_tries > 1 || !http->userpass[0]) &&
-      (!_cups_strncasecmp(www_auth, "Basic", 5) ||
-       !_cups_strncasecmp(www_auth, "Digest", 6)))
+  for (schemedata = cups_auth_scheme(www_auth, scheme, sizeof(scheme)); schemedata; schemedata = cups_auth_scheme(schemedata + strlen(scheme), scheme, sizeof(scheme)))
   {
    /*
-    * Nope - get a new password from the user...
+    * Check the scheme name...
     */
 
-    char default_username[HTTP_MAX_VALUE];
-					/* Default username */
-
-    cg = _cupsGlobals();
-
-    if (!cg->lang_default)
-      cg->lang_default = cupsLangDefault();
-
-    if (httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "username",
-                        default_username))
-      cupsSetUser(default_username);
-
-    snprintf(prompt, sizeof(prompt),
-             _cupsLangString(cg->lang_default, _("Password for %s on %s? ")),
-	     cupsUser(),
-	     http->hostname[0] == '/' ? "localhost" : http->hostname);
-
-    http->digest_tries  = _cups_strncasecmp(www_auth, "Digest", 6) != 0;
-    http->userpass[0]   = '\0';
-
-    if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
-    {
-      http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
-      return (-1);
-    }
-
-    snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(),
-             password);
-  }
-  else if (http->status == HTTP_STATUS_UNAUTHORIZED)
-    http->digest_tries ++;
-
-  if (http->status == HTTP_STATUS_UNAUTHORIZED && http->digest_tries >= 3)
-  {
-    DEBUG_printf(("1cupsDoAuthentication: Too many authentication tries (%d)",
-		  http->digest_tries));
-
-    http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
-    return (-1);
-  }
-
- /*
-  * Got a password; encode it for the server...
-  */
-
 #ifdef HAVE_GSSAPI
-  if (!_cups_strncasecmp(www_auth, "Negotiate", 9))
-  {
+    if (!_cups_strcasecmp(scheme, "Negotiate"))
+    {
+     /*
+      * Kerberos authentication...
+      */
+
+      if (_cupsSetNegotiateAuthString(http, method, resource))
+      {
+	http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+	return (-1);
+      }
+
+      break;
+    }
+    else
+#endif /* HAVE_GSSAPI */
+    if (_cups_strcasecmp(scheme, "Basic") && _cups_strcasecmp(scheme, "Digest"))
+      continue;				/* Not supported (yet) */
+
    /*
-    * Kerberos authentication...
+    * See if we should retry the current username:password...
     */
 
-    if (_cupsSetNegotiateAuthString(http, method, resource))
+    if ((http->digest_tries > 1 || !http->userpass[0]) && (!_cups_strcasecmp(scheme, "Basic") || (!_cups_strcasecmp(scheme, "Digest"))))
     {
+     /*
+      * Nope - get a new password from the user...
+      */
+
+      char default_username[HTTP_MAX_VALUE];
+					/* Default username */
+
+      cg = _cupsGlobals();
+
+      if (!cg->lang_default)
+	cg->lang_default = cupsLangDefault();
+
+      if (cups_auth_param(schemedata, "username", default_username, sizeof(default_username)))
+	cupsSetUser(default_username);
+
+      snprintf(prompt, sizeof(prompt), _cupsLangString(cg->lang_default, _("Password for %s on %s? ")), cupsUser(), http->hostname[0] == '/' ? "localhost" : http->hostname);
+
+      http->digest_tries  = _cups_strncasecmp(scheme, "Digest", 6) != 0;
+      http->userpass[0]   = '\0';
+
+      if ((password = cupsGetPassword2(prompt, http, method, resource)) == NULL)
+      {
+	http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+	return (-1);
+      }
+
+      snprintf(http->userpass, sizeof(http->userpass), "%s:%s", cupsUser(), password);
+    }
+    else if (http->status == HTTP_STATUS_UNAUTHORIZED)
+      http->digest_tries ++;
+
+    if (http->status == HTTP_STATUS_UNAUTHORIZED && http->digest_tries >= 3)
+    {
+      DEBUG_printf(("1cupsDoAuthentication: Too many authentication tries (%d)", http->digest_tries));
+
       http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
       return (-1);
     }
-  }
-  else
-#endif /* HAVE_GSSAPI */
-  if (!_cups_strncasecmp(www_auth, "Basic", 5))
-  {
+
    /*
-    * Basic authentication...
+    * Got a password; encode it for the server...
     */
 
-    char	encode[256];		/* Base64 buffer */
+    if (!_cups_strcasecmp(scheme, "Basic"))
+    {
+     /*
+      * Basic authentication...
+      */
 
+      char	encode[256];		/* Base64 buffer */
 
-    httpEncode64_2(encode, sizeof(encode), http->userpass,
-                   (int)strlen(http->userpass));
-    httpSetAuthString(http, "Basic", encode);
+      httpEncode64_2(encode, sizeof(encode), http->userpass, (int)strlen(http->userpass));
+      httpSetAuthString(http, "Basic", encode);
+      break;
+    }
+    else if (!_cups_strcasecmp(scheme, "Digest"))
+    {
+     /*
+      * Digest authentication...
+      */
+
+      char nonce[HTTP_MAX_VALUE];	/* nonce="xyz" string */
+
+      cups_auth_param(schemedata, "algorithm", http->algorithm, sizeof(http->algorithm));
+      cups_auth_param(schemedata, "opaque", http->opaque, sizeof(http->opaque));
+      cups_auth_param(schemedata, "nonce", nonce, sizeof(nonce));
+      cups_auth_param(schemedata, "realm", http->realm, sizeof(http->realm));
+
+      if (_httpSetDigestAuthString(http, nonce, method, resource))
+        break;
+    }
   }
-  else if (!_cups_strncasecmp(www_auth, "Digest", 6))
+
+  if (http->authstring)
   {
-   /*
-    * Digest authentication...
-    */
+    DEBUG_printf(("1cupsDoAuthentication: authstring=\"%s\"", http->authstring));
 
-    char	encode[33],		/* MD5 buffer */
-		digest[1024];		/* Digest auth data */
-
-    httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "realm", realm);
-    httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE, "nonce", nonce);
-
-    httpMD5(cupsUser(), realm, strchr(http->userpass, ':') + 1, encode);
-    httpMD5Final(nonce, method, resource, encode);
-    snprintf(digest, sizeof(digest),
-	     "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
-	     "response=\"%s\"", cupsUser(), realm, nonce, resource, encode);
-    httpSetAuthString(http, "Digest", digest);
+    return (0);
   }
   else
   {
-    DEBUG_printf(("1cupsDoAuthentication: Unknown auth type: \"%s\"",
-                  www_auth));
+    DEBUG_printf(("1cupsDoAuthentication: Unknown auth type: \"%s\"", www_auth));
     http->status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
+
     return (-1);
   }
-
-  DEBUG_printf(("1cupsDoAuthentication: authstring=\"%s\"", http->authstring));
-
-  return (0);
 }
 
 
@@ -336,7 +347,7 @@
 				      GSS_C_NO_BUFFER, &http->gssmech,
 				      &output_token, NULL, NULL);
 
-#ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
+#  ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
   if (major_status == GSS_S_NO_CRED)
   {
    /*
@@ -412,7 +423,7 @@
       }
     }
   }
-#endif /* HAVE_GSS_ACQUIRED_CRED_EX_F */
+#  endif /* HAVE_GSS_ACQUIRED_CRED_EX_F */
 
   if (GSS_ERROR(major_status))
   {
@@ -422,11 +433,11 @@
     return (-1);
   }
 
-#ifdef DEBUG
+#  ifdef DEBUG
   else if (major_status == GSS_S_CONTINUE_NEEDED)
     cups_gss_printf(major_status, minor_status,
 		    "_cupsSetNegotiateAuthString: Continuation needed!");
-#endif /* DEBUG */
+#  endif /* DEBUG */
 
   if (output_token.length > 0 && output_token.length <= 65536)
   {
@@ -464,8 +475,259 @@
 
   return (0);
 }
+#endif /* HAVE_GSSAPI */
 
 
+/*
+ * 'cups_auth_find()' - Find the named WWW-Authenticate scheme.
+ *
+ * The "www_authenticate" parameter points to the current position in the header.
+ *
+ * Returns @code NULL@ if the auth scheme is not present.
+ */
+
+static const char *				/* O - Start of matching scheme or @code NULL@ if not found */
+cups_auth_find(const char *www_authenticate,	/* I - Pointer into WWW-Authenticate header */
+               const char *scheme)		/* I - Authentication scheme */
+{
+  size_t	schemelen = strlen(scheme);	/* Length of scheme */
+
+
+  DEBUG_printf(("8cups_auth_find(www_authenticate=\"%s\", scheme=\"%s\"(%d))", www_authenticate, scheme, (int)schemelen));
+
+  while (*www_authenticate)
+  {
+   /*
+    * Skip leading whitespace and commas...
+    */
+
+    DEBUG_printf(("9cups_auth_find: Before whitespace: \"%s\"", www_authenticate));
+    while (isspace(*www_authenticate & 255) || *www_authenticate == ',')
+      www_authenticate ++;
+    DEBUG_printf(("9cups_auth_find: After whitespace: \"%s\"", www_authenticate));
+
+   /*
+    * See if this is "Scheme" followed by whitespace or the end of the string.
+    */
+
+    if (!strncmp(www_authenticate, scheme, schemelen) && (isspace(www_authenticate[schemelen] & 255) || www_authenticate[schemelen] == ',' || !www_authenticate[schemelen]))
+    {
+     /*
+      * Yes, this is the start of the scheme-specific information...
+      */
+
+      DEBUG_printf(("9cups_auth_find: Returning \"%s\".", www_authenticate));
+
+      return (www_authenticate);
+    }
+
+   /*
+    * Skip the scheme name or param="value" string...
+    */
+
+    while (!isspace(*www_authenticate & 255) && *www_authenticate)
+    {
+      if (*www_authenticate == '\"')
+      {
+       /*
+        * Skip quoted value...
+        */
+
+        www_authenticate ++;
+        while (*www_authenticate && *www_authenticate != '\"')
+          www_authenticate ++;
+
+        DEBUG_printf(("9cups_auth_find: After quoted: \"%s\"", www_authenticate));
+      }
+
+      www_authenticate ++;
+    }
+
+    DEBUG_printf(("9cups_auth_find: After skip: \"%s\"", www_authenticate));
+  }
+
+  DEBUG_puts("9cups_auth_find: Returning NULL.");
+
+  return (NULL);
+}
+
+
+/*
+ * 'cups_auth_param()' - Copy the value for the named authentication parameter,
+ *                       if present.
+ */
+
+static const char *				/* O - Parameter value or @code NULL@ if not present */
+cups_auth_param(const char *scheme,		/* I - Pointer to auth data */
+                const char *name,		/* I - Name of parameter */
+                char       *value,		/* I - Value buffer */
+                size_t     valsize)		/* I - Size of value buffer */
+{
+  char		*valptr = value,		/* Pointer into value buffer */
+		*valend = value + valsize - 1;	/* Pointer to end of buffer */
+  size_t	namelen = strlen(name);		/* Name length */
+  int		param;				/* Is this a parameter? */
+
+
+  DEBUG_printf(("8cups_auth_param(scheme=\"%s\", name=\"%s\", value=%p, valsize=%d)", scheme, name, (void *)value, (int)valsize));
+
+  while (!isspace(*scheme & 255) && *scheme)
+    scheme ++;
+
+  while (*scheme)
+  {
+    while (isspace(*scheme & 255) || *scheme == ',')
+      scheme ++;
+
+    if (!strncmp(scheme, name, namelen) && scheme[namelen] == '=')
+    {
+     /*
+      * Found the parameter, copy the value...
+      */
+
+      scheme += namelen + 1;
+      if (*scheme == '\"')
+      {
+        scheme ++;
+
+	while (*scheme && *scheme != '\"')
+	{
+	  if (valptr < valend)
+	    *valptr++ = *scheme;
+
+	  scheme ++;
+	}
+      }
+      else
+      {
+	while (*scheme && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~+/=", *scheme))
+	{
+	  if (valptr < valend)
+	    *valptr++ = *scheme;
+
+	  scheme ++;
+	}
+      }
+
+      *valptr = '\0';
+
+      DEBUG_printf(("9cups_auth_param: Returning \"%s\".", value));
+
+      return (value);
+    }
+
+   /*
+    * Skip the param=value string...
+    */
+
+    param = 0;
+
+    while (!isspace(*scheme & 255) && *scheme)
+    {
+      if (*scheme == '=')
+        param = 1;
+      else if (*scheme == '\"')
+      {
+       /*
+        * Skip quoted value...
+        */
+
+        scheme ++;
+        while (*scheme && *scheme != '\"')
+          scheme ++;
+      }
+
+      scheme ++;
+    }
+
+   /*
+    * If this wasn't a parameter, we are at the end of this scheme's
+    * parameters...
+    */
+
+    if (!param)
+      break;
+  }
+
+  *value = '\0';
+
+  DEBUG_puts("9cups_auth_param: Returning NULL.");
+
+  return (NULL);
+}
+
+
+/*
+ * 'cups_auth_scheme()' - Get the (next) WWW-Authenticate scheme.
+ *
+ * The "www_authenticate" parameter points to the current position in the header.
+ *
+ * Returns @code NULL@ if there are no (more) auth schemes present.
+ */
+
+static const char *				/* O - Start of scheme or @code NULL@ if not found */
+cups_auth_scheme(const char *www_authenticate,	/* I - Pointer into WWW-Authenticate header */
+		 char       *scheme,		/* I - Scheme name buffer */
+		 size_t     schemesize)		/* I - Size of buffer */
+{
+  const char	*start;				/* Start of scheme data */
+  char		*sptr = scheme,			/* Pointer into scheme buffer */
+		*send = scheme + schemesize - 1;/* End of scheme buffer */
+  int		param;				/* Is this a parameter? */
+
+
+  DEBUG_printf(("8cups_auth_scheme(www_authenticate=\"%s\", scheme=%p, schemesize=%d)", www_authenticate, (void *)scheme, (int)schemesize));
+
+  while (*www_authenticate)
+  {
+   /*
+    * Skip leading whitespace and commas...
+    */
+
+    while (isspace(*www_authenticate & 255) || *www_authenticate == ',')
+      www_authenticate ++;
+
+   /*
+    * Parse the scheme name or param="value" string...
+    */
+
+    for (sptr = scheme, start = www_authenticate, param = 0; *www_authenticate && *www_authenticate != ',' && !isspace(*www_authenticate & 255); www_authenticate ++)
+    {
+      if (*www_authenticate == '=')
+        param = 1;
+      else if (!param && sptr < send)
+        *sptr++ = *www_authenticate;
+      else if (*www_authenticate == '\"')
+      {
+       /*
+        * Skip quoted value...
+        */
+
+        www_authenticate ++;
+        while (*www_authenticate && *www_authenticate != '\"')
+          www_authenticate ++;
+      }
+    }
+
+    if (sptr > scheme && !param)
+    {
+      *sptr = '\0';
+
+      DEBUG_printf(("9cups_auth_scheme: Returning \"%s\".", start));
+
+      return (start);
+    }
+  }
+
+  *scheme = '\0';
+
+  DEBUG_puts("9cups_auth_scheme: Returning NULL.");
+
+  return (NULL);
+}
+
+
+#ifdef HAVE_GSSAPI
 #  ifdef HAVE_GSS_ACQUIRE_CRED_EX_F
 /*
  * 'cups_gss_acquire()' - Kerberos credentials callback.
@@ -639,9 +901,9 @@
 					/*    -1 error */
 cups_local_auth(http_t *http)		/* I - HTTP connection to server */
 {
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
  /*
-  * Currently WIN32 and OS-2 do not support the CUPS server...
+  * Currently _WIN32 and OS-2 do not support the CUPS server...
   */
 
   return (1);
@@ -650,6 +912,8 @@
   FILE			*fp;		/* Certificate file */
   char			trc[16],	/* Try Root Certificate parameter */
 			filename[1024];	/* Certificate filename */
+  const char		*www_auth,	/* WWW-Authenticate header */
+			*schemedata;	/* Data for the named auth scheme */
   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
 #  if defined(HAVE_AUTHORIZATION_H)
   OSStatus		status;		/* Status */
@@ -668,13 +932,14 @@
   * See if we are accessing localhost...
   */
 
-  if (!httpAddrLocalhost(http->hostaddr) &&
-      _cups_strcasecmp(http->hostname, "localhost") != 0)
+  if (!httpAddrLocalhost(http->hostaddr) && _cups_strcasecmp(http->hostname, "localhost") != 0)
   {
     DEBUG_puts("8cups_local_auth: Not a local connection!");
     return (1);
   }
 
+  www_auth = httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE);
+
 #  if defined(HAVE_AUTHORIZATION_H)
  /*
   * Delete any previous authorization reference...
@@ -686,12 +951,9 @@
     http->auth_ref = NULL;
   }
 
-  if (!getenv("GATEWAY_INTERFACE") &&
-      httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
-		       auth_key, sizeof(auth_key)))
+  if (!getenv("GATEWAY_INTERFACE") && (schemedata = cups_auth_find(www_auth, "AuthRef")) != NULL && cups_auth_param(schemedata, "key", auth_key, sizeof(auth_key)))
   {
-    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
-				 kAuthorizationFlagDefaults, &http->auth_ref);
+    status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &http->auth_ref);
     if (status != errAuthorizationSuccess)
     {
       DEBUG_printf(("8cups_local_auth: AuthorizationCreate() returned %d (%s)",
@@ -745,6 +1007,11 @@
   }
 #  endif /* HAVE_AUTHORIZATION_H */
 
+#  ifdef HAVE_GSSAPI
+  if (cups_auth_find(www_auth, "Negotiate"))
+    return (1);
+#  endif /* HAVE_GSSAPI */
+
 #  if defined(SO_PEERCRED) && defined(AF_LOCAL)
  /*
   * See if we can authenticate using the peer credentials provided over a
@@ -752,16 +1019,9 @@
   * information...
   */
 
-  if (
-#    ifdef HAVE_GSSAPI
-      _cups_strncasecmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9) &&
-#    endif /* HAVE_GSSAPI */
-#    ifdef HAVE_AUTHORIZATION_H
-      !httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
-		        auth_key, sizeof(auth_key)) &&
-#    endif /* HAVE_AUTHORIZATION_H */
-      http->hostaddr->addr.sa_family == AF_LOCAL &&
-      !getenv("GATEWAY_INTERFACE"))	/* Not via CGI programs... */
+  if (http->hostaddr->addr.sa_family == AF_LOCAL &&
+      !getenv("GATEWAY_INTERFACE") &&	/* Not via CGI programs... */
+      cups_auth_find(www_auth, "PeerCred"))
   {
    /*
     * Verify that the current cupsUser() matches the current UID...
@@ -784,6 +1044,9 @@
   }
 #  endif /* SO_PEERCRED && AF_LOCAL */
 
+  if ((schemedata = cups_auth_find(www_auth, "Local")) == NULL)
+    return (1);
+
  /*
   * Try opening a certificate file for this PID.  If that fails,
   * try the root certificate...
@@ -797,33 +1060,9 @@
     * No certificate for this PID; see if we can get the root certificate...
     */
 
-    DEBUG_printf(("9cups_local_auth: Unable to open file %s: %s",
-                  filename, strerror(errno)));
+    DEBUG_printf(("9cups_local_auth: Unable to open file \"%s\": %s", filename, strerror(errno)));
 
-#  ifdef HAVE_GSSAPI
-    if (!_cups_strncasecmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE], "Negotiate", 9))
-    {
-     /*
-      * Kerberos required, don't try the root certificate...
-      */
-
-      return (1);
-    }
-#  endif /* HAVE_GSSAPI */
-
-#  ifdef HAVE_AUTHORIZATION_H
-    if (httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "authkey",
-		         auth_key, sizeof(auth_key)))
-    {
-     /*
-      * Don't use the root certificate as a replacement for an authkey...
-      */
-
-      return (1);
-    }
-#  endif /* HAVE_AUTHORIZATION_H */
-    if (!httpGetSubField2(http, HTTP_FIELD_WWW_AUTHENTICATE, "trc", trc,
-	                  sizeof(trc)))
+    if (!cups_auth_param(schemedata, "trc", trc, sizeof(trc)))
     {
      /*
       * Scheduler doesn't want us to use the root certificate...
@@ -833,7 +1072,8 @@
     }
 
     snprintf(filename, sizeof(filename), "%s/certs/0", cg->cups_statedir);
-    fp = fopen(filename, "r");
+    if ((fp = fopen(filename, "r")) == NULL)
+      DEBUG_printf(("9cups_local_auth: Unable to open file \"%s\": %s", filename, strerror(errno)));
   }
 
   if (fp)
@@ -864,5 +1104,5 @@
   }
 
   return (1);
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 }
diff --git a/cups/backchannel.c b/cups/backchannel.c
index e804d45..bca04dc 100644
--- a/cups/backchannel.c
+++ b/cups/backchannel.c
@@ -19,12 +19,12 @@
 
 #include "cups.h"
 #include <errno.h>
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #  include <fcntl.h>
 #else
 #  include <sys/time.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -77,11 +77,11 @@
   * Read bytes from the pipe...
   */
 
-#ifdef WIN32
+#ifdef _WIN32
   return ((ssize_t)_read(3, buffer, (unsigned)bytes));
 #else
   return (read(3, buffer, bytes));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -139,11 +139,11 @@
     * Write bytes to the pipe...
     */
 
-#ifdef WIN32
+#ifdef _WIN32
     count = (ssize_t)_write(3, buffer, (unsigned)(bytes - total));
 #else
     count = write(3, buffer, bytes - total);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
     if (count < 0)
     {
diff --git a/cups/cups-private.h b/cups/cups-private.h
index 264fd01..6fd66a9 100644
--- a/cups/cups-private.h
+++ b/cups/cups-private.h
@@ -1,7 +1,7 @@
 /*
  * Private definitions for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -105,7 +105,7 @@
   int			need_res_init;	/* Need to reinitialize resolver? */
 
   /* ipp.c */
-  ipp_uchar_t		ipp_date[11];	/* RFC-1903 date/time data */
+  ipp_uchar_t		ipp_date[11];	/* RFC-2579 date/time data */
   _cups_buffer_t	*cups_buffers;	/* Buffer list */
 
   /* ipp-support.c */
diff --git a/cups/cups.h b/cups/cups.h
index 3205dd8..cf3be12 100644
--- a/cups/cups.h
+++ b/cups/cups.h
@@ -1,7 +1,7 @@
 /*
  * API definitions for CUPS.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -21,12 +21,12 @@
  */
 
 #  include <sys/types.h>
-#  if defined(WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
+#  if defined(_WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
 #    define __CUPS_SSIZE_T_DEFINED
 #    include <stddef.h>
 /* Windows does not support the ssize_t type, so map it to long... */
 typedef long ssize_t;			/* @private@ */
-#  endif /* WIN32 && !__CUPS_SSIZE_T_DEFINED */
+#  endif /* _WIN32 && !__CUPS_SSIZE_T_DEFINED */
 
 #  include "file.h"
 #  include "ipp.h"
@@ -47,10 +47,10 @@
  * Constants...
  */
 
-#  define CUPS_VERSION			2.0203
+#  define CUPS_VERSION			2.0209
 #  define CUPS_VERSION_MAJOR		2
 #  define CUPS_VERSION_MINOR		2
-#  define CUPS_VERSION_PATCH		3
+#  define CUPS_VERSION_PATCH		9
 
 #  define CUPS_BC_FD			3
 					/* Back-channel file descriptor for
@@ -78,7 +78,7 @@
 #  define CUPS_DEST_FLAGS_NONE		0x00
 					/* No flags are set */
 #  define CUPS_DEST_FLAGS_UNCONNECTED	0x01
-					/* There is not connection */
+					/* There is no connection */
 #  define CUPS_DEST_FLAGS_MORE		0x02
 					/* There are more destinations */
 #  define CUPS_DEST_FLAGS_REMOVED	0x04
@@ -92,6 +92,8 @@
 					/* A connection is being established */
 #  define CUPS_DEST_FLAGS_CANCELED	0x40
 					/* Operation was canceled */
+#  define CUPS_DEST_FLAGS_DEVICE        0x80
+                                        /* For @link cupsConnectDest@: Connect to device */
 
 /* Flags for cupsGetDestMediaByName/Size */
 #  define CUPS_MEDIA_FLAGS_DEFAULT 	0x00
@@ -207,38 +209,37 @@
   CUPS_PRINTER_REMOTE = 0x0002,		/* Remote printer or class */
   CUPS_PRINTER_BW = 0x0004,		/* Can do B&W printing */
   CUPS_PRINTER_COLOR = 0x0008,		/* Can do color printing */
-  CUPS_PRINTER_DUPLEX = 0x0010,		/* Can do duplexing */
+  CUPS_PRINTER_DUPLEX = 0x0010,		/* Can do two-sided printing */
   CUPS_PRINTER_STAPLE = 0x0020,		/* Can staple output */
-  CUPS_PRINTER_COPIES = 0x0040,		/* Can do copies */
-  CUPS_PRINTER_COLLATE = 0x0080,	/* Can collage copies */
+  CUPS_PRINTER_COPIES = 0x0040,		/* Can do copies in hardware */
+  CUPS_PRINTER_COLLATE = 0x0080,	/* Can quickly collate copies */
   CUPS_PRINTER_PUNCH = 0x0100,		/* Can punch output */
   CUPS_PRINTER_COVER = 0x0200,		/* Can cover output */
   CUPS_PRINTER_BIND = 0x0400,		/* Can bind output */
   CUPS_PRINTER_SORT = 0x0800,		/* Can sort output */
-  CUPS_PRINTER_SMALL = 0x1000,		/* Can do Letter/Legal/A4 */
-  CUPS_PRINTER_MEDIUM = 0x2000,		/* Can do Tabloid/B/C/A3/A2 */
-  CUPS_PRINTER_LARGE = 0x4000,		/* Can do D/E/A1/A0 */
-  CUPS_PRINTER_VARIABLE = 0x8000,	/* Can do variable sizes */
+  CUPS_PRINTER_SMALL = 0x1000,		/* Can print on Letter/Legal/A4-size media */
+  CUPS_PRINTER_MEDIUM = 0x2000,		/* Can print on Tabloid/B/C/A3/A2-size media */
+  CUPS_PRINTER_LARGE = 0x4000,		/* Can print on D/E/A1/A0-size media */
+  CUPS_PRINTER_VARIABLE = 0x8000,	/* Can print on rolls and custom-size media */
   CUPS_PRINTER_IMPLICIT = 0x10000,	/* Implicit class @private@
 					 * @since Deprecated@ */
   CUPS_PRINTER_DEFAULT = 0x20000,	/* Default printer on network */
   CUPS_PRINTER_FAX = 0x40000,		/* Fax queue */
   CUPS_PRINTER_REJECTING = 0x80000,	/* Printer is rejecting jobs */
   CUPS_PRINTER_DELETE = 0x100000,	/* Delete printer
-					 * @since CUPS 1.2/macOS 10.5@ */
+					 * @deprecated@ @exclude all@ */
   CUPS_PRINTER_NOT_SHARED = 0x200000,	/* Printer is not shared
 					 * @since CUPS 1.2/macOS 10.5@ */
   CUPS_PRINTER_AUTHENTICATED = 0x400000,/* Printer requires authentication
 					 * @since CUPS 1.2/macOS 10.5@ */
   CUPS_PRINTER_COMMANDS = 0x800000,	/* Printer supports maintenance commands
 					 * @since CUPS 1.2/macOS 10.5@ */
-  CUPS_PRINTER_DISCOVERED = 0x1000000,	/* Printer was automatically discovered
-					 * and added @private@ */
+  CUPS_PRINTER_DISCOVERED = 0x1000000,	/* Printer was discovered @since CUPS 1.2/macOS 10.5@ */
   CUPS_PRINTER_SCANNER = 0x2000000,	/* Scanner-only device
-					 * @since CUPS 1.4/macOS 10.6@ */
+					 * @since CUPS 1.4/macOS 10.6@ @private@ */
   CUPS_PRINTER_MFP = 0x4000000,		/* Printer with scanning capabilities
-					 * @since CUPS 1.4/macOS 10.6@ */
-  CUPS_PRINTER_3D = 0x8000000,		/* Printer with 3D capabilities @private@ */
+					 * @since CUPS 1.4/macOS 10.6@ @private@ */
+  CUPS_PRINTER_3D = 0x8000000,		/* Printer with 3D capabilities @exclude all@ @private@ */
   CUPS_PRINTER_OPTIONS = 0x6fffc	/* ~(CLASS | REMOTE | IMPLICIT |
 					 * DEFAULT | FAX | REJECTING | DELETE |
 					 * NOT_SHARED | AUTHENTICATED |
@@ -269,7 +270,7 @@
   int		id;			/* The job ID */
   char		*dest;			/* Printer or class name */
   char		*title;			/* Title/job name */
-  char		*user;			/* User the submitted the job */
+  char		*user;			/* User that submitted the job */
   char		*format;		/* Document format */
   ipp_jstate_t	state;			/* Job state */
   int		size;			/* Size in kilobytes */
@@ -309,11 +310,12 @@
 #  ifdef __BLOCKS__
 typedef int (^cups_dest_block_t)(unsigned flags, cups_dest_t *dest);
 			      		/* Destination enumeration block
-					 * @since CUPS 1.6/macOS 10.8@ */
+					 * @since CUPS 1.6/macOS 10.8@
+                                         * @exclude all@ */
 #  endif /* __BLOCKS__ */
 
 typedef const char *(*cups_password_cb_t)(const char *prompt);
-					/* Password callback */
+					/* Password callback @exclude all@ */
 
 typedef const char *(*cups_password_cb2_t)(const char *prompt, http_t *http,
 					   const char *method,
@@ -340,11 +342,11 @@
 			               const char *resource);
 extern http_encryption_t cupsEncryption(void);
 extern void		cupsFreeJobs(int num_jobs, cups_job_t *jobs);
-extern int		cupsGetClasses(char ***classes) _CUPS_DEPRECATED_MSG("Use cupsGetDests instead.");
+extern int		cupsGetClasses(char ***classes) _CUPS_DEPRECATED_MSG("Use cupsEnumDests instead.");
 extern const char	*cupsGetDefault(void);
 extern int		cupsGetJobs(cups_job_t **jobs, const char *name,
 			            int myjobs, int whichjobs);
-extern int		cupsGetPrinters(char ***printers) _CUPS_DEPRECATED_MSG("Use cupsGetDests instead.");
+extern int		cupsGetPrinters(char ***printers) _CUPS_DEPRECATED_MSG("Use cupsEnumDests instead.");
 extern ipp_status_t	cupsLastError(void);
 extern int		cupsPrintFile(const char *name, const char *filename,
 			              const char *title, int num_options,
@@ -600,6 +602,13 @@
 /* New in CUPS 2.2/macOS 10.12 */
 extern ssize_t		cupsHashData(const char *algorithm, const void *data, size_t datalen, unsigned char *hash, size_t hashsize) _CUPS_API_2_2;
 
+/* New in CUPS 2.2.4 */
+extern int		cupsAddIntegerOption(const char *name, int value, int num_options, cups_option_t **options) _CUPS_API_2_2_4;
+extern int		cupsGetIntegerOption(const char *name, int num_options, cups_option_t *options) _CUPS_API_2_2_4;
+
+/* New in CUPS 2.2.7 */
+extern const char	*cupsHashString(const unsigned char *hash, size_t hashsize, char *buffer, size_t bufsize) _CUPS_API_2_2_7;
+
 #  ifdef __cplusplus
 }
 #  endif /* __cplusplus */
diff --git a/cups/cupspm-icon.png b/cups/cupspm-icon.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cups/cupspm-icon.png
diff --git a/cups/cupspm.md b/cups/cupspm.md
new file mode 100644
index 0000000..cb381ac
--- /dev/null
+++ b/cups/cupspm.md
@@ -0,0 +1,994 @@
+---
+title: CUPS Programming Manual
+author: Michael R Sweet
+copyright: Copyright © 2007-2017 by Apple Inc. All Rights Reserved.
+version: 2.2.5
+...
+
+> Please [file issues on Github](https://github.com/apple/cups/issues) to
+> provide feedback on this document.
+
+
+# Introduction
+
+CUPS provides the "cups" library to talk to the different parts of CUPS and with
+Internet Printing Protocol (IPP) printers. The "cups" library functions are
+accessed by including the `<cups/cups.h>` header.
+
+CUPS is based on the Internet Printing Protocol ("IPP"), which allows clients
+(applications) to communicate with a server (the scheduler, printers, etc.) to
+get a list of destinations, send print jobs, and so forth.  You identify which
+server you want to communicate with using a pointer to the opaque structure
+`http_t`.  The `CUPS_HTTP_DEFAULT` constant can be used when you want to talk to
+the CUPS scheduler.
+
+
+## Guidelines
+
+When writing software that uses the "cups" library:
+
+- Do not use undocumented or deprecated APIs,
+- Do not rely on pre-configured printers,
+- Do not assume that printers support specific features or formats, and
+- Do not rely on implementation details (PPDs, etc.)
+
+CUPS is designed to insulate users and developers from the implementation
+details of printers and file formats.  The goal is to allow an application to
+supply a print file in a standard format with the user intent ("print four
+copies, two-sided on A4 media, and staple each copy") and have the printing
+system manage the printer communication and format conversion needed.
+
+Similarly, printer and job management applications can use standard query
+operations to obtain the status information in a common, generic form and use
+standard management operations to control the state of those printers and jobs.
+
+
+## Terms Used in This Document
+
+A *Destination* is a printer or print queue that accepts print jobs.  A
+*Print Job* is one or more documents that are processed by a destination
+using options supplied when creating the job.  A *Document* is a file (JPEG
+image, PDF file, etc.) suitable for printing.  An *Option* controls some aspect
+of printing, such as the media used. *Media* is the sheets or roll that is
+printed on.  An *Attribute* is an option encoded for an Internet Printing
+Protocol (IPP) request.
+
+
+## Compiling Programs That Use the CUPS API
+
+The CUPS libraries can be used from any C, C++, or Objective C program.
+The method of compiling against the libraries varies depending on the
+operating system and installation of CUPS. The following sections show how
+to compile a simple program (shown below) in two common environments.
+
+The following simple program lists the available destinations:
+
+    #include <stdio.h>
+    #include <cups/cups.h>
+
+    int print_dest(void *user_data, unsigned flags, cups_dest_t *dest)
+    {
+      if (dest->instance)
+        printf("%s/%s\n", dest->name, dest->instance);
+      else
+        puts(dest->name);
+
+      return (1);
+    }
+
+    int main(void)
+    {
+      cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0, print_dest, NULL);
+
+      return (0);
+    }
+
+
+### Compiling with Xcode
+
+In Xcode, choose *New Project...* from the *File* menu (or press SHIFT+CMD+N),
+then select the *Command Line Tool* under the macOS Application project type.
+Click *Next* and enter a name for the project, for example "firstcups".  Click
+*Next* and choose a project directory. The click *Next* to create the project.
+
+In the project window, click on the *Build Phases* group and expand the
+*Link Binary with Libraries* section. Click *+*, type "libcups" to show the
+library, and then double-click on `libcups.tbd`.
+
+Finally, click on the `main.c` file in the sidebar and copy the example program
+to the file.  Build and run (CMD+R) to see the list of destinations.
+
+
+### Compiling with GCC
+
+From the command-line, create a file called `sample.c` using your favorite
+editor, copy the example to this file, and save.  Then run the following command
+to compile it with GCC and run it:
+
+    gcc -o simple `cups-config --cflags` simple.c `cups-config --libs`
+    ./simple
+
+The `cups-config` command provides the compiler flags (`cups-config --cflags`)
+and libraries (`cups-config --libs`) needed for the local system.
+
+
+# Working with Destinations
+
+Destinations, which in CUPS represent individual printers or classes
+(collections or pools) of printers, are represented by the `cups_dest_t`
+structure which includes the name \(`name`), instance \(`instance`, saved
+options/settings), whether the destination is the default for the user
+\(`is_default`), and the options and basic information associated with that
+destination \(`num_options` and `options`).
+
+Historically destinations have been manually maintained by the administrator of
+a system or network, but CUPS also supports dynamic discovery of destinations on
+the current network.
+
+
+## Finding Available Destinations
+
+The `cupsEnumDests` function finds all of the available destinations:
+
+     int
+     cupsEnumDests(unsigned flags, int msec, int *cancel,
+                   cups_ptype_t type, cups_ptype_t mask,
+                   cups_dest_cb_t cb, void *user_data)
+
+The `flags` argument specifies enumeration options, which at present must be
+`CUPS_DEST_FLAGS_NONE`.
+
+The `msec` argument specifies the maximum amount of time that should be used for
+enumeration in milliseconds - interactive applications should keep this value to
+5000 or less when run on the main thread.
+
+The `cancel` argument points to an integer variable that, when set to a non-zero
+value, will cause enumeration to stop as soon as possible.  It can be `NULL` if
+not needed.
+
+The `type` and `mask` arguments are bitfields that allow the caller to filter
+the destinations based on categories and/or capabilities.  The destination's
+"printer-type" value is masked by the `mask` value and compared to the `type`
+value when filtering.  For example, to only enumerate destinations that are
+hosted on the local system, pass `CUPS_PRINTER_LOCAL` for the `type` argument
+and `CUPS_PRINTER_DISCOVERED` for the `mask` argument.  The following constants
+can be used for filtering:
+
+- `CUPS_PRINTER_CLASS`: A collection of destinations.
+- `CUPS_PRINTER_FAX`: A facsimile device.
+- `CUPS_PRINTER_LOCAL`: A local printer or class.  This constant has the value 0
+  (no bits set) and is only used for the `type` argument and is paired with the
+  `CUPS_PRINTER_REMOTE` or `CUPS_PRINTER_DISCOVERED` constant passed in the
+  `mask` argument.
+- `CUPS_PRINTER_REMOTE`: A remote (shared) printer or class.
+- `CUPS_PRINTER_DISCOVERED`: An available network printer or class.
+- `CUPS_PRINTER_BW`: Can do B&W printing.
+- `CUPS_PRINTER_COLOR`: Can do color printing.
+- `CUPS_PRINTER_DUPLEX`: Can do two-sided printing.
+- `CUPS_PRINTER_STAPLE`: Can staple output.
+- `CUPS_PRINTER_COLLATE`: Can quickly collate copies.
+- `CUPS_PRINTER_PUNCH`: Can punch output.
+- `CUPS_PRINTER_COVER`: Can cover output.
+- `CUPS_PRINTER_BIND`: Can bind output.
+- `CUPS_PRINTER_SORT`: Can sort output (mailboxes, etc.)
+- `CUPS_PRINTER_SMALL`: Can print on Letter/Legal/A4-size media.
+- `CUPS_PRINTER_MEDIUM`: Can print on Tabloid/B/C/A3/A2-size media.
+- `CUPS_PRINTER_LARGE`: Can print on D/E/A1/A0-size media.
+- `CUPS_PRINTER_VARIABLE`: Can print on rolls and custom-size media.
+
+The `cb` argument specifies a function to call for every destination that is
+found:
+
+    typedef int (*cups_dest_cb_t)(void *user_data,
+                                  unsigned flags,
+                                  cups_dest_t *dest);
+
+The callback function receives a copy of the `user_data` argument along with a
+bitfield \(`flags`) and the destination that was found.  The `flags` argument
+can have any of the following constant (bit) values set:
+
+- `CUPS_DEST_FLAGS_MORE`: There are more destinations coming.
+- `CUPS_DEST_FLAGS_REMOVED`: The destination has gone away and should be removed
+  from the list of destinations a user can select.
+- `CUPS_DEST_FLAGS_ERROR`: An error occurred.  The reason for the error can be
+  found by calling the `cupsLastError` and/or `cupsLastErrorString` functions.
+
+The callback function returns 0 to stop enumeration or 1 to continue.
+
+> Note that the callback function will likely be called multiple times for the
+> same destination, so it is up to the caller to suppress any duplicate
+> destinations.
+
+The following example shows how to use `cupsEnumDests` to get a filtered array
+of destinations:
+
+    typedef struct
+    {
+      int num_dests;
+      cups_dest_t *dests;
+    } my_user_data_t;
+
+    int
+    my_dest_cb(my_user_data_t *user_data, unsigned flags,
+               cups_dest_t *dest)
+    {
+      if (flags & CUPS_DEST_FLAGS_REMOVED)
+      {
+       /*
+        * Remove destination from array...
+        */
+
+        user_data->num_dests =
+            cupsRemoveDest(dest->name, dest->instance,
+                           user_data->num_dests,
+                           &(user_data->dests));
+      }
+      else
+      {
+       /*
+        * Add destination to array...
+        */
+
+        user_data->num_dests =
+            cupsCopyDest(dest, user_data->num_dests,
+                         &(user_data->dests));
+      }
+
+      return (1);
+    }
+
+    int
+    my_get_dests(cups_ptype_t type, cups_ptype_t mask,
+                 cups_dest_t **dests)
+    {
+      my_user_data_t user_data = { 0, NULL };
+
+      if (!cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, type,
+                         mask, (cups_dest_cb_t)my_dest_cb,
+                         &user_data))
+      {
+       /*
+        * An error occurred, free all of the destinations and
+        * return...
+        */
+
+        cupsFreeDests(user_data.num_dests, user_dasta.dests);
+
+        *dests = NULL;
+
+        return (0);
+      }
+
+     /*
+      * Return the destination array...
+      */
+
+      *dests = user_data.dests;
+
+      return (user_data.num_dests);
+    }
+
+
+## Basic Destination Information
+
+The `num_options` and `options` members of the `cups_dest_t` structure provide
+basic attributes about the destination in addition to the user default options
+and values for that destination.  The following names are predefined for various
+destination attributes:
+
+- "auth-info-required": The type of authentication required for printing to this
+  destination: "none", "username,password", "domain,username,password", or
+  "negotiate" (Kerberos).
+- "printer-info": The human-readable description of the destination such as "My
+  Laser Printer".
+- "printer-is-accepting-jobs": "true" if the destination is accepting new jobs,
+  "false" otherwise.
+- "printer-is-shared": "true" if the destination is being shared with other
+  computers, "false" otherwise.
+- "printer-location": The human-readable location of the destination such as
+  "Lab 4".
+- "printer-make-and-model": The human-readable make and model of the destination
+  such as "ExampleCorp LaserPrinter 4000 Series".
+- "printer-state": "3" if the destination is idle, "4" if the destination is
+  printing a job, and "5" if the destination is stopped.
+- "printer-state-change-time": The UNIX time when the destination entered the
+  current state.
+- "printer-state-reasons": Additional comma-delimited state keywords for the
+  destination such as "media-tray-empty-error" and "toner-low-warning".
+- "printer-type": The `cups_ptype_t` value associated with the destination.
+- "printer-uri-supported": The URI associated with the destination; if not set,
+  this destination was discovered but is not yet setup as a local printer.
+
+Use the `cupsGetOption` function to retrieve the value.  For example, the
+following code gets the make and model of a destination:
+
+    const char *model = cupsGetOption("printer-make-and-model",
+                                      dest->num_options,
+                                      dest->options);
+
+
+## Detailed Destination Information
+
+Once a destination has been chosen, the `cupsCopyDestInfo` function can be used
+to gather detailed information about the destination:
+
+    cups_dinfo_t *
+    cupsCopyDestInfo(http_t *http, cups_dest_t *dest);
+
+The `http` argument specifies a connection to the CUPS scheduler and is
+typically the constant `CUPS_HTTP_DEFAULT`.  The `dest` argument specifies the
+destination to query.
+
+The `cups_dinfo_t` structure that is returned contains a snapshot of the
+supported options and their supported, ready, and default values.  It also can
+report constraints between different options and values, and recommend changes
+to resolve those constraints.
+
+
+### Getting Supported Options and Values
+
+The `cupsCheckDestSupported` function can be used to test whether a particular
+option or option and value is supported:
+
+    int
+    cupsCheckDestSupported(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *info,
+                           const char *option,
+                           const char *value);
+
+The `option` argument specifies the name of the option to check.  The following
+constants can be used to check the various standard options:
+
+- `CUPS_COPIES`: Controls the number of copies that are produced.
+- `CUPS_FINISHINGS`: A comma-delimited list of integer constants that control
+  the finishing processes that are applied to the job, including stapling,
+  punching, and folding.
+- `CUPS_MEDIA`: Controls the media size that is used, typically one of the
+  following: `CUPS_MEDIA_3X5`, `CUPS_MEDIA_4X6`, `CUPS_MEDIA_5X7`,
+  `CUPS_MEDIA_8X10`, `CUPS_MEDIA_A3`, `CUPS_MEDIA_A4`, `CUPS_MEDIA_A5`,
+  `CUPS_MEDIA_A6`, `CUPS_MEDIA_ENV10`, `CUPS_MEDIA_ENVDL`, `CUPS_MEDIA_LEGAL`,
+  `CUPS_MEDIA_LETTER`, `CUPS_MEDIA_PHOTO_L`, `CUPS_MEDIA_SUPERBA3`, or
+  `CUPS_MEDIA_TABLOID`.
+- `CUPS_MEDIA_SOURCE`: Controls where the media is pulled from, typically either
+  `CUPS_MEDIA_SOURCE_AUTO` or `CUPS_MEDIA_SOURCE_MANUAL`.
+- `CUPS_MEDIA_TYPE`: Controls the type of media that is used, typically one of
+  the following: `CUPS_MEDIA_TYPE_AUTO`, `CUPS_MEDIA_TYPE_ENVELOPE`,
+  `CUPS_MEDIA_TYPE_LABELS`, `CUPS_MEDIA_TYPE_LETTERHEAD`,
+  `CUPS_MEDIA_TYPE_PHOTO`, `CUPS_MEDIA_TYPE_PHOTO_GLOSSY`,
+  `CUPS_MEDIA_TYPE_PHOTO_MATTE`, `CUPS_MEDIA_TYPE_PLAIN`, or
+  `CUPS_MEDIA_TYPE_TRANSPARENCY`.
+- `CUPS_NUMBER_UP`: Controls the number of document pages that are placed on
+  each media side.
+- `CUPS_ORIENTATION`: Controls the orientation of document pages placed on the
+  media: `CUPS_ORIENTATION_PORTRAIT` or `CUPS_ORIENTATION_LANDSCAPE`.
+- `CUPS_PRINT_COLOR_MODE`: Controls whether the output is in color
+  \(`CUPS_PRINT_COLOR_MODE_COLOR`), grayscale
+  \(`CUPS_PRINT_COLOR_MODE_MONOCHROME`), or either
+  \(`CUPS_PRINT_COLOR_MODE_AUTO`).
+- `CUPS_PRINT_QUALITY`: Controls the generate quality of the output:
+  `CUPS_PRINT_QUALITY_DRAFT`, `CUPS_PRINT_QUALITY_NORMAL`, or
+  `CUPS_PRINT_QUALITY_HIGH`.
+- `CUPS_SIDES`: Controls whether prints are placed on one or both sides of the
+  media: `CUPS_SIDES_ONE_SIDED`, `CUPS_SIDES_TWO_SIDED_PORTRAIT`, or
+  `CUPS_SIDES_TWO_SIDED_LANDSCAPE`.
+
+If the `value` argument is `NULL`, the `cupsCheckDestSupported` function returns
+whether the option is supported by the destination.  Otherwise, the function
+returns whether the specified value of the option is supported.
+
+The `cupsFindDestSupported` function returns the IPP attribute containing the
+supported values for a given option:
+
+     ipp_attribute_t *
+     cupsFindDestSupported(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *dinfo,
+                           const char *option);
+
+For example, the following code prints the supported finishing processes for a
+destination, if any, to the standard output:
+
+    cups_dinfo_t *info = cupsCopyDestInfo(CUPS_HTTP_DEFAULT,
+                                          dest);
+
+    if (cupsCheckDestSupported(CUPS_HTTP_DEFAULT, dest, info,
+                               CUPS_FINISHINGS, NULL))
+    {
+      ipp_attribute_t *finishings =
+          cupsFindDestSupported(CUPS_HTTP_DEFAULT, dest, info,
+                                CUPS_FINISHINGS);
+      int i, count = ippGetCount(finishings);
+
+      puts("finishings supported:");
+      for (i = 0; i < count; i ++)
+        printf("  %d\n", ippGetInteger(finishings, i));
+    }
+    else
+      puts("finishings not supported.");
+
+The "job-creation-attributes" option can be queried to get a list of supported
+options.  For example, the following code prints the list of supported options
+to the standard output:
+
+    ipp_attribute_t *attrs =
+        cupsFindDestSupported(CUPS_HTTP_DEFAULT, dest, info,
+                              "job-creation-attributes");
+    int i, count = ippGetCount(attrs);
+
+    for (i = 0; i < count; i ++)
+      puts(ippGetString(attrs, i, NULL));
+
+
+### Getting Default Values
+
+There are two sets of default values - user defaults that are available via the
+`num_options` and `options` members of the `cups_dest_t` structure, and
+destination defaults that available via the `cups_dinfo_t` structure and the
+`cupsFindDestDefault` function which returns the IPP attribute containing the
+default value(s) for a given option:
+
+    ipp_attribute_t *
+    cupsFindDestDefault(http_t *http, cups_dest_t *dest,
+                        cups_dinfo_t *dinfo,
+                        const char *option);
+
+The user defaults from `cupsGetOption` should always take preference over the
+destination defaults.  For example, the following code prints the default
+finishings value(s) to the standard output:
+
+    const char *def_value =
+        cupsGetOption(CUPS_FINISHINGS, dest->num_options,
+                      dest->options);
+    ipp_attribute_t *def_attr =
+        cupsFindDestDefault(CUPS_HTTP_DEFAULT, dest, info,
+                            CUPS_FINISHINGS);
+
+    if (def_value != NULL)
+    {
+      printf("Default finishings: %s\n", def_value);
+    }
+    else
+    {
+      int i, count = ippGetCount(def_attr);
+
+      printf("Default finishings: %d",
+             ippGetInteger(def_attr, 0));
+      for (i = 1; i < count; i ++)
+        printf(",%d", ippGetInteger(def_attr, i));
+      putchar('\n');
+    }
+
+
+### Getting Ready (Loaded) Values
+
+The finishings and media options also support queries for the ready, or loaded,
+values.  For example, a printer may have punch and staple finishers installed
+but be out of staples - the supported values will list both punch and staple
+finishing processes but the ready values will only list the punch processes.
+Similarly, a printer may support hundreds of different sizes of media but only
+have a single size loaded at any given time - the ready values are limited to
+the media that is actually in the printer.
+
+The `cupsFindDestReady` function finds the IPP attribute containing the ready
+values for a given option:
+
+    ipp_attribute_t *
+    cupsFindDestReady(http_t *http, cups_dest_t *dest,
+                      cups_dinfo_t *dinfo, const char *option);
+
+For example, the following code lists the ready finishing processes:
+
+    ipp_attribute_t *ready_finishings =
+        cupsFindDestReady(CUPS_HTTP_DEFAULT, dest, info,
+                          CUPS_FINISHINGS);
+
+    if (ready_finishings != NULL)
+    {
+      int i, count = ippGetCount(ready_finishings);
+
+      puts("finishings ready:");
+      for (i = 0; i < count; i ++)
+        printf("  %d\n", ippGetInteger(ready_finishings, i));
+    }
+    else
+      puts("no finishings are ready.");
+
+
+### Media Size Options
+
+CUPS provides functions for querying the dimensions and margins for each of the
+supported media size options.  The `cups_size_t` structure is used to describe a
+media size:
+
+    typedef struct cups_size_s
+    {
+      char media[128];
+      int width, length;
+      int bottom, left, right, top;
+    } cups_size_t;
+
+The `width` and `length` members specify the dimensions of the media in
+hundredths of millimeters (1/2540th of an inch).  The `bottom`, `left`, `right`,
+and `top` members specify the margins of the printable area, also in hundredths
+of millimeters.
+
+The `cupsGetDestMediaByName` and `cupsGetDestMediaBySize` functions lookup the
+media size information using a standard media size name or dimensions in
+hundredths of millimeters:
+
+    int
+    cupsGetDestMediaByName(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *dinfo,
+                           const char *media,
+                           unsigned flags, cups_size_t *size);
+
+    int
+    cupsGetDestMediaBySize(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *dinfo,
+                           int width, int length,
+                           unsigned flags, cups_size_t *size);
+
+The `media`, `width`, and `length` arguments specify the size to lookup.  The
+`flags` argument specifies a bitfield controlling various lookup options:
+
+- `CUPS_MEDIA_FLAGS_DEFAULT`: Find the closest size supported by the printer.
+- `CUPS_MEDIA_FLAGS_BORDERLESS`: Find a borderless size.
+- `CUPS_MEDIA_FLAGS_DUPLEX`: Find a size compatible with two-sided printing.
+- `CUPS_MEDIA_FLAGS_EXACT`: Find an exact match for the size.
+- `CUPS_MEDIA_FLAGS_READY`: If the printer supports media sensing or
+  configuration of the media in each tray/source, find the size amongst the
+  "ready" media.
+
+If a matching size is found for the destination, the size information is stored
+in the structure pointed to by the `size` argument and 1 is returned.  Otherwise
+0 is returned.
+
+For example, the following code prints the margins for two-sided printing on US
+Letter media:
+
+    cups_size_t size;
+
+    if (cupsGetDestMediaByName(CUPS_HTTP_DEFAULT, dest, info,
+                               CUPS_MEDIA_LETTER,
+                               CUPS_MEDIA_FLAGS_DUPLEX, &size))
+    {
+      puts("Margins for duplex US Letter:");
+      printf("  Bottom: %.2fin\n", size.bottom / 2540.0);
+      printf("    Left: %.2fin\n", size.left / 2540.0);
+      printf("   Right: %.2fin\n", size.right / 2540.0);
+      printf("     Top: %.2fin\n", size.top / 2540.0);
+    }
+    else
+      puts("Margins for duplex US Letter are not available.");
+
+You can also enumerate all of the sizes that match a given `flags` value using
+the `cupsGetDestMediaByIndex` and `cupsGetDestMediaCount` functions:
+
+    int
+    cupsGetDestMediaByIndex(http_t *http, cups_dest_t *dest,
+                            cups_dinfo_t *dinfo, int n,
+                            unsigned flags, cups_size_t *size);
+
+    int
+    cupsGetDestMediaCount(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *dinfo, unsigned flags);
+
+For example, the following code prints the list of ready media and corresponding
+margins:
+
+    cups_size_t size;
+    int i;
+    int count = cupsGetDestMediaCount(CUPS_HTTP_DEFAULT,
+                                      dest, info,
+                                      CUPS_MEDIA_FLAGS_READY);
+
+    for (i = 0; i < count; i ++)
+    {
+      if (cupsGetDestMediaByIndex(CUPS_HTTP_DEFAULT, dest, info,
+                                  i, CUPS_MEDIA_FLAGS_READY,
+                                  &size))
+      {
+        printf("%s:\n", size.name);
+        printf("   Width: %.2fin\n", size.width / 2540.0);
+        printf("  Length: %.2fin\n", size.length / 2540.0);
+        printf("  Bottom: %.2fin\n", size.bottom / 2540.0);
+        printf("    Left: %.2fin\n", size.left / 2540.0);
+        printf("   Right: %.2fin\n", size.right / 2540.0);
+        printf("     Top: %.2fin\n", size.top / 2540.0);
+      }
+    }
+
+Finally, the `cupsGetDestMediaDefault` function returns the default media size:
+
+    int
+    cupsGetDestMediaDefault(http_t *http, cups_dest_t *dest,
+                            cups_dinfo_t *dinfo, unsigned flags,
+                            cups_size_t *size);
+
+
+### Localizing Options and Values
+
+CUPS provides three functions to get localized, human-readable strings in the
+user's current locale for options and values: `cupsLocalizeDestMedia`,
+`cupsLocalizeDestOption`, and `cupsLocalizeDestValue`:
+
+    const char *
+    cupsLocalizeDestMedia(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *info, unsigned flags,
+                          cups_size_t *size);
+
+    const char *
+    cupsLocalizeDestOption(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *info,
+                           const char *option);
+
+    const char *
+    cupsLocalizeDestValue(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *info,
+                          const char *option, const char *value);
+
+
+## Submitting a Print Job
+
+Once you are ready to submit a print job, you create a job using the
+`cupsCreateDestJob` function:
+
+    ipp_status_t
+    cupsCreateDestJob(http_t *http, cups_dest_t *dest,
+                      cups_dinfo_t *info, int *job_id,
+                      const char *title, int num_options,
+                      cups_option_t *options);
+
+The `title` argument specifies a name for the print job such as "My Document".
+The `num_options` and `options` arguments specify the options for the print
+job which are allocated using the `cupsAddOption` function.
+
+When successful, the job's numeric identifier is stored in the integer pointed
+to by the `job_id` argument and `IPP_STATUS_OK` is returned.  Otherwise, an IPP
+error status is returned.
+
+For example, the following code creates a new job that will print 42 copies of a
+two-sided US Letter document:
+
+    int job_id = 0;
+    int num_options = 0;
+    cups_option_t *options = NULL;
+
+    num_options = cupsAddOption(CUPS_COPIES, "42",
+                                num_options, &options);
+    num_options = cupsAddOption(CUPS_MEDIA, CUPS_MEDIA_LETTER,
+                                num_options, &options);
+    num_options = cupsAddOption(CUPS_SIDES,
+                                CUPS_SIDES_TWO_SIDED_PORTRAIT,
+                                num_options, &options);
+
+    if (cupsCreateDestJob(CUPS_HTTP_DEFAULT, dest, info,
+                          &job_id, "My Document", num_options,
+                          options) == IPP_STATUS_OK)
+      printf("Created job: %d\n", job_id);
+    else
+      printf("Unable to create job: %s\n",
+             cupsLastErrorString());
+
+Once the job is created, you submit documents for the job using the
+`cupsStartDestDocument`, `cupsWriteRequestData`, and `cupsFinishDestDocument`
+functions:
+
+    http_status_t
+    cupsStartDestDocument(http_t *http, cups_dest_t *dest,
+                          cups_dinfo_t *info, int job_id,
+                          const char *docname,
+                          const char *format,
+                          int num_options,
+                          cups_option_t *options,
+                          int last_document);
+
+    http_status_t
+    cupsWriteRequestData(http_t *http, const char *buffer,
+                         size_t length);
+
+    ipp_status_t
+    cupsFinishDestDocument(http_t *http, cups_dest_t *dest,
+                           cups_dinfo_t *info);
+
+The `docname` argument specifies the name of the document, typically the
+original filename.  The `format` argument specifies the MIME media type of the
+document, including the following constants:
+
+- `CUPS_FORMAT_JPEG`: "image/jpeg"
+- `CUPS_FORMAT_PDF`: "application/pdf"
+- `CUPS_FORMAT_POSTSCRIPT`: "application/postscript"
+- `CUPS_FORMAT_TEXT`: "text/plain"
+
+The `num_options` and `options` arguments specify per-document print options,
+which at present must be 0 and `NULL`.  The `last_document` argument specifies
+whether this is the last document in the job.
+
+For example, the following code submits a PDF file to the job that was just
+created:
+
+    FILE *fp = fopen("filename.pdf", "rb");
+    size_t bytes;
+    char buffer[65536];
+
+    if (cupsStartDestDocument(CUPS_HTTP_DEFAULT, dest, info,
+                              job_id, "filename.pdf", 0, NULL,
+                              1) == HTTP_STATUS_CONTINUE)
+    {
+      while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
+        if (cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer,
+                                 bytes) != HTTP_STATUS_CONTINUE)
+          break;
+
+      if (cupsFinishDestDocument(CUPS_HTTP_DEFAULT, dest,
+                                 info) == IPP_STATUS_OK)
+        puts("Document send succeeded.");
+      else
+        printf("Document send failed: %s\n",
+               cupsLastErrorString());
+    }
+
+    fclose(fp);
+
+
+# Sending IPP Requests
+
+CUPS provides a rich API for sending IPP requests to the scheduler or printers,
+typically from management or utility applications whose primary purpose is not
+to send print jobs.
+
+
+## Connecting to the Scheduler or Printer
+
+The connection to the scheduler or printer is represented by the HTTP connection
+type `http_t`.  The `cupsConnectDest` function connects to the scheduler or
+printer associated with the destination:
+
+    http_t *
+    cupsConnectDest(cups_dest_t *dest, unsigned flags, int msec,
+                    int *cancel, char *resource,
+                    size_t resourcesize, cups_dest_cb_t cb,
+                    void *user_data);
+
+The `dest` argument specifies the destination to connect to.
+
+The `flags` argument specifies whether you want to connect to the scheduler
+(`CUPS_DEST_FLAGS_NONE`) or device/printer (`CUPS_DEST_FLAGS_DEVICE`) associated
+with the destination.
+
+The `msec` argument specifies how long you are willing to wait for the
+connection to be established in milliseconds.  Specify a value of `-1` to wait
+indefinitely.
+
+The `cancel` argument specifies the address of an integer variable that can be
+set to a non-zero value to cancel the connection.  Specify a value of `NULL`
+to not provide a cancel variable.
+
+The `resource` and `resourcesize` arguments specify the address and size of a
+character string array to hold the path to use when sending an IPP request.
+
+The `cb` and `user_data` arguments specify a destination callback function that
+returns 1 to continue connecting or 0 to stop.  The destination callback work
+the same way as the one used for the `cupsEnumDests` function.
+
+On success, a HTTP connection is returned that can be used to send IPP requests
+and get IPP responses.
+
+For example, the following code connects to the printer associated with a
+destination with a 30 second timeout:
+
+    char resource[256];
+    http_t *http = cupsConnectDest(dest, CUPS_DEST_FLAGS_DEVICE,
+                                   30000, NULL, resource,
+                                   sizeof(resource), NULL, NULL);
+
+
+## Creating an IPP Request
+
+IPP requests are represented by the IPP message type `ipp_t` and each IPP
+attribute in the request is representing using the type `ipp_attribute_t`.  Each
+IPP request includes an operation code (`IPP_OP_CREATE_JOB`,
+`IPP_OP_GET_PRINTER_ATTRIBUTES`, etc.) and a 32-bit integer identifier.
+
+The `ippNewRequest` function creates a new IPP request:
+
+    ipp_t *
+    ippNewRequest(ipp_op_t op);
+
+The `op` argument specifies the IPP operation code for the request.  For
+example, the following code creates an IPP Get-Printer-Attributes request:
+
+    ipp_t *request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+
+The request identifier is automatically set to a unique value for the current
+process.
+
+Each IPP request starts with two IPP attributes, "attributes-charset" and
+"attributes-natural-language", followed by IPP attribute(s) that specify the
+target of the operation.  The `ippNewRequest` automatically adds the correct
+"attributes-charset" and "attributes-natural-language" attributes, but you must
+add the target attribute(s).  For example, the following code adds the
+"printer-uri" attribute to the IPP Get-Printer-Attributes request to specify
+which printer is being queried:
+
+    const char *printer_uri = cupsGetOption("device-uri",
+                                            dest->num_options,
+                                            dest->options);
+
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+                 "printer-uri", NULL, printer_uri);
+
+> Note: If we wanted to query the scheduler instead of the device, we would look
+> up the "printer-uri-supported" option instead of the "device-uri" value.
+
+The `ippAddString` function adds the "printer-uri" attribute the the IPP
+request.  The `IPP_TAG_OPERATION` argument specifies that the attribute is part
+of the operation.  The `IPP_TAG_URI` argument specifies that the value is a
+Universal Resource Identifier (URI) string.  The `NULL` argument specifies there
+is no language (English, French, Japanese, etc.) associated with the string, and
+the `printer_uri` argument specifies the string value.
+
+The IPP Get-Printer-Attributes request also supports an IPP attribute called
+"requested-attributes" that lists the attributes and values you are interested
+in.  For example, the following code requests the printer state attributes:
+
+    static const char * const requested_attributes[] =
+    {
+      "printer-state",
+      "printer-state-message",
+      "printer-state-reasons"
+    };
+
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                  "requested-attributes", 3, NULL,
+                  requested_attributes);
+
+The `ippAddStrings` function adds an attribute with one or more strings, in this
+case three.  The `IPP_TAG_KEYWORD` argument specifies that the strings are
+keyword values, which are used for attribute names.  All strings use the same
+language (`NULL`), and the attribute will contain the three strings in the
+array `requested_attributes`.
+
+CUPS provides many functions to adding attributes of different types:
+
+- `ippAddBoolean` adds a boolean (`IPP_TAG_BOOLEAN`) attribute with one value.
+- `ippAddInteger` adds an enum (`IPP_TAG_ENUM`) or integer (`IPP_TAG_INTEGER`)
+  attribute with one value.
+- `ippAddIntegers` adds an enum or integer attribute with one or more values.
+- `ippAddOctetString` adds an octetString attribute with one value.
+- `ippAddOutOfBand` adds a admin-defined (`IPP_TAG_ADMINDEFINE`), default
+  (`IPP_TAG_DEFAULT`), delete-attribute (`IPP_TAG_DELETEATTR`), no-value
+  (`IPP_TAG_NOVALUE`), not-settable (`IPP_TAG_NOTSETTABLE`), unknown
+  (`IPP_TAG_UNKNOWN`), or unsupported (`IPP_TAG_UNSUPPORTED_VALUE`) out-of-band
+  attribute.
+- `ippAddRange` adds a rangeOfInteger attribute with one range.
+- `ippAddRanges` adds a rangeOfInteger attribute with one or more ranges.
+- `ippAddResolution` adds a resolution attribute with one resolution.
+- `ippAddResolutions` adds a resolution attribute with one or more resolutions.
+- `ippAddString` adds a charset (`IPP_TAG_CHARSET`), keyword (`IPP_TAG_KEYWORD`),
+  mimeMediaType (`IPP_TAG_MIMETYPE`), name (`IPP_TAG_NAME` and
+  `IPP_TAG_NAMELANG`), naturalLanguage (`IPP_TAG_NATURAL_LANGUAGE`), text
+  (`IPP_TAG_TEXT` and `IPP_TAG_TEXTLANG`), uri (`IPP_TAG_URI`), or uriScheme
+  (`IPP_TAG_URISCHEME`) attribute with one value.
+- `ippAddStrings` adds a charset, keyword, mimeMediaType, name, naturalLanguage,
+  text, uri, or uriScheme attribute with one or more values.
+
+
+## Sending the IPP Request
+
+Once you have created the IPP request, you can send it using the
+`cupsDoRequest` function.  For example, the following code sends the IPP
+Get-Printer-Attributes request to the destination and saves the response:
+
+    ipp_t *response = cupsDoRequest(http, request, resource);
+
+For requests like Send-Document that include a file, the `cupsDoFileRequest`
+function should be used:
+
+    ipp_t *response = cupsDoFileRequest(http, request, resource,
+                                        filename);
+
+Both `cupsDoRequest` and `cupsDoFileRequest` free the IPP request.  If a valid
+IPP response is received, it is stored in a new IPP message (`ipp_t`) and
+returned to the caller.  Otherwise `NULL` is returned.
+
+The status from the most recent request can be queried using the `cupsLastError`
+function, for example:
+
+    if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST)
+    {
+      /* request failed */
+    }
+
+A human-readable error message is also available using the `cupsLastErrorString`
+function:
+
+    if (cupsLastError() >= IPP_STATUS_ERROR_BAD_REQUEST)
+    {
+      /* request failed */
+      printf("Request failed: %s\n", cupsLastErrorString());
+    }
+
+
+## Processing the IPP Response
+
+Each response to an IPP request is also an IPP message (`ipp_t`) with its own
+IPP attributes (`ipp_attribute_t`) that includes a status code (`IPP_STATUS_OK`,
+`IPP_STATUS_ERROR_BAD_REQUEST`, etc.) and the corresponding 32-bit integer
+identifier from the request.
+
+For example, the following code finds the printer state attributes and prints
+their values:
+
+    ipp_attribute_t *attr;
+
+    if ((attr = ippFindAttribute(response, "printer-state",
+                                 IPP_TAG_ENUM)) != NULL)
+    {
+      printf("printer-state=%s\n",
+             ippEnumString("printer-state", ippGetInteger(attr, 0)));
+    }
+    else
+      puts("printer-state=unknown");
+
+    if ((attr = ippFindAttribute(response, "printer-state-message",
+                                 IPP_TAG_TEXT)) != NULL)
+    {
+      printf("printer-state-message=\"%s\"\n",
+             ippGetString(attr, 0, NULL)));
+    }
+
+    if ((attr = ippFindAttribute(response, "printer-state-reasons",
+                                 IPP_TAG_KEYWORD)) != NULL)
+    {
+      int i, count = ippGetCount(attr);
+
+      puts("printer-state-reasons=");
+      for (i = 0; i < count; i ++)
+        printf("    %s\n", ippGetString(attr, i, NULL)));
+    }
+
+The `ippGetCount` function returns the number of values in an attribute.
+
+The `ippGetInteger` and `ippGetString` functions return a single integer or
+string value from an attribute.
+
+The `ippEnumString` function converts a enum value to its keyword (string)
+equivalent.
+
+Once you are done using the IPP response message, free it using the `ippDelete`
+function:
+
+    ippDelete(response);
+
+
+## Authentication
+
+CUPS normally handles authentication through the console.  GUI applications
+should set a password callback using the `cupsSetPasswordCB2` function:
+
+    void
+    cupsSetPasswordCB2(cups_password_cb2_t cb, void *user_data);
+
+The password callback will be called when needed and is responsible for setting
+the current user name using `cupsSetUser` and returning a string:
+
+    const char *
+    cups_password_cb2(const char *prompt, http_t *http,
+                      const char *method, const char *resource,
+                      void *user_data);
+
+The `prompt` argument is a string from CUPS that should be displayed to the
+user.
+
+The `http` argument is the connection hosting the request that is being
+authenticated.  The password callback can call the `httpGetField` and
+`httpGetSubField` functions to look for additional details concerning the
+authentication challenge.
+
+The `method` argument specifies the HTTP method used for the request and is
+typically "POST".
+
+The `resource` argument specifies the path used for the request.
+
+The `user_data` argument provides the user data pointer from the
+`cupsSetPasswordCB2` call.
diff --git a/cups/cupspm.opacity b/cups/cupspm.opacity
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cups/cupspm.opacity
diff --git a/cups/cupspm.png b/cups/cupspm.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cups/cupspm.png
diff --git a/cups/debug-private.h b/cups/debug-private.h
index 23a0ae1..dc9fe4e 100644
--- a/cups/debug-private.h
+++ b/cups/debug-private.h
@@ -1,7 +1,7 @@
 /*
  * Private debugging macros for CUPS.
  *
- * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2005 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -67,20 +67,10 @@
  */
 
 #  ifdef DEBUG
-#    ifdef WIN32
-#      ifdef LIBCUPS2_EXPORTS
-#        define DLLExport __declspec(dllexport)
-#      else
-#        define DLLExport
-#      endif /* LIBCUPS2_EXPORTS */
-#    else
-#      define DLLExport
-#    endif /* WIN32 */
 #    define DEBUG_puts(x) _cups_debug_puts(x)
 #    define DEBUG_printf(x) _cups_debug_printf x
 #    define DEBUG_set(logfile,level,filter) _cups_debug_set(logfile,level,filter,1)
 #  else
-#    define DLLExport
 #    define DEBUG_puts(x)
 #    define DEBUG_printf(x)
 #    define DEBUG_set(logfile,level,filter)
@@ -93,16 +83,13 @@
 
 extern int	_cups_debug_fd;
 extern int	_cups_debug_level;
-extern void	DLLExport _cups_debug_printf(const char *format, ...)
-		__attribute__ ((__format__ (__printf__, 1, 2)));
-extern void	DLLExport _cups_debug_puts(const char *s);
-extern void	DLLExport _cups_debug_set(const char *logfile,
-					  const char *level, const char *filter,
-					  int force);
-#  ifdef WIN32
-extern int	_cups_gettimeofday(struct timeval *tv, void *tz);
+extern void	_cups_debug_printf(const char *format, ...) _CUPS_FORMAT(1, 2);
+extern void	_cups_debug_puts(const char *s);
+extern void	_cups_debug_set(const char *logfile, const char *level, const char *filter, int force) _CUPS_PRIVATE;
+#  ifdef _WIN32
+extern int	_cups_gettimeofday(struct timeval *tv, void *tz) _CUPS_PRIVATE;
 #    define gettimeofday(a,b) _cups_gettimeofday(a, b)
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
 #  ifdef __cplusplus
 }
diff --git a/cups/debug.c b/cups/debug.c
index bd244fe..b68f5ed 100644
--- a/cups/debug.c
+++ b/cups/debug.c
@@ -18,7 +18,7 @@
 
 #include "cups-private.h"
 #include "thread-private.h"
-#ifdef WIN32
+#ifdef _WIN32
 #  include <sys/timeb.h>
 #  include <time.h>
 #  include <io.h>
@@ -36,7 +36,7 @@
 #else
 #  include <sys/time.h>
 #  include <unistd.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 #include <regex.h>
 #include <fcntl.h>
 
@@ -83,7 +83,7 @@
  * '_cups_debug_printf()' - Write a formatted line to the log.
  */
 
-void DLLExport
+void
 _cups_debug_printf(const char *format,	/* I - Printf-style format string */
                    ...)			/* I - Additional arguments as needed */
 {
@@ -168,7 +168,7 @@
  * '_cups_debug_puts()' - Write a single line to the log.
  */
 
-void DLLExport
+void
 _cups_debug_puts(const char *s)		/* I - String to output */
 {
   struct timeval	curtime;	/* Current time */
@@ -248,7 +248,7 @@
  * '_cups_debug_set()' - Enable or disable debug logging.
  */
 
-void DLLExport
+void
 _cups_debug_set(const char *logfile,	/* I - Log file or NULL */
                 const char *level,	/* I - Log level or NULL */
 		const char *filter,	/* I - Filter string or NULL */
diff --git a/cups/dest-job.c b/cups/dest-job.c
index b0d89b6..f12b1dc 100644
--- a/cups/dest-job.c
+++ b/cups/dest-job.c
@@ -1,7 +1,7 @@
 /*
  * Destination job support for CUPS.
  *
- * Copyright 2012-2016 by Apple Inc.
+ * Copyright 2012-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -25,13 +25,13 @@
  * The "job_id" is the number returned by cupsCreateDestJob.
  *
  * Returns @code IPP_STATUS_OK@ on success and
- * @code IPP_STATUS_ERRPR_NOT_AUTHORIZED@ or
+ * @code IPP_STATUS_ERROR_NOT_AUTHORIZED@ or
  * @code IPP_STATUS_ERROR_FORBIDDEN@ on failure.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
 
-ipp_status_t
+ipp_status_t                            /* O - Status of cancel operation */
 cupsCancelDestJob(http_t      *http,	/* I - Connection to destination */
                   cups_dest_t *dest,	/* I - Destination */
                   int         job_id)	/* I - Job ID */
@@ -84,6 +84,13 @@
   DEBUG_printf(("cupsCloseDestJob(http=%p, dest=%p(%s/%s), info=%p, job_id=%d)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, job_id));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -172,6 +179,13 @@
                 "job_id=%p, title=\"%s\", num_options=%d, options=%p)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, (void *)job_id, title, num_options, (void *)options));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -252,6 +266,13 @@
   DEBUG_printf(("cupsFinishDestDocument(http=%p, dest=%p(%s/%s), info=%p)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -307,6 +328,13 @@
   DEBUG_printf(("cupsStartDestDocument(http=%p, dest=%p(%s/%s), info=%p, job_id=%d, docname=\"%s\", format=\"%s\", num_options=%d, options=%p, last_document=%d)", (void *)http, (void *)dest, dest ? dest->name : NULL, dest ? dest->instance : NULL, (void *)info, job_id, docname, format, num_options, (void *)options, last_document));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
diff --git a/cups/dest-localization.c b/cups/dest-localization.c
index 6d75a97..fceaad2 100644
--- a/cups/dest-localization.c
+++ b/cups/dest-localization.c
@@ -60,12 +60,15 @@
 			*ltype;		/* Localized media type */
 
 
+  DEBUG_printf(("cupsLocalizeDestMedia(http=%p, dest=%p, dinfo=%p, flags=%x, size=%p(\"%s\"))", (void *)http, (void *)dest, (void *)dinfo, flags, (void *)size, size ? size->media : "(null)"));
+
  /*
   * Range check input...
   */
 
   if (!http || !dest || !dinfo || !size)
   {
+    DEBUG_puts("1cupsLocalizeDestMedia: Returning NULL.");
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
     return (NULL);
   }
@@ -79,13 +82,24 @@
 
   key.id = size->media;
   if ((match = (_cups_message_t *)cupsArrayFind(dinfo->localizations, &key)) != NULL)
+  {
+    DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
     return (match->str);
+  }
 
  /*
   * If not, get the localized size, source, and type strings...
   */
 
   lang = cupsLangDefault();
+
+  snprintf(temp, sizeof(temp), "media.%s", size->media);
+  if ((lsize = _cupsLangString(lang, temp)) != NULL && strcmp(lsize, temp))
+  {
+    DEBUG_printf(("1cupsLocalizeDestMedia: Returning standard localization \"%s\".", lsize));
+    return (lsize);
+  }
+
   pwg  = pwgMediaForSize(size->width, size->length);
 
   if (pwg->ppd)
@@ -101,7 +115,7 @@
       * Use inches since the size is a multiple of 1/4 inch.
       */
 
-      snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g")), size->width / 2540.0, size->length / 2540.0);
+      snprintf(temp, sizeof(temp), _cupsLangString(lang, _("%g x %g \"")), size->width / 2540.0, size->length / 2540.0);
     }
     else
     {
@@ -189,6 +203,8 @@
 
   cupsArrayAdd(dinfo->localizations, match);
 
+  DEBUG_printf(("1cupsLocalizeDestMedia: Returning \"%s\".", match->str));
+
   return (match->str);
 }
 
@@ -212,21 +228,23 @@
 {
   _cups_message_t	key,		/* Search key */
 			*match;		/* Matching entry */
+  const char            *localized;     /* Localized string */
 
 
+  DEBUG_printf(("cupsLocalizeDestOption(http=%p, dest=%p, dinfo=%p, option=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option));
+
   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 if ((localized = _cupsLangString(cupsLangDefault(), option)) != NULL)
+    return (localized);
   else
     return (option);
 }
@@ -253,22 +271,40 @@
   _cups_message_t	key,		/* Search key */
 			*match;		/* Matching entry */
   char			pair[256];	/* option.value pair */
+  const char            *localized;     /* Localized string */
 
 
+  DEBUG_printf(("cupsLocalizeDestValue(http=%p, dest=%p, dinfo=%p, option=\"%s\", value=\"%s\")", (void *)http, (void *)dest, (void *)dinfo, option, value));
+
   if (!http || !dest || !dinfo)
     return (value);
 
+  if (!strcmp(option, "media"))
+  {
+    pwg_media_t *media = pwgMediaForPWG(value);
+    cups_size_t size;
+
+    strlcpy(size.media, value, sizeof(size.media));
+    size.width  = media ? media->width : 0;
+    size.length = media ? media->length : 0;
+    size.left   = 0;
+    size.right  = 0;
+    size.bottom = 0;
+    size.top    = 0;
+
+    return (cupsLocalizeDestMedia(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size));
+  }
+
   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 if ((localized = _cupsLangString(cupsLangDefault(), pair)) != NULL && strcmp(localized, pair))
+    return (localized);
   else
     return (value);
 }
diff --git a/cups/dest-options.c b/cups/dest-options.c
index bf9020b..51705a5 100644
--- a/cups/dest-options.c
+++ b/cups/dest-options.c
@@ -1,7 +1,7 @@
 /*
  * Destination option/media support for CUPS.
  *
- * Copyright 2012-2016 by Apple Inc.
+ * Copyright 2012-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -73,7 +73,7 @@
     cups_dest_t  *dest,			/* I - Destination */
     cups_dinfo_t *dinfo,		/* I - Destination information */
     const char   *option,		/* I - Option */
-    const char   *value)		/* I - Value */
+    const char   *value)		/* I - Value or @code NULL@ */
 {
   int			i;		/* Looping var */
   char			temp[1024];	/* Temporary string */
@@ -86,10 +86,17 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
-  if (!http || !dest || !dinfo || !option || !value)
+  if (!http || !dest || !dinfo || !option)
     return (0);
 
  /*
@@ -107,7 +114,10 @@
   if (!attr)
     return (0);
 
- /*
+  if (!value)
+    return (1);
+
+/*
   * Compare values...
   */
 
@@ -316,6 +326,13 @@
     *resolved = NULL;
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -575,6 +592,13 @@
   DEBUG_printf(("cupsCopyDestSupported(http=%p, dest=%p(%s))", (void *)http, (void *)dest, dest ? dest->name : ""));
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -686,6 +710,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -727,6 +758,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -770,6 +808,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -791,6 +836,8 @@
 /*
  * 'cupsFreeDestInfo()' - Free destination information obtained using
  *                        @link cupsCopyDestInfo@.
+ *
+ * @since CUPS 1.6/macOS 10.8@
  */
 
 void
@@ -852,6 +899,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -940,6 +994,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -1008,6 +1069,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -1059,6 +1127,13 @@
     unsigned     flags)			/* I - Media flags */
 {
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
@@ -1104,6 +1179,13 @@
 
 
  /*
+  * Get the default connection as needed...
+  */
+
+  if (!http)
+    http = _cupsConnect();
+
+ /*
   * Range check input...
   */
 
diff --git a/cups/dest.c b/cups/dest.c
index b06a9ee..7ef85a1 100644
--- a/cups/dest.c
+++ b/cups/dest.c
@@ -1,7 +1,7 @@
 /*
  * User-defined destination (and option) support for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -60,6 +60,13 @@
 #  define kUseLastPrinter	CFSTR("UseLastPrinter")
 #endif /* __APPLE__ */
 
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+#  define _CUPS_DNSSD_GET_DESTS 250     /* Milliseconds for cupsGetDests */
+#  define _CUPS_DNSSD_MAXTIME	50	/* Milliseconds for maximum quantum of time */
+#else
+#  define _CUPS_DNSSD_GET_DESTS 0       /* Milliseconds for cupsGetDests */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
 
 /*
  * Types...
@@ -85,6 +92,7 @@
   AvahiSimplePoll	*simple_poll;	/* Polling interface */
   AvahiClient		*client;	/* Client information */
   int			got_data;	/* Did we get data? */
+  int			browsers;	/* How many browsers are running? */
 #  endif /* HAVE_DNSSD */
   cups_dest_cb_t	cb;		/* Callback */
   void			*user_data;	/* User data pointer */
@@ -101,9 +109,9 @@
 #  else /* HAVE_AVAHI */
   AvahiRecordBrowser	*ref;		/* Browser for query */
 #  endif /* HAVE_DNSSD */
-  char			*domain,	/* Domain name */
-			*fullName,	/* Full name */
-			*regtype;	/* Registration type */
+  char			*fullName,	/* Full name */
+			*regtype,	/* Registration type */
+			*domain;	/* Domain name */
   cups_ptype_t		type;		/* Device registration type */
   cups_dest_t		dest;		/* Destination record */
 } _cups_dnssd_device_t;
@@ -115,6 +123,18 @@
 } _cups_dnssd_resolve_t;
 #endif /* HAVE_DNSSD */
 
+typedef struct _cups_getdata_s
+{
+  int         num_dests;                /* Number of destinations */
+  cups_dest_t *dests;                   /* Destinations */
+} _cups_getdata_t;
+
+typedef struct _cups_namedata_s
+{
+  const char  *name;                    /* Named destination */
+  cups_dest_t *dest;                    /* Destination */
+} _cups_namedata_t;
+
 
 /*
  * Local functions...
@@ -208,10 +228,13 @@
 static int		cups_dnssd_resolve_cb(void *context);
 static void		cups_dnssd_unquote(char *dst, const char *src,
 			                   size_t dstsize);
+static int		cups_elapsed(struct timeval *t);
 #endif /* HAVE_DNSSD || HAVE_AVAHI */
+static int              cups_enum_dests(http_t *http, unsigned flags, int msec, int *cancel, cups_ptype_t type, cups_ptype_t mask, cups_dest_cb_t cb, void *user_data);
 static int		cups_find_dest(const char *name, const char *instance,
 				       int num_dests, cups_dest_t *dests, int prev,
 				       int *rdiff);
+static int              cups_get_cb(_cups_getdata_t *data, unsigned flags, cups_dest_t *dest);
 static char		*cups_get_default(const char *filename, char *namebuf,
 					  size_t namesize, const char **instance);
 static int		cups_get_dests(const char *filename, const char *match_name,
@@ -219,6 +242,8 @@
 				       int num_dests, cups_dest_t **dests);
 static char		*cups_make_string(ipp_attribute_t *attr, char *buffer,
 			                  size_t bufsize);
+static int              cups_name_cb(_cups_namedata_t *data, unsigned flags, cups_dest_t *dest);
+static void		cups_queue_name(char *name, const char *serviceName, size_t namesize);
 
 
 /*
@@ -549,18 +574,24 @@
 
 
 /*
- * 'cupsConnectDest()' - Connect to the server for a destination.
+ * 'cupsConnectDest()' - Open a conection to the destination.
  *
- * Connect to the destination, returning a new http_t connection object and
- * optionally the resource path to use for the destination.  These calls will
- * block until a connection is made, the timeout expires, the integer pointed
- * to by "cancel" is non-zero, or the callback function (or block) returns 0,
- * The caller is responsible for calling httpClose() on the returned object.
+ * Connect to the destination, returning a new @code http_t@ connection object
+ * and optionally the resource path to use for the destination.  These calls
+ * will block until a connection is made, the timeout expires, the integer
+ * pointed to by "cancel" is non-zero, or the callback function (or block)
+ * returns 0.  The caller is responsible for calling @link httpClose@ on the
+ * returned connection.
+ *
+ * Starting with CUPS 2.2.4, the caller can pass  @code CUPS_DEST_FLAGS_DEVICE@
+ * for the "flags" argument to connect directly to the device associated with
+ * the destination.  Otherwise, the connection is made to the CUPS scheduler
+ * associated with the destination.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
 
-http_t *				/* O - Connection to server or @code NULL@ */
+http_t *				/* O - Connection to destination or @code NULL@ */
 cupsConnectDest(
     cups_dest_t    *dest,		/* I - Destination */
     unsigned       flags,		/* I - Connection flags */
@@ -608,17 +639,24 @@
   * Grab the printer URI...
   */
 
-  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
+  if (flags & CUPS_DEST_FLAGS_DEVICE)
   {
-    if ((uri = cupsGetOption("resolved-device-uri", dest->num_options, dest->options)) == NULL)
+    if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
     {
-      if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
-      {
 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
-        if (strstr(uri, "._tcp"))
-          uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
+      if (strstr(uri, "._tcp"))
+        uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
 #endif /* HAVE_DNSSD || HAVE_AVAHI */
-      }
+    }
+  }
+  else if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
+  {
+    if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
+    {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      if (strstr(uri, "._tcp"))
+        uri = cups_dnssd_resolve(dest, uri, msec, cancel, cb, user_data);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
     }
 
     if (uri)
@@ -725,18 +763,23 @@
 
 #ifdef __BLOCKS__
 /*
- * 'cupsConnectDestBlock()' - Connect to the server for a destination.
+ * 'cupsConnectDestBlock()' - Open a connection to the destination.
  *
- * Connect to the destination, returning a new http_t connection object and
- * optionally the resource path to use for the destination.  These calls will
- * block until a connection is made, the timeout expires, the integer pointed
- * to by "cancel" is non-zero, or the callback function (or block) returns 0,
- * The caller is responsible for calling httpClose() on the returned object.
+ * Connect to the destination, returning a new @code http_t@ connection object
+ * and optionally the resource path to use for the destination.  These calls
+ * will block until a connection is made, the timeout expires, the integer
+ * pointed to by "cancel" is non-zero, or the block returns 0.  The caller is
+ * responsible for calling @link httpClose@ on the returned connection.
  *
- * @since CUPS 1.6/macOS 10.8@
+ * Starting with CUPS 2.2.4, the caller can pass  @code CUPS_DEST_FLAGS_DEVICE@
+ * for the "flags" argument to connect directly to the device associated with
+ * the destination.  Otherwise, the connection is made to the CUPS scheduler
+ * associated with the destination.
+ *
+ * @since CUPS 1.6/macOS 10.8@ @exclude all@
  */
 
-http_t *				/* O - Connection to server or @code NULL@ */
+http_t *				/* O - Connection to destination or @code NULL@ */
 cupsConnectDestBlock(
     cups_dest_t       *dest,		/* I - Destination */
     unsigned          flags,		/* I - Connection flags */
@@ -762,10 +805,10 @@
  * @since CUPS 1.6/macOS 10.8@
  */
 
-int
-cupsCopyDest(cups_dest_t *dest,
-             int         num_dests,
-             cups_dest_t **dests)
+int                                     /* O  - New number of destinations */
+cupsCopyDest(cups_dest_t *dest,         /* I  - Destination to copy */
+             int         num_dests,     /* I  - Number of destinations */
+             cups_dest_t **dests)       /* IO - Destination array */
 {
   int		i;			/* Looping var */
   cups_dest_t	*new_dest;		/* New destination pointer */
@@ -907,334 +950,36 @@
 /*
  * 'cupsEnumDests()' - Enumerate available destinations with a callback function.
  *
- * Destinations are enumerated from one or more sources. The callback function
- * receives the @code user_data@ pointer, destination name, instance, number of
- * options, and options which can be used as input to the @link cupsAddDest@
- * function.  The function must return 1 to continue enumeration or 0 to stop.
+ * Destinations are enumerated from one or more sources.  The callback function
+ * receives the @code user_data@ pointer and the destination pointer which can
+ * be used as input to the @link cupsCopyDest@ function.  The function must
+ * return 1 to continue enumeration or 0 to stop.
+ *
+ * The @code type@ and @code mask@ arguments allow the caller to filter the
+ * destinations that are enumerated.  Passing 0 for both will enumerate all
+ * printers.  The constant @code CUPS_PRINTER_DISCOVERED@ is used to filter on
+ * destinations that are available but have not yet been added locally.
  *
  * Enumeration happens on the current thread and does not return until all
  * destinations have been enumerated or the callback function returns 0.
  *
+ * Note: The callback function will likely receive multiple updates for the same
+ * destinations - it is up to the caller to suppress any duplicate destinations.
+ *
  * @since CUPS 1.6/macOS 10.8@
  */
 
 int					/* O - 1 on success, 0 on failure */
 cupsEnumDests(
-    unsigned       flags,		/* I - Enumeration flags */
-    int            msec,		/* I - Timeout in milliseconds,
-					 *     -1 for indefinite */
-    int            *cancel,		/* I - Pointer to "cancel" variable */
-    cups_ptype_t   type,		/* I - Printer type bits */
-    cups_ptype_t   mask,		/* I - Mask for printer type bits */
-    cups_dest_cb_t cb,			/* I - Callback function */
-    void           *user_data)		/* I - User data */
+  unsigned       flags,			/* I - Enumeration flags */
+  int            msec,			/* I - Timeout in milliseconds, -1 for indefinite */
+  int            *cancel,		/* I - Pointer to "cancel" variable */
+  cups_ptype_t   type,			/* I - Printer type bits */
+  cups_ptype_t   mask,			/* I - Mask for printer type bits */
+  cups_dest_cb_t cb,			/* I - Callback function */
+  void           *user_data)		/* I - User data */
 {
-  int			i,		/* Looping var */
-			num_dests;	/* Number of destinations */
-  cups_dest_t		*dests = NULL,	/* Destinations */
-			*dest;		/* Current destination */
-  const char		*defprinter;	/* Default printer */
-  char			name[1024],	/* Copy of printer name */
-			*instance,	/* Pointer to instance name */
-			*user_default;	/* User default printer */
-#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 */
-#  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
-  DNSServiceRef		ipps_ref,	/* IPPS browser */
-			local_ipps_ref;	/* Local IPPS browser */
-#    endif /* HAVE_SSL */
-#    ifdef HAVE_POLL
-  struct pollfd		pfd;		/* Polling data */
-#    else
-  fd_set		input;		/* Input set for select() */
-  struct timeval	timeout;	/* Timeout for select() */
-#    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...
-  */
-
-  (void)flags;
-
-  if (!cb)
-    return (0);
-
- /*
-  * Get the list of local printers and pass them to the callback function...
-  */
-
-  num_dests = _cupsGetDests(CUPS_HTTP_DEFAULT, IPP_OP_CUPS_GET_PRINTERS, NULL,
-                            &dests, type, mask | CUPS_PRINTER_3D);
-
-  if ((user_default = _cupsUserDefault(name, sizeof(name))) != NULL)
-    defprinter = name;
-  else if ((defprinter = cupsGetDefault2(CUPS_HTTP_DEFAULT)) != NULL)
-  {
-    strlcpy(name, defprinter, sizeof(name));
-    defprinter = name;
-  }
-
-  if (defprinter)
-  {
-   /*
-    * Separate printer and instance name...
-    */
-
-    if ((instance = strchr(name, '/')) != NULL)
-      *instance++ = '\0';
-
-   /*
-    * Lookup the printer and instance and make it the default...
-    */
-
-    if ((dest = cupsGetDest(name, instance, num_dests, dests)) != NULL)
-      dest->is_default = 1;
-  }
-
-  for (i = num_dests, dest = dests;
-       i > 0 && (!cancel || !*cancel);
-       i --, dest ++)
-    if (!(*cb)(user_data, i > 1 ? CUPS_DEST_FLAGS_MORE : CUPS_DEST_FLAGS_NONE,
-               dest))
-      break;
-
-  cupsFreeDests(num_dests, dests);
-
-  if (i > 0 || msec == 0)
-    return (1);
-
-#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
- /*
-  * Get Bonjour-shared printers...
-  */
-
-  data.type      = type;
-  data.mask      = mask;
-  data.cb        = cb;
-  data.user_data = user_data;
-  data.devices   = cupsArrayNew3((cups_array_func_t)cups_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)cups_dnssd_free_device);
-
-#  ifdef HAVE_DNSSD
-  if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)
-    return (0);
-
-  main_fd = DNSServiceRefSockFD(data.main_ref);
-
-  ipp_ref = data.main_ref;
-  DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
-                   "_ipp._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data);
-
-  local_ipp_ref = data.main_ref;
-  DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
-                   kDNSServiceInterfaceIndexLocalOnly,
-                   "_ipp._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_local_cb, &data);
-
-#    ifdef HAVE_SSL
-  ipps_ref = data.main_ref;
-  DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
-                   "_ipps._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data);
-
-  local_ipps_ref = data.main_ref;
-  DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
-                   kDNSServiceInterfaceIndexLocalOnly,
-                   "_ipps._tcp", NULL,
-                   (DNSServiceBrowseReply)cups_dnssd_local_cb, &data);
-#    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;
-  else
-    remaining = msec;
-
-  while (remaining > 0 && (!cancel || !*cancel))
-  {
-   /*
-    * Check for input...
-    */
-
-#  ifdef HAVE_DNSSD
-#    ifdef HAVE_POLL
-    pfd.fd     = main_fd;
-    pfd.events = POLLIN;
-
-    nfds = poll(&pfd, 1, remaining > 250 ? 250 : remaining);
-
-#    else
-    FD_ZERO(&input);
-    FD_SET(main_fd, &input);
-
-    timeout.tv_sec  = 0;
-    timeout.tv_usec = remaining > 250 ? 250000 : remaining * 1000;
-
-    nfds = select(main_fd + 1, &input, NULL, NULL, &timeout);
-#    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;
-         device = (_cups_dnssd_device_t *)cupsArrayNext(data.devices))
-    {
-      if (device->ref)
-        count ++;
-
-      if (!device->ref && device->state == _CUPS_DNSSD_NEW)
-      {
-	DEBUG_printf(("1cupsEnumDests: Querying '%s'.", device->fullName));
-
-#  ifdef HAVE_DNSSD
-        device->ref = data.main_ref;
-
-	if (DNSServiceQueryRecord(&(device->ref),
-				  kDNSServiceFlagsShareConnection,
-				  0, device->fullName,
-				  kDNSServiceType_TXT,
-				  kDNSServiceClass_IN,
-				  (DNSServiceQueryRecordReply)cups_dnssd_query_cb,
-				  &data) == kDNSServiceErr_NoError)
-	{
-	  count ++;
-	}
-	else
-	{
-	  device->ref   = 0;
-	  device->state = _CUPS_DNSSD_ERROR;
-
-	  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 ((device->type & mask) == type)
-        {
-	  if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest))
-	  {
-	    remaining = -1;
-	    break;
-	  }
-        }
-
-        device->state = _CUPS_DNSSD_ACTIVE;
-      }
-    }
-  }
-
-  cupsArrayDelete(data.devices);
-
-#  ifdef HAVE_DNSSD
-  DNSServiceRefDeallocate(ipp_ref);
-  DNSServiceRefDeallocate(local_ipp_ref);
-
-#    ifdef HAVE_SSL
-  DNSServiceRefDeallocate(ipp_ref);
-  DNSServiceRefDeallocate(local_ipp_ref);
-#    endif /* HAVE_SSL */
-
-  DNSServiceRefDeallocate(data.main_ref);
-
-#  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);
+  return (cups_enum_dests(CUPS_HTTP_DEFAULT, flags, msec, cancel, type, mask, cb, user_data));
 }
 
 
@@ -1242,15 +987,23 @@
 /*
  * 'cupsEnumDestsBlock()' - Enumerate available destinations with a block.
  *
- * Destinations are enumerated from one or more sources. The block receives the
- * destination name, instance, number of options, and options which can be used
- * as input to the @link cupsAddDest@ function.  The block must return 1 to
+ * Destinations are enumerated from one or more sources.  The block receives the
+ * @code user_data@ pointer and the destination pointer which can be used as
+ * input to the @link cupsCopyDest@ function.  The block must return 1 to
  * continue enumeration or 0 to stop.
  *
+ * The @code type@ and @code mask@ arguments allow the caller to filter the
+ * destinations that are enumerated.  Passing 0 for both will enumerate all
+ * printers.  The constant @code CUPS_PRINTER_DISCOVERED@ is used to filter on
+ * destinations that are available but have not yet been added locally.
+ *
  * Enumeration happens on the current thread and does not return until all
  * destinations have been enumerated or the block returns 0.
  *
- * @since CUPS 1.6/macOS 10.8@
+ * Note: The block will likely receive multiple updates for the same
+ * destinations - it is up to the caller to suppress any duplicate destinations.
+ *
+ * @since CUPS 1.6/macOS 10.8@ @exclude all@
  */
 
 int					/* O - 1 on success, 0 on failure */
@@ -1298,7 +1051,7 @@
 /*
  * 'cupsGetDest()' - Get the named destination from the list.
  *
- * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
+ * Use the @link cupsEnumDests@ or @link cupsGetDests2@ functions to get a
  * list of supported destinations for the current user.
  */
 
@@ -1363,6 +1116,8 @@
   int		port;			/* Port number */
 
 
+  DEBUG_printf(("_cupsGetDestResource(dest=%p(%s), resource=%p, resourcesize=%d)", (void *)dest, dest->name, (void *)resource, (int)resourcesize));
+
  /*
   * Range check input...
   */
@@ -1380,34 +1135,59 @@
   * Grab the printer URI...
   */
 
-  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options,
-                           dest->options)) == NULL)
+  if ((uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options)) == NULL)
   {
-    if (resource)
-      *resource = '\0';
+    if ((uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL)
+    {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      if (strstr(uri, "._tcp"))
+        uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+    }
 
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+    if (uri)
+    {
+      DEBUG_printf(("1_cupsGetDestResource: Resolved printer-uri-supported=\"%s\"", uri));
 
-    return (NULL);
-  }
+      uri = _cupsCreateDest(dest->name, cupsGetOption("printer-info", dest->num_options, dest->options), NULL, uri, resource, resourcesize);
+    }
 
-#ifdef HAVE_DNSSD
-  if (strstr(uri, "._tcp"))
-  {
-    if ((uri = cups_dnssd_resolve(dest, uri, 5000, NULL, NULL, NULL)) == NULL)
+    if (uri)
+    {
+      DEBUG_printf(("1_cupsGetDestResource: Local printer-uri-supported=\"%s\"", uri));
+
+      dest->num_options = cupsAddOption("printer-uri-supported", uri, dest->num_options, &dest->options);
+
+      uri = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
+    }
+    else
+    {
+      DEBUG_puts("1_cupsGetDestResource: No printer-uri-supported found.");
+
+      if (resource)
+        *resource = '\0';
+
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
+
       return (NULL);
+    }
   }
-#endif /* HAVE_DNSSD */
-
-  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
-                      userpass, sizeof(userpass), hostname, sizeof(hostname),
-                      &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
+  else
   {
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
+    DEBUG_printf(("1_cupsGetDestResource: printer-uri-supported=\"%s\"", uri));
 
-    return (NULL);
+    if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme),
+                        userpass, sizeof(userpass), hostname, sizeof(hostname),
+                        &port, resource, (int)resourcesize) < HTTP_URI_STATUS_OK)
+    {
+      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad printer-uri."), 1);
+
+      return (NULL);
+    }
   }
 
+  DEBUG_printf(("1_cupsGetDestResource: resource=\"%s\"", resource));
+
   return (uri);
 }
 
@@ -1434,6 +1214,7 @@
 		hostname[256],		/* Hostname from URI */
 		resource[1024],		/* Resource path from URI */
 		*ptr;			/* Pointer into string */
+  const char	*info;			/* printer-info string */
   int		port;			/* Port number from URI */
 
 
@@ -1455,7 +1236,11 @@
     return (NULL);
   }
 
-  if (!name)
+  if (name)
+  {
+    info = name;
+  }
+  else
   {
    /*
     * Create the name from the URI...
@@ -1467,24 +1252,29 @@
       * Use the service instance name...
       */
 
-      if ((ptr = strchr(hostname, '.')) != NULL)
+      if ((ptr = strstr(hostname, "._")) != NULL)
         *ptr = '\0';
 
-      name = hostname;
+      cups_queue_name(temp, hostname, sizeof(temp));
+      name = temp;
+      info = hostname;
     }
     else if (!strncmp(resource, "/classes/", 9))
     {
       snprintf(temp, sizeof(temp), "%s @ %s", resource + 9, hostname);
-      name = temp;
+      name = resource + 9;
+      info = temp;
     }
     else if (!strncmp(resource, "/printers/", 10))
     {
       snprintf(temp, sizeof(temp), "%s @ %s", resource + 10, hostname);
-      name = temp;
+      name = resource + 10;
+      info = temp;
     }
     else
     {
       name = hostname;
+      info = hostname;
     }
   }
 
@@ -1500,7 +1290,7 @@
 
   dest->name        = _cupsStrAlloc(name);
   dest->num_options = cupsAddOption("device-uri", uri, dest->num_options, &(dest->options));
-  dest->num_options = cupsAddOption("printer-info", name, dest->num_options, &(dest->options));
+  dest->num_options = cupsAddOption("printer-info", info, dest->num_options, &(dest->options));
 
   return (dest);
 }
@@ -1574,6 +1364,7 @@
 		  "printer-info",
 		  "printer-is-accepting-jobs",
 		  "printer-is-shared",
+                  "printer-is-temporary",
 		  "printer-location",
 		  "printer-make-and-model",
 		  "printer-mandatory-job-attributes",
@@ -1586,12 +1377,15 @@
 		};
 
 
+  DEBUG_printf(("_cupsGetDests(http=%p, op=%x(%s), name=\"%s\", dests=%p, type=%x, mask=%x)", (void *)http, op, ippOpString(op), name, (void *)dests, type, mask));
+
 #ifdef __APPLE__
  /*
   * Get the default paper size...
   */
 
   appleGetPaperSize(media_default, sizeof(media_default));
+  DEBUG_printf(("1_cupsGetDests: Default media is '%s'.", media_default));
 #endif /* __APPLE__ */
 
  /*
@@ -1678,7 +1472,8 @@
 	    !strcmp(attr->name, "marker-types") ||
 	    !strcmp(attr->name, "printer-commands") ||
 	    !strcmp(attr->name, "printer-info") ||
-	    !strcmp(attr->name, "printer-is-shared") ||
+            !strcmp(attr->name, "printer-is-shared") ||
+            !strcmp(attr->name, "printer-is-temporary") ||
 	    !strcmp(attr->name, "printer-make-and-model") ||
 	    !strcmp(attr->name, "printer-mandatory-job-attributes") ||
 	    !strcmp(attr->name, "printer-state") ||
@@ -1699,7 +1494,7 @@
 				      num_options, &options);
 	}
 #ifdef __APPLE__
-	else if (!strcmp(attr->name, "media-supported"))
+	else if (!strcmp(attr->name, "media-supported") && media_default[0])
 	{
 	 /*
 	  * See if we can set a default media size...
@@ -1710,8 +1505,8 @@
 	  for (i = 0; i < attr->num_values; i ++)
 	    if (!_cups_strcasecmp(media_default, attr->values[i].string.text))
 	    {
-	      num_options = cupsAddOption("media", media_default, num_options,
-	                                  &options);
+              DEBUG_printf(("1_cupsGetDests: Setting media to '%s'.", media_default));
+	      num_options = cupsAddOption("media", media_default, num_options, &options);
               break;
 	    }
 	}
@@ -1720,7 +1515,8 @@
 	         attr->value_tag == IPP_TAG_NAME)
 	  printer_name = attr->values[0].string.text;
         else if (strncmp(attr->name, "notify-", 7) &&
-	         (attr->value_tag == IPP_TAG_BOOLEAN ||
+                 strncmp(attr->name, "print-quality-", 14) &&
+                 (attr->value_tag == IPP_TAG_BOOLEAN ||
 		  attr->value_tag == IPP_TAG_ENUM ||
 		  attr->value_tag == IPP_TAG_INTEGER ||
 		  attr->value_tag == IPP_TAG_KEYWORD ||
@@ -1735,12 +1531,8 @@
           strlcpy(optname, attr->name, sizeof(optname));
 	  optname[ptr - attr->name] = '\0';
 
-	  if (_cups_strcasecmp(optname, "media") ||
-	      !cupsGetOption("media", num_options, options))
-	    num_options = cupsAddOption(optname,
-					cups_make_string(attr, value,
-							 sizeof(value)),
-					num_options, &options);
+	  if (_cups_strcasecmp(optname, "media") || !cupsGetOption("media", num_options, options))
+	    num_options = cupsAddOption(optname, cups_make_string(attr, value, sizeof(value)), num_options, &options);
 	}
       }
 
@@ -1785,15 +1577,23 @@
  * 'cupsGetDests()' - Get the list of destinations from the default server.
  *
  * Starting with CUPS 1.2, the returned list of destinations include the
- * printer-info, printer-is-accepting-jobs, printer-is-shared,
- * printer-make-and-model, printer-state, printer-state-change-time,
- * printer-state-reasons, and printer-type attributes as options.  CUPS 1.4
- * adds the marker-change-time, marker-colors, marker-high-levels,
- * marker-levels, marker-low-levels, marker-message, marker-names,
- * marker-types, and printer-commands attributes as well.
+ * "printer-info", "printer-is-accepting-jobs", "printer-is-shared",
+ * "printer-make-and-model", "printer-state", "printer-state-change-time",
+ * "printer-state-reasons", "printer-type", and "printer-uri-supported"
+ * attributes as options.
+ *
+ * CUPS 1.4 adds the "marker-change-time", "marker-colors",
+ * "marker-high-levels", "marker-levels", "marker-low-levels", "marker-message",
+ * "marker-names", "marker-types", and "printer-commands" attributes as options.
+ *
+ * CUPS 2.2 adds accessible IPP printers to the list of destinations that can
+ * be used.  The "printer-uri-supported" option will be present for those IPP
+ * printers that have been recently used.
  *
  * Use the @link cupsFreeDests@ function to free the destination list and
  * the @link cupsGetDest@ function to find a particular destination.
+ *
+ * @exclude all@
  */
 
 int					/* O - Number of destinations */
@@ -1807,12 +1607,18 @@
  * 'cupsGetDests2()' - Get the list of destinations from the specified server.
  *
  * Starting with CUPS 1.2, the returned list of destinations include the
- * printer-info, printer-is-accepting-jobs, printer-is-shared,
- * printer-make-and-model, printer-state, printer-state-change-time,
- * printer-state-reasons, and printer-type attributes as options.  CUPS 1.4
- * adds the marker-change-time, marker-colors, marker-high-levels,
- * marker-levels, marker-low-levels, marker-message, marker-names,
- * marker-types, and printer-commands attributes as well.
+ * "printer-info", "printer-is-accepting-jobs", "printer-is-shared",
+ * "printer-make-and-model", "printer-state", "printer-state-change-time",
+ * "printer-state-reasons", "printer-type", and "printer-uri-supported"
+ * attributes as options.
+ *
+ * CUPS 1.4 adds the "marker-change-time", "marker-colors",
+ * "marker-high-levels", "marker-levels", "marker-low-levels", "marker-message",
+ * "marker-names", "marker-types", and "printer-commands" attributes as options.
+ *
+ * CUPS 2.2 adds accessible IPP printers to the list of destinations that can
+ * be used.  The "printer-uri-supported" option will be present for those IPP
+ * printers that have been recently used.
  *
  * Use the @link cupsFreeDests@ function to free the destination list and
  * the @link cupsGetDest@ function to find a particular destination.
@@ -1824,8 +1630,8 @@
 cupsGetDests2(http_t      *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
               cups_dest_t **dests)	/* O - Destinations */
 {
-  int		num_dests;		/* Number of destinations */
-  cups_dest_t	*dest;			/* Destination pointer */
+  _cups_getdata_t data;                 /* Enumeration data */
+  cups_dest_t   *dest;                  /* Current destination */
   const char	*home;			/* HOME environment variable */
   char		filename[1024];		/* Local ~/.cups/lpoptions file */
   const char	*defprinter;		/* Default printer */
@@ -1837,41 +1643,70 @@
   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
 
 
- /*
+  DEBUG_printf(("cupsGetDests2(http=%p, dests=%p)", (void *)http, (void *)dests));
+
+/*
   * Range check the input...
   */
 
   if (!dests)
   {
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad NULL dests pointer"), 1);
+    DEBUG_puts("1cupsGetDests2: NULL dests pointer, returning 0.");
     return (0);
   }
 
  /*
+  * Connect to the server as needed...
+  */
+
+  if (!http)
+  {
+    if ((http = _cupsConnect()) == NULL)
+    {
+      *dests = NULL;
+
+      return (0);
+    }
+  }
+
+ /*
   * Grab the printers and classes...
   */
 
-  *dests    = (cups_dest_t *)0;
-  num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, dests, 0, CUPS_PRINTER_3D);
+  data.num_dests = 0;
+  data.dests     = NULL;
 
-  if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+  if (!httpAddrLocalhost(httpGetAddress(http)))
   {
-    cupsFreeDests(num_dests, *dests);
-    *dests = (cups_dest_t *)0;
-    return (0);
+   /*
+    * When talking to a remote cupsd, just enumerate printers on the remote
+    * cupsd.
+    */
+
+    cups_enum_dests(http, 0, _CUPS_DNSSD_GET_DESTS, NULL, 0, CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)cups_get_cb, &data);
+  }
+  else
+  {
+   /*
+    * When talking to a local cupsd, enumerate both local printers and ones we
+    * can find on the network...
+    */
+
+    cups_enum_dests(http, 0, _CUPS_DNSSD_GET_DESTS, NULL, 0, 0, (cups_dest_cb_t)cups_get_cb, &data);
   }
 
  /*
   * Make a copy of the "real" queues for a later sanity check...
   */
 
-  if (num_dests > 0)
+  if (data.num_dests > 0)
   {
-    num_reals = num_dests;
+    num_reals = data.num_dests;
     reals     = calloc((size_t)num_reals, sizeof(cups_dest_t));
 
     if (reals)
-      memcpy(reals, *dests, (size_t)num_reals * sizeof(cups_dest_t));
+      memcpy(reals, data.dests, (size_t)num_reals * sizeof(cups_dest_t));
     else
       num_reals = 0;
   }
@@ -1906,7 +1741,7 @@
     * Lookup the printer and instance and make it the default...
     */
 
-    if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
+    if ((dest = cupsGetDest(name, instance, data.num_dests, data.dests)) != NULL)
       dest->is_default = 1;
   }
   else
@@ -1917,15 +1752,13 @@
   */
 
   snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
-  num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
-                             num_dests, dests);
+  data.num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL, data.num_dests, &data.dests);
 
   if ((home = getenv("HOME")) != NULL)
   {
     snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
 
-    num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL,
-                               num_dests, dests);
+    data.num_dests = cups_get_dests(filename, NULL, NULL, user_default != NULL, data.num_dests, &data.dests);
   }
 
  /*
@@ -1940,7 +1773,7 @@
     * See if we have a default printer...
     */
 
-    if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
+    if ((dest = cupsGetDest(NULL, NULL, data.num_dests, data.dests)) != NULL)
     {
      /*
       * Have a default; see if it is real...
@@ -1953,8 +1786,7 @@
         * going to an unexpected printer... (<rdar://problem/14216472>)
         */
 
-        num_dests = cupsRemoveDest(dest->name, dest->instance, num_dests,
-                                   dests);
+        data.num_dests = cupsRemoveDest(dest->name, dest->instance, data.num_dests, &data.dests);
       }
     }
 
@@ -1969,10 +1801,14 @@
   * Return the number of destinations...
   */
 
-  if (num_dests > 0)
+  *dests = data.dests;
+
+  if (data.num_dests > 0)
     _cupsSetError(IPP_STATUS_OK, NULL, 0);
 
-  return (num_dests);
+  DEBUG_printf(("1cupsGetDests2: Returning %d destinations.", data.num_dests));
+
+  return (data.num_dests);
 }
 
 
@@ -1980,10 +1816,10 @@
  * 'cupsGetNamedDest()' - Get options for the named destination.
  *
  * This function is optimized for retrieving a single destination and should
- * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
- * know the name of the destination or want to print to the default destination.
- * If @code NULL@ is returned, the destination does not exist or there is no
- * default destination.
+ * be used instead of @link cupsGetDests2@ and @link cupsGetDest@ when you
+ * either know the name of the destination or want to print to the default
+ * destination.  If @code NULL@ is returned, the destination does not exist or
+ * there is no default destination.
  *
  * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
  * server will be used.
@@ -2002,6 +1838,7 @@
                  const char *name,	/* I - Destination name or @code NULL@ for the default destination */
                  const char *instance)	/* I - Instance name or @code NULL@ */
 {
+  const char    *dest_name;             /* Working destination name */
   cups_dest_t	*dest;			/* Destination */
   char		filename[1024],		/* Path to lpoptions */
 		defname[256];		/* Default printer name */
@@ -2012,16 +1849,20 @@
   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
 
 
+  DEBUG_printf(("cupsGetNamedDest(http=%p, name=\"%s\", instance=\"%s\")", (void *)http, name, instance));
+
  /*
   * If "name" is NULL, find the default destination...
   */
 
-  if (!name)
+  dest_name = name;
+
+  if (!dest_name)
   {
     set_as_default = 1;
-    name           = _cupsUserDefault(defname, sizeof(defname));
+    dest_name      = _cupsUserDefault(defname, sizeof(defname));
 
-    if (name)
+    if (dest_name)
     {
       char	*ptr;			/* Temporary pointer... */
 
@@ -2041,36 +1882,60 @@
 
       snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
 
-      name = cups_get_default(filename, defname, sizeof(defname), &instance);
+      dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
     }
 
-    if (!name)
+    if (!dest_name)
     {
      /*
       * Still not there?  Try the system lpoptions file...
       */
 
-      snprintf(filename, sizeof(filename), "%s/lpoptions",
-	       cg->cups_serverroot);
-      name = cups_get_default(filename, defname, sizeof(defname), &instance);
+      snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
+      dest_name = cups_get_default(filename, defname, sizeof(defname), &instance);
     }
 
-    if (!name)
+    if (!dest_name)
     {
      /*
       * No locally-set default destination, ask the server...
       */
 
       op = IPP_OP_CUPS_GET_DEFAULT;
+
+      DEBUG_puts("1cupsGetNamedDest: Asking server for default printer...");
     }
+    else
+      DEBUG_printf(("1cupsGetNamedDest: Using name=\"%s\"...", name));
   }
 
  /*
   * Get the printer's attributes...
   */
 
-  if (!_cupsGetDests(http, op, name, &dest, 0, CUPS_PRINTER_3D))
-    return (NULL);
+  if (!_cupsGetDests(http, op, dest_name, &dest, 0, 0))
+  {
+    if (name)
+    {
+      _cups_namedata_t  data;           /* Callback data */
+
+      DEBUG_puts("1cupsGetNamedDest: No queue found for printer, looking on network...");
+
+      data.name = name;
+      data.dest = NULL;
+
+      cupsEnumDests(0, 1000, NULL, 0, 0, (cups_dest_cb_t)cups_name_cb, &data);
+
+      if (!data.dest)
+        return (NULL);
+
+      dest = data.dest;
+    }
+    else
+      return (NULL);
+  }
+
+  DEBUG_printf(("1cupsGetNamedDest: Got dest=%p", (void *)dest));
 
   if (instance)
     dest->instance = _cupsStrAlloc(instance);
@@ -2083,13 +1948,13 @@
   */
 
   snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
-  cups_get_dests(filename, name, instance, 1, 1, &dest);
+  cups_get_dests(filename, dest_name, instance, 1, 1, &dest);
 
   if (home)
   {
     snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
 
-    cups_get_dests(filename, name, instance, 1, 1, &dest);
+    cups_get_dests(filename, dest_name, instance, 1, 1, &dest);
   }
 
  /*
@@ -2193,6 +2058,8 @@
  *
  * This function saves the destinations to /etc/cups/lpoptions when run
  * as root and ~/.cups/lpoptions when run as a normal user.
+ *
+ * @exclude all@
  */
 
 void
@@ -2223,9 +2090,9 @@
   cups_option_t	*option;		/* Current option */
   _ipp_option_t	*match;			/* Matching attribute for option */
   FILE		*fp;			/* File pointer */
-#ifndef WIN32
+#ifndef _WIN32
   const char	*home;			/* HOME environment variable */
-#endif /* WIN32 */
+#endif /* _WIN32 */
   char		filename[1024];		/* lpoptions file */
   int		num_temps;		/* Number of temporary destinations */
   cups_dest_t	*temps = NULL,		/* Temporary destinations */
@@ -2245,7 +2112,7 @@
   * Get the server destinations...
   */
 
-  num_temps = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &temps, 0, CUPS_PRINTER_3D);
+  num_temps = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &temps, 0, 0);
 
   if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
   {
@@ -2259,7 +2126,7 @@
 
   snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
 
-#ifndef WIN32
+#ifndef _WIN32
   if (getuid())
   {
    /*
@@ -2285,7 +2152,7 @@
       snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
     }
   }
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
  /*
   * Try to open the file...
@@ -2297,7 +2164,7 @@
     return (-1);
   }
 
-#ifndef WIN32
+#ifndef _WIN32
  /*
   * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
   * file...
@@ -2305,7 +2172,7 @@
 
   if (!getuid())
     fchmod(fileno(fp), 0644);
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
  /*
   * Write each printer; each line looks like:
@@ -2839,11 +2706,12 @@
   (void)protocol;
   (void)context;
 
+  DEBUG_printf(("cups_dnssd_browse_cb(..., name=\"%s\", type=\"%s\", domain=\"%s\", ...);", name, type, domain));
+
   switch (event)
   {
     case AVAHI_BROWSER_FAILURE:
-	DEBUG_printf(("cups_dnssd_browse_cb: %s",
-		      avahi_strerror(avahi_client_errno(client))));
+	DEBUG_printf(("cups_dnssd_browse_cb: %s", avahi_strerror(avahi_client_errno(client))));
 	avahi_simple_poll_quit(data->simple_poll);
 	break;
 
@@ -2858,8 +2726,7 @@
 	  * This comes from the local machine so ignore it.
 	  */
 
-	  DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".",
-	                name));
+	  DEBUG_printf(("cups_dnssd_browse_cb: Ignoring local service \"%s\".", name));
 	}
 	else
 	{
@@ -2871,9 +2738,13 @@
 	}
 	break;
 
-    case AVAHI_BROWSER_REMOVE:
-    case AVAHI_BROWSER_ALL_FOR_NOW:
-    case AVAHI_BROWSER_CACHE_EXHAUSTED:
+    case AVAHI_BROWSER_REMOVE :
+    case AVAHI_BROWSER_CACHE_EXHAUSTED :
+        break;
+
+    case AVAHI_BROWSER_ALL_FOR_NOW :
+        DEBUG_puts("cups_dnssd_browse_cb: ALL_FOR_NOW");
+        data->browsers --;
         break;
   }
 }
@@ -2895,6 +2766,8 @@
 
   (void)client;
 
+  DEBUG_printf(("cups_dnssd_client_cb(client=%p, state=%d, context=%p)", client, state, context));
+
  /*
   * If the connection drops, quit.
   */
@@ -2964,8 +2837,9 @@
 {
   _cups_dnssd_device_t	key,		/* Search key */
 			*device;	/* Device */
-  char			fullName[kDNSServiceMaxDomainName];
+  char			fullName[kDNSServiceMaxDomainName],
 					/* Full name for query */
+			name[128];	/* Queue name */
 
 
   DEBUG_printf(("5cups_dnssd_get_device(data=%p, serviceName=\"%s\", regtype=\"%s\", replyDomain=\"%s\")", (void *)data, serviceName, regtype, replyDomain));
@@ -2974,7 +2848,9 @@
   * See if this is an existing device...
   */
 
-  key.dest.name = (char *)serviceName;
+  cups_queue_name(name, serviceName, sizeof(name));
+
+  key.dest.name = name;
 
   if ((device = cupsArrayFind(data->devices, &key)) != NULL)
   {
@@ -3035,10 +2911,12 @@
                   replyDomain));
 
     device            = calloc(sizeof(_cups_dnssd_device_t), 1);
-    device->dest.name = _cupsStrAlloc(serviceName);
+    device->dest.name = _cupsStrAlloc(name);
     device->domain    = _cupsStrAlloc(replyDomain);
     device->regtype   = _cupsStrAlloc(regtype);
 
+    device->dest.num_options = cupsAddOption("printer-info", serviceName, 0, &device->dest.options);
+
     cupsArrayAdd(data->devices, device);
   }
 
@@ -3047,11 +2925,9 @@
   */
 
 #  ifdef HAVE_DNSSD
-  DNSServiceConstructFullName(fullName, device->dest.name, device->regtype,
-			      device->domain);
+  DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
 #  else /* HAVE_AVAHI */
-  avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName,
-                          regtype, replyDomain);
+  avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain);
 #  endif /* HAVE_DNSSD */
 
   _cupsStrFree(device->fullName);
@@ -3070,6 +2946,8 @@
 
   if (device->state == _CUPS_DNSSD_ACTIVE)
   {
+    DEBUG_printf(("6cups_dnssd_get_device: Remove callback for \"%s\".", device->dest.name));
+
     (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
     device->state = _CUPS_DNSSD_NEW;
   }
@@ -3128,7 +3006,10 @@
   }
 
   if (device->state == _CUPS_DNSSD_ACTIVE)
+  {
+    DEBUG_printf(("6cups_dnssd_local_cb: Remove callback for \"%s\".", device->dest.name));
     (*data->cb)(data->user_data, CUPS_DEST_FLAGS_REMOVED, &device->dest);
+  }
 
   device->state = _CUPS_DNSSD_LOCAL;
 }
@@ -3141,7 +3022,9 @@
  *
  * Note: This function is needed because avahi_simple_poll_iterate is broken
  *       and always uses a timeout of 0 (!) milliseconds.
- *       (Avahi Ticket #364)
+ *       (https://github.com/lathiat/avahi/issues/127)
+ *
+ * @private@
  */
 
 static int				/* O - Number of file descriptors matching */
@@ -3156,16 +3039,22 @@
   int			val;		/* Return value */
 
 
+  DEBUG_printf(("cups_dnssd_poll_cb(pollfds=%p, num_pollfds=%d, timeout=%d, context=%p)", pollfds, num_pollfds, timeout, context));
+
   (void)timeout;
 
-  val = poll(pollfds, num_pollfds, 250);
+  val = poll(pollfds, num_pollfds, _CUPS_DNSSD_MAXTIME);
+
+  DEBUG_printf(("cups_dnssd_poll_cb: poll() returned %d", val));
 
   if (val < 0)
   {
     DEBUG_printf(("cups_dnssd_poll_cb: %s", strerror(errno)));
   }
   else if (val > 0)
+  {
     data->got_data = 1;
+  }
 
   return (val);
 }
@@ -3214,7 +3103,8 @@
 #  endif /* HAVE_DNSSD */
   _cups_dnssd_data_t	*data = (_cups_dnssd_data_t *)context;
 					/* Enumeration data */
-  char			name[1024],	/* Service name */
+  char			serviceName[256],/* Service name */
+			name[128],	/* Queue name */
 			*ptr;		/* Pointer into string */
   _cups_dnssd_device_t	dkey,		/* Search key */
 			*device;	/* Device */
@@ -3231,11 +3121,7 @@
     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));
+  DEBUG_printf(("cups_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...
@@ -3244,8 +3130,7 @@
   if (event != AVAHI_BROWSER_NEW)
   {
     if (event == AVAHI_BROWSER_FAILURE)
-      DEBUG_printf(("cups_dnssd_query_cb: %s",
-		    avahi_strerror(avahi_client_errno(client))));
+      DEBUG_printf(("cups_dnssd_query_cb: %s", avahi_strerror(avahi_client_errno(client))));
 
     return;
   }
@@ -3255,14 +3140,16 @@
   * Lookup the service in the devices array.
   */
 
-  dkey.dest.name = name;
+  cups_dnssd_unquote(serviceName, fullName, sizeof(serviceName));
 
-  cups_dnssd_unquote(name, fullName, sizeof(name));
-
-  if ((ptr = strstr(name, "._")) != NULL)
+  if ((ptr = strstr(serviceName, "._")) != NULL)
     *ptr = '\0';
 
-  if ((device = cupsArrayFind(data->devices, &dkey)) != NULL)
+  cups_queue_name(name, serviceName, sizeof(name));
+
+  dkey.dest.name = name;
+
+  if ((device = cupsArrayFind(data->devices, &dkey)) != NULL && device->state == _CUPS_DNSSD_NEW)
   {
    /*
     * Found it, pull out the make and model from the TXT record and save it...
@@ -3279,7 +3166,7 @@
 			model[256],	/* Model */
 			uriname[1024],	/* Name for URI */
 			uri[1024];	/* Printer URI */
-    cups_ptype_t	type = CUPS_PRINTER_REMOTE | CUPS_PRINTER_BW;
+    cups_ptype_t	type = CUPS_PRINTER_DISCOVERED | CUPS_PRINTER_BW;
 					/* Printer type */
     int			saw_printer_type = 0;
 					/* Did we see a printer-type key? */
@@ -3398,7 +3285,7 @@
         */
 
 	saw_printer_type = 1;
-        type             = (cups_ptype_t)strtol(value, NULL, 0);
+        type             = (cups_ptype_t)strtol(value, NULL, 0) | CUPS_PRINTER_DISCOVERED;
       }
       else if (!saw_printer_type)
       {
@@ -3454,8 +3341,6 @@
     * 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));
@@ -3543,9 +3428,9 @@
   * Save the resolved URI...
   */
 
-  dest->num_options = cupsAddOption("resolved-device-uri", uri, dest->num_options, &dest->options);
+  dest->num_options = cupsAddOption("device-uri", uri, dest->num_options, &dest->options);
 
-  return (cupsGetOption("resolved-device-uri", dest->num_options, dest->options));
+  return (cupsGetOption("device-uri", dest->num_options, dest->options));
 }
 
 
@@ -3620,6 +3505,476 @@
 #endif /* HAVE_DNSSD */
 
 
+#if defined(HAVE_AVAHI) || defined(HAVE_DNSSD)
+/*
+ * 'cups_elapsed()' - Return the elapsed time in milliseconds.
+ */
+
+static int				/* O  - Elapsed time in milliseconds */
+cups_elapsed(struct timeval *t)		/* IO - Previous time */
+{
+  int			msecs;		/* Milliseconds */
+  struct timeval	nt;		/* New time */
+
+
+  gettimeofday(&nt, NULL);
+
+  msecs = (int)(1000 * (nt.tv_sec - t->tv_sec) + (nt.tv_usec - t->tv_usec) / 1000);
+
+  *t = nt;
+
+  return (msecs);
+}
+#endif /* HAVE_AVAHI || HAVE_DNSSD */
+
+
+/*
+ * 'cups_enum_dests()' - Enumerate destinations from a specific server.
+ */
+
+static int                              /* O - 1 on success, 0 on failure */
+cups_enum_dests(
+  http_t         *http,                 /* I - Connection to scheduler */
+  unsigned       flags,                 /* I - Enumeration flags */
+  int            msec,                  /* I - Timeout in milliseconds, -1 for indefinite */
+  int            *cancel,               /* I - Pointer to "cancel" variable */
+  cups_ptype_t   type,                  /* I - Printer type bits */
+  cups_ptype_t   mask,                  /* I - Mask for printer type bits */
+  cups_dest_cb_t cb,                    /* I - Callback function */
+  void           *user_data)            /* I - User data */
+{
+  int           i,                      /* Looping var */
+                num_dests;              /* Number of destinations */
+  cups_dest_t   *dests = NULL,          /* Destinations */
+                *dest;                  /* Current destination */
+  const char    *defprinter;            /* Default printer */
+  char          name[1024],             /* Copy of printer name */
+                *instance,              /* Pointer to instance name */
+                *user_default;          /* User default printer */
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  int           count,                  /* Number of queries started */
+                completed,              /* Number of completed queries */
+                remaining;              /* Remainder of timeout */
+  struct timeval curtime;               /* Current time */
+  _cups_dnssd_data_t data;              /* Data for callback */
+  _cups_dnssd_device_t *device;         /* Current device */
+#  ifdef HAVE_DNSSD
+  int           nfds,                   /* Number of files responded */
+                main_fd;                /* File descriptor for lookups */
+  DNSServiceRef ipp_ref = NULL,         /* IPP browser */
+                local_ipp_ref = NULL;   /* Local IPP browser */
+#    ifdef HAVE_SSL
+  DNSServiceRef ipps_ref = NULL,        /* IPPS browser */
+                local_ipps_ref = NULL;  /* Local IPPS browser */
+#    endif /* HAVE_SSL */
+#    ifdef HAVE_POLL
+  struct pollfd pfd;                    /* Polling data */
+#    else
+  fd_set        input;                  /* Input set for select() */
+  struct timeval timeout;               /* Timeout for select() */
+#    endif /* HAVE_POLL */
+#  else /* HAVE_AVAHI */
+  int           error;                  /* Error value */
+  AvahiServiceBrowser *ipp_ref = NULL;  /* IPP browser */
+#    ifdef HAVE_SSL
+  AvahiServiceBrowser *ipps_ref = NULL; /* IPPS browser */
+#    endif /* HAVE_SSL */
+#  endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+
+  DEBUG_printf(("cups_enum_dests(flags=%x, msec=%d, cancel=%p, type=%x, mask=%x, cb=%p, user_data=%p)", flags, msec, (void *)cancel, type, mask, (void *)cb, (void *)user_data));
+
+ /*
+  * Range check input...
+  */
+
+  (void)flags;
+
+  if (!cb)
+  {
+    DEBUG_puts("1cups_enum_dests: No callback, returning 0.");
+    return (0);
+  }
+
+ /*
+  * Get ready to enumerate...
+  */
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  memset(&data, 0, sizeof(data));
+
+  data.type      = type;
+  data.mask      = mask;
+  data.cb        = cb;
+  data.user_data = user_data;
+  data.devices   = cupsArrayNew3((cups_array_func_t)cups_dnssd_compare_devices, NULL, NULL, 0, NULL, (cups_afree_func_t)cups_dnssd_free_device);
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+  if (!(mask & CUPS_PRINTER_DISCOVERED) || !(type & CUPS_PRINTER_DISCOVERED))
+  {
+   /*
+    * Get the list of local printers and pass them to the callback function...
+    */
+
+    num_dests = _cupsGetDests(http, IPP_OP_CUPS_GET_PRINTERS, NULL, &dests, type, mask);
+
+    if ((user_default = _cupsUserDefault(name, sizeof(name))) != NULL)
+      defprinter = name;
+    else if ((defprinter = cupsGetDefault2(http)) != NULL)
+    {
+      strlcpy(name, defprinter, sizeof(name));
+      defprinter = name;
+    }
+
+    if (defprinter)
+    {
+     /*
+      * Separate printer and instance name...
+      */
+
+      if ((instance = strchr(name, '/')) != NULL)
+        *instance++ = '\0';
+
+     /*
+      * Lookup the printer and instance and make it the default...
+      */
+
+      if ((dest = cupsGetDest(name, instance, num_dests, dests)) != NULL)
+        dest->is_default = 1;
+    }
+
+    for (i = num_dests, dest = dests;
+         i > 0 && (!cancel || !*cancel);
+         i --, dest ++)
+    {
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      const char *device_uri;    /* Device URI */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+      if (!(*cb)(user_data, i > 1 ? CUPS_DEST_FLAGS_MORE : CUPS_DEST_FLAGS_NONE, dest))
+        break;
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+      if (!dest->instance && (device_uri = cupsGetOption("device-uri", dest->num_options, dest->options)) != NULL && !strncmp(device_uri, "dnssd://", 8))
+      {
+       /*
+        * Add existing queue using service name, etc. so we don't list it again...
+        */
+
+        char    scheme[32],             /* URI scheme */
+                userpass[32],           /* Username:password */
+                serviceName[256],       /* Service name (host field) */
+                resource[256],          /* Resource (options) */
+                *regtype,               /* Registration type */
+                *replyDomain;           /* Registration domain */
+        int     port;                   /* Port number (not used) */
+
+        if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), serviceName, sizeof(serviceName), &port, resource, sizeof(resource)) >= HTTP_URI_STATUS_OK)
+        {
+          if ((regtype = strstr(serviceName, "._ipp")) != NULL)
+          {
+            *regtype++ = '\0';
+
+            if ((replyDomain = strstr(regtype, "._tcp.")) != NULL)
+            {
+              replyDomain[5] = '\0';
+              replyDomain += 6;
+
+              if ((device = cups_dnssd_get_device(&data, serviceName, regtype, replyDomain)) != NULL)
+                device->state = _CUPS_DNSSD_ACTIVE;
+            }
+          }
+        }
+      }
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+    }
+
+    cupsFreeDests(num_dests, dests);
+
+    if (i > 0 || msec == 0)
+      goto enum_finished;
+  }
+
+ /*
+  * Return early if the caller doesn't want to do discovery...
+  */
+
+  if ((mask & CUPS_PRINTER_DISCOVERED) && !(type & CUPS_PRINTER_DISCOVERED))
+    goto enum_finished;
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+ /*
+  * Get Bonjour-shared printers...
+  */
+
+  gettimeofday(&curtime, NULL);
+
+#  ifdef HAVE_DNSSD
+  if (DNSServiceCreateConnection(&data.main_ref) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create service browser, returning 0.");
+    return (0);
+  }
+
+  main_fd = DNSServiceRefSockFD(data.main_ref);
+
+  ipp_ref = data.main_ref;
+  if (DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0, "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create IPP browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+
+  local_ipp_ref = data.main_ref;
+  if (DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, "_ipp._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_local_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create local IPP browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+
+#    ifdef HAVE_SSL
+  ipps_ref = data.main_ref;
+  if (DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0, "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_browse_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create IPPS browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+
+  local_ipps_ref = data.main_ref;
+  if (DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, "_ipps._tcp", NULL, (DNSServiceBrowseReply)cups_dnssd_local_cb, &data) != kDNSServiceErr_NoError)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create local IPPS browser, returning 0.");
+    DNSServiceRefDeallocate(data.main_ref);
+    return (0);
+  }
+#    endif /* HAVE_SSL */
+
+#  else /* HAVE_AVAHI */
+  if ((data.simple_poll = avahi_simple_poll_new()) == NULL)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create Avahi poll, returning 0.");
+    return (0);
+  }
+
+  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("1cups_enum_dests: Unable to create Avahi client, returning 0.");
+    avahi_simple_poll_free(data.simple_poll);
+    return (0);
+  }
+
+  data.browsers = 1;
+  if ((ipp_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ipp._tcp", NULL, 0, cups_dnssd_browse_cb, &data)) == NULL)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create Avahi IPP browser, returning 0.");
+
+    avahi_client_free(data.client);
+    avahi_simple_poll_free(data.simple_poll);
+    return (0);
+  }
+
+#    ifdef HAVE_SSL
+  data.browsers ++;
+  if ((ipps_ref = avahi_service_browser_new(data.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ipps._tcp", NULL, 0, cups_dnssd_browse_cb, &data)) == NULL)
+  {
+    DEBUG_puts("1cups_enum_dests: Unable to create Avahi IPPS browser, returning 0.");
+
+    avahi_service_browser_free(ipp_ref);
+    avahi_client_free(data.client);
+    avahi_simple_poll_free(data.simple_poll);
+    return (0);
+  }
+#    endif /* HAVE_SSL */
+#  endif /* HAVE_DNSSD */
+
+  if (msec < 0)
+    remaining = INT_MAX;
+  else
+    remaining = msec;
+
+  while (remaining > 0 && (!cancel || !*cancel))
+  {
+   /*
+    * Check for input...
+    */
+
+    DEBUG_printf(("1cups_enum_dests: remaining=%d", remaining));
+
+    cups_elapsed(&curtime);
+
+#  ifdef HAVE_DNSSD
+#    ifdef HAVE_POLL
+    pfd.fd     = main_fd;
+    pfd.events = POLLIN;
+
+    nfds = poll(&pfd, 1, remaining > _CUPS_DNSSD_MAXTIME ? _CUPS_DNSSD_MAXTIME : remaining);
+
+#    else
+    FD_ZERO(&input);
+    FD_SET(main_fd, &input);
+
+    timeout.tv_sec  = 0;
+    timeout.tv_usec = 1000 * (remaining > _CUPS_DNSSD_MAXTIME ? _CUPS_DNSSD_MAXTIME : remaining);
+
+    nfds = select(main_fd + 1, &input, NULL, NULL, &timeout);
+#    endif /* HAVE_POLL */
+
+    if (nfds > 0)
+      DNSServiceProcessResult(data.main_ref);
+    else if (nfds < 0 && errno != EINTR && errno != EAGAIN)
+      break;
+
+#  else /* HAVE_AVAHI */
+    data.got_data = 0;
+
+    if ((error = avahi_simple_poll_iterate(data.simple_poll, _CUPS_DNSSD_MAXTIME)) > 0)
+    {
+     /*
+      * We've been told to exit the loop.  Perhaps the connection to
+      * Avahi failed.
+      */
+
+      break;
+    }
+
+    DEBUG_printf(("1cups_enum_dests: got_data=%d", data.got_data));
+#  endif /* HAVE_DNSSD */
+
+    remaining -= cups_elapsed(&curtime);
+
+    for (device = (_cups_dnssd_device_t *)cupsArrayFirst(data.devices),
+             count = 0, completed = 0;
+         device;
+         device = (_cups_dnssd_device_t *)cupsArrayNext(data.devices))
+    {
+      if (device->ref)
+        count ++;
+
+      if (device->state == _CUPS_DNSSD_ACTIVE)
+        completed ++;
+
+      if (!device->ref && device->state == _CUPS_DNSSD_NEW)
+      {
+        DEBUG_printf(("1cups_enum_dests: Querying '%s'.", device->fullName));
+
+#  ifdef HAVE_DNSSD
+        device->ref = data.main_ref;
+
+        if (DNSServiceQueryRecord(&(device->ref), kDNSServiceFlagsShareConnection, 0, device->fullName, kDNSServiceType_TXT, kDNSServiceClass_IN, (DNSServiceQueryRecordReply)cups_dnssd_query_cb, &data) == kDNSServiceErr_NoError)
+        {
+          count ++;
+        }
+        else
+        {
+          device->ref   = 0;
+          device->state = _CUPS_DNSSD_ERROR;
+
+          DEBUG_puts("1cups_enum_dests: 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)
+        {
+          DEBUG_printf(("1cups_enum_dests: Query ref=%p", device->ref));
+          count ++;
+        }
+        else
+        {
+          device->state = _CUPS_DNSSD_ERROR;
+
+          DEBUG_printf(("1cups_enum_dests: Query failed: %s", avahi_strerror(avahi_client_errno(data.client))));
+        }
+#  endif /* HAVE_DNSSD */
+      }
+      else if (device->ref && device->state == _CUPS_DNSSD_PENDING)
+      {
+        completed ++;
+
+        DEBUG_printf(("1cups_enum_dests: Query for \"%s\" is complete.", device->fullName));
+
+        if ((device->type & mask) == type)
+        {
+          DEBUG_printf(("1cups_enum_dests: Add callback for \"%s\".", device->dest.name));
+          if (!(*cb)(user_data, CUPS_DEST_FLAGS_NONE, &device->dest))
+          {
+            remaining = -1;
+            break;
+          }
+        }
+
+        device->state = _CUPS_DNSSD_ACTIVE;
+      }
+    }
+
+#  ifdef HAVE_AVAHI
+    DEBUG_printf(("1cups_enum_dests: remaining=%d, browsers=%d, completed=%d, count=%d, devices count=%d", remaining, data.browsers, completed, count, cupsArrayCount(data.devices)));
+
+    if (data.browsers == 0 && completed == cupsArrayCount(data.devices))
+      break;
+#  else
+    DEBUG_printf(("1cups_enum_dests: remaining=%d, completed=%d, count=%d, devices count=%d", remaining, completed, count, cupsArrayCount(data.devices)));
+
+    if (completed == cupsArrayCount(data.devices))
+      break;
+#  endif /* HAVE_AVAHI */
+  }
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+ /*
+  * Return...
+  */
+
+  enum_finished:
+
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
+  cupsArrayDelete(data.devices);
+
+#  ifdef HAVE_DNSSD
+  if (ipp_ref)
+    DNSServiceRefDeallocate(ipp_ref);
+  if (local_ipp_ref)
+    DNSServiceRefDeallocate(local_ipp_ref);
+
+#    ifdef HAVE_SSL
+  if (ipps_ref)
+    DNSServiceRefDeallocate(ipps_ref);
+  if (local_ipps_ref)
+    DNSServiceRefDeallocate(local_ipps_ref);
+#    endif /* HAVE_SSL */
+
+  if (data.main_ref)
+    DNSServiceRefDeallocate(data.main_ref);
+
+#  else /* HAVE_AVAHI */
+  if (ipp_ref)
+    avahi_service_browser_free(ipp_ref);
+#    ifdef HAVE_SSL
+  if (ipps_ref)
+    avahi_service_browser_free(ipps_ref);
+#    endif /* HAVE_SSL */
+
+  if (data.client)
+    avahi_client_free(data.client);
+  if (data.simple_poll)
+    avahi_simple_poll_free(data.simple_poll);
+#  endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
+
+  DEBUG_puts("1cups_enum_dests: Returning 1.");
+
+  return (1);
+}
+
+
 /*
  * 'cups_find_dest()' - Find a destination using a binary search.
  */
@@ -3724,6 +4079,36 @@
 
 
 /*
+ * 'cups_get_cb()' - Collect enumerated destinations.
+ */
+
+static int                              /* O - 1 to continue, 0 to stop */
+cups_get_cb(_cups_getdata_t *data,      /* I - Data from cupsGetDests */
+            unsigned        flags,      /* I - Enumeration flags */
+            cups_dest_t     *dest)      /* I - Destination */
+{
+  if (flags & CUPS_DEST_FLAGS_REMOVED)
+  {
+   /*
+    * Remove destination from array...
+    */
+
+    data->num_dests = cupsRemoveDest(dest->name, dest->instance, data->num_dests, &data->dests);
+  }
+  else
+  {
+   /*
+    * Add destination to array...
+    */
+
+    data->num_dests = cupsCopyDest(dest, data->num_dests, &data->dests);
+  }
+
+  return (1);
+}
+
+
+/*
  * 'cups_get_default()' - Get the default destination from an lpoptions file.
  */
 
@@ -4027,3 +4412,58 @@
 
   return (buffer);
 }
+
+
+/*
+ * 'cups_name_cb()' - Find an enumerated destination.
+ */
+
+static int                              /* O - 1 to continue, 0 to stop */
+cups_name_cb(_cups_namedata_t *data,    /* I - Data from cupsGetNamedDest */
+             unsigned         flags,    /* I - Enumeration flags */
+             cups_dest_t      *dest)    /* I - Destination */
+{
+  DEBUG_printf(("2cups_name_cb(data=%p(%s), flags=%x, dest=%p(%s)", (void *)data, data->name, flags, (void *)dest, dest->name));
+
+  if (!(flags & CUPS_DEST_FLAGS_REMOVED) && !dest->instance && !strcasecmp(data->name, dest->name))
+  {
+   /*
+    * Copy destination and stop enumeration...
+    */
+
+    cupsCopyDest(dest, 0, &data->dest);
+    return (0);
+  }
+
+  return (1);
+}
+
+
+/*
+ * 'cups_queue_name()' - Create a local queue name based on the service name.
+ */
+
+static void
+cups_queue_name(
+    char       *name,			/* I - Name buffer */
+    const char *serviceName,		/* I - Service name */
+    size_t     namesize)		/* I - Size of name buffer */
+{
+  const char	*ptr;			/* Pointer into serviceName */
+  char		*nameptr;		/* Pointer into name */
+
+
+  for (nameptr = name, ptr = serviceName; *ptr && nameptr < (name + namesize - 1); ptr ++)
+  {
+   /*
+    * Sanitize the printer name...
+    */
+
+    if (_cups_isalnum(*ptr))
+      *nameptr++ = *ptr;
+    else if (nameptr == name || nameptr[-1] != '_')
+      *nameptr++ = '_';
+  }
+
+  *nameptr = '\0';
+}
diff --git a/cups/dir.c b/cups/dir.c
index 65b8c4f..6b37778 100644
--- a/cups/dir.c
+++ b/cups/dir.c
@@ -3,7 +3,7 @@
  *
  * This set of APIs abstracts enumeration of directory entries.
  *
- * Copyright 2007-2012 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2005 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -26,7 +26,7 @@
  * Windows implementation...
  */
 
-#ifdef WIN32
+#ifdef _WIN32
 #  include <windows.h>
 
 /*
@@ -145,7 +145,7 @@
 cups_dentry_t *				/* O - Directory entry or @code NULL@ if there are no more */
 cupsDirRead(cups_dir_t *dp)		/* I - Directory pointer */
 {
-  WIN32_FIND_DATA	entry;		/* Directory entry data */
+  WIN32_FIND_DATAA	entry;		/* Directory entry data */
 
 
  /*
@@ -165,11 +165,11 @@
     * No, find the first file...
     */
 
-    dp->dir = FindFirstFile(dp->directory, &entry);
+    dp->dir = FindFirstFileA(dp->directory, &entry);
     if (dp->dir == INVALID_HANDLE_VALUE)
       return (NULL);
   }
-  else if (!FindNextFile(dp->dir, &entry))
+  else if (!FindNextFileA(dp->dir, &entry))
     return (NULL);
 
  /*
@@ -338,10 +338,6 @@
 {
   struct dirent	*entry;			/* Pointer to entry */
   char		filename[1024];		/* Full filename */
-#  ifdef HAVE_PTHREAD_H
-  char		buffer[sizeof(struct dirent) + 1024];
-					/* Directory entry buffer */
-#  endif /* HAVE_PTHREAD_H */
 
 
   DEBUG_printf(("2cupsDirRead(dp=%p)", (void *)dp));
@@ -359,29 +355,8 @@
 
   for (;;)
   {
-#  ifdef HAVE_PTHREAD_H
    /*
-    * Read the next entry using the reentrant version of readdir...
-    */
-
-    if (readdir_r(dp->dir, (struct dirent *)buffer, &entry))
-    {
-      DEBUG_printf(("3cupsDirRead: readdir_r() failed - %s\n", strerror(errno)));
-      return (NULL);
-    }
-
-    if (!entry)
-    {
-      DEBUG_puts("3cupsDirRead: readdir_r() returned a NULL pointer!");
-      return (NULL);
-    }
-
-    DEBUG_printf(("4cupsDirRead: readdir_r() returned \"%s\"...",
-                  entry->d_name));
-
-#  else
-   /*
-    * Read the next entry using the original version of readdir...
+    * Read the next entry...
     */
 
     if ((entry = readdir(dp->dir)) == NULL)
@@ -392,8 +367,6 @@
 
     DEBUG_printf(("4cupsDirRead: readdir() returned \"%s\"...", entry->d_name));
 
-#  endif /* HAVE_PTHREAD_H */
-
    /*
     * Skip "." and ".."...
     */
@@ -449,4 +422,4 @@
 
   rewinddir(dp->dir);
 }
-#endif /* WIN32 */
+#endif /* _WIN32 */
diff --git a/cups/encode.c b/cups/encode.c
index e60aec0..94695d0 100644
--- a/cups/encode.c
+++ b/cups/encode.c
@@ -1,7 +1,7 @@
 /*
  * Option encoding routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -329,7 +329,7 @@
  * 'cupsEncodeOptions()' - Encode printer options into IPP attributes.
  *
  * This function adds operation, job, and then subscription attributes,
- * in that order. Use the cupsEncodeOptions2() function to add attributes
+ * in that order. Use the @link cupsEncodeOptions2@ function to add attributes
  * for a single group.
  */
 
@@ -354,7 +354,7 @@
  * 'cupsEncodeOptions2()' - Encode printer options into IPP attributes for a group.
  *
  * This function only adds attributes for a single group. Call this
- * function multiple times for each group, or use cupsEncodeOptions()
+ * function multiple times for each group, or use @link cupsEncodeOptions@
  * to add the standard groups.
  *
  * @since CUPS 1.2/macOS 10.5@
diff --git a/cups/file-private.h b/cups/file-private.h
index b8ca431..6789283 100644
--- a/cups/file-private.h
+++ b/cups/file-private.h
@@ -4,9 +4,9 @@
  * Since stdio files max out at 256 files on many systems, we have to
  * write similar functions without this limit.  At the same time, using
  * our own file functions allows us to provide transparent support of
- * gzip'd print files, PPD files, etc.
+ * different line endings, gzip'd print files, PPD files, etc.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -31,13 +31,10 @@
 #  include <stdarg.h>
 #  include <fcntl.h>
 
-#  ifdef HAVE_LIBZ
-#    include <zlib.h>
-#  endif /* HAVE_LIBZ */
-#  ifdef WIN32
+#  ifdef _WIN32
 #    include <io.h>
 #    include <sys/locking.h>
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
 
 /*
@@ -88,30 +85,6 @@
 typedef void (*_cups_fc_func_t)(void *context, _cups_fc_result_t result,
 				const char *message);
 
-struct _cups_file_s			/**** CUPS file structure... ****/
-
-{
-  int		fd;			/* File descriptor */
-  char		mode,			/* Mode ('r' or 'w') */
-		compressed,		/* Compression used? */
-		is_stdio,		/* stdin/out/err? */
-		eof,			/* End of file? */
-		buf[4096],		/* Buffer */
-		*ptr,			/* Pointer into buffer */
-		*end;			/* End of buffer data */
-  off_t		pos,			/* Position in file */
-		bufpos;			/* File position for start of buffer */
-
-#ifdef HAVE_LIBZ
-  z_stream	stream;			/* (De)compression stream */
-  Bytef		cbuf[4096];		/* (De)compression buffer */
-  uLong		crc;			/* (De)compression CRC */
-#endif /* HAVE_LIBZ */
-
-  char		*printf_buffer;		/* cupsFilePrintf buffer */
-  size_t	printf_size;		/* Size of cupsFilePrintf buffer */
-};
-
 
 /*
  * Prototypes...
@@ -125,6 +98,7 @@
 extern void			_cupsFileCheckFilter(void *context,
 						     _cups_fc_result_t result,
 						     const char *message);
+extern int			_cupsFilePeekAhead(cups_file_t *fp, int ch);
 
 #  ifdef __cplusplus
 }
diff --git a/cups/file.c b/cups/file.c
index a027df4..5c9ddf8 100644
--- a/cups/file.c
+++ b/cups/file.c
@@ -4,9 +4,9 @@
  * Since stdio files max out at 256 files on many systems, we have to
  * write similar functions without this limit.  At the same time, using
  * our own file functions allows us to provide transparent support of
- * gzip'd print files, PPD files, etc.
+ * different line endings, gzip'd print files, PPD files, etc.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -26,6 +26,39 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#  ifdef HAVE_LIBZ
+#    include <zlib.h>
+#  endif /* HAVE_LIBZ */
+
+
+/*
+ * Internal structures...
+ */
+
+struct _cups_file_s			/**** CUPS file structure... ****/
+
+{
+  int		fd;			/* File descriptor */
+  char		mode,			/* Mode ('r' or 'w') */
+		compressed,		/* Compression used? */
+		is_stdio,		/* stdin/out/err? */
+		eof,			/* End of file? */
+		buf[4096],		/* Buffer */
+		*ptr,			/* Pointer into buffer */
+		*end;			/* End of buffer data */
+  off_t		pos,			/* Position in file */
+		bufpos;			/* File position for start of buffer */
+
+#ifdef HAVE_LIBZ
+  z_stream	stream;			/* (De)compression stream */
+  Bytef		cbuf[4096];		/* (De)compression buffer */
+  uLong		crc;			/* (De)compression CRC */
+#endif /* HAVE_LIBZ */
+
+  char		*printf_buffer;		/* cupsFilePrintf buffer */
+  size_t	printf_size;		/* Size of cupsFilePrintf buffer */
+};
+
 
 /*
  * Local functions...
@@ -40,7 +73,7 @@
 static ssize_t	cups_write(cups_file_t *fp, const char *buf, size_t bytes);
 
 
-#ifndef WIN32
+#ifndef _WIN32
 /*
  * '_cupsFileCheck()' - Check the permissions of the given filename.
  */
@@ -306,7 +339,7 @@
 
   fprintf(stderr, "%s: %s\n", prefix, message);
 }
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 
 /*
@@ -321,7 +354,6 @@
   int	fd;				/* File descriptor */
   char	mode;				/* Open mode */
   int	status;				/* Return status */
-  int	is_stdio;			/* Is a stdio file? */
 
 
   DEBUG_printf(("cupsFileClose(fp=%p)", (void *)fp));
@@ -410,12 +442,19 @@
 #endif /* HAVE_LIBZ */
 
  /*
+  * If this is one of the cupsFileStdin/out/err files, return now and don't
+  * actually free memory or close (these last the life of the process...)
+  */
+
+  if (fp->is_stdio)
+    return (status);
+
+/*
   * Save the file descriptor we used and free memory...
   */
 
-  fd       = fp->fd;
-  mode     = fp->mode;
-  is_stdio = fp->is_stdio;
+  fd   = fp->fd;
+  mode = fp->mode;
 
   if (fp->printf_buffer)
     free(fp->printf_buffer);
@@ -431,11 +470,8 @@
     if (httpAddrClose(NULL, fd) < 0)
       status = -1;
   }
-  else if (!is_stdio)
-  {
-    if (close(fd) < 0)
-      status = -1;
-  }
+  else if (close(fd) < 0)
+    status = -1;
 
   return (status);
 }
@@ -523,22 +559,22 @@
 
   while (*path)
   {
-#ifdef WIN32
+#ifdef _WIN32
     if (*path == ';' || (*path == ':' && ((bufptr - buffer) > 1 || !isalpha(buffer[0] & 255))))
 #else
     if (*path == ';' || *path == ':')
-#endif /* WIN32 */
+#endif /* _WIN32 */
     {
       if (bufptr > buffer && bufptr[-1] != '/' && bufptr < bufend)
         *bufptr++ = '/';
 
       strlcpy(bufptr, filename, (size_t)(bufend - bufptr));
 
-#ifdef WIN32
+#ifdef _WIN32
       if (!access(buffer, 0))
 #else
       if (!access(buffer, executable ? X_OK : 0))
-#endif /* WIN32 */
+#endif /* _WIN32 */
       {
         DEBUG_printf(("1cupsFileFind: Returning \"%s\"", buffer));
         return (buffer);
@@ -989,11 +1025,11 @@
   * Try the lock...
   */
 
-#ifdef WIN32
+#ifdef _WIN32
   return (_locking(fp->fd, block ? _LK_LOCK : _LK_NBLCK, 0));
 #else
   return (lockf(fp->fd, block ? F_LOCK : F_TLOCK, 0));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -1081,11 +1117,11 @@
 	}
 
 	if (fd >= 0)
-#ifdef WIN32
+#ifdef _WIN32
 	  _chsize(fd, 0);
 #else
 	  ftruncate(fd, 0);
-#endif /* WIN32 */
+#endif /* _WIN32 */
         break;
 
     case 's' : /* Read/write socket */
@@ -1252,15 +1288,27 @@
   * Don't pass this file to child processes...
   */
 
-#ifndef WIN32
+#ifndef _WIN32
   fcntl(fp->fd, F_SETFD, fcntl(fp->fd, F_GETFD) | FD_CLOEXEC);
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
   return (fp);
 }
 
 
 /*
+ * '_cupsFilePeekAhead()' - See if the requested character is buffered up.
+ */
+
+int					/* O - 1 if present, 0 otherwise */
+_cupsFilePeekAhead(cups_file_t *fp,	/* I - CUPS file */
+                   int         ch)	/* I - Character */
+{
+  return (fp && fp->ptr && memchr(fp->ptr, ch, (size_t)(fp->end - fp->ptr)));
+}
+
+
+/*
  * 'cupsFilePeekChar()' - Peek at the next character from a file.
  *
  * @since CUPS 1.2/macOS 10.5@
@@ -1384,7 +1432,11 @@
   {
     memcpy(fp->ptr, fp->printf_buffer, (size_t)bytes);
     fp->ptr += bytes;
-    return ((int)bytes);
+
+    if (fp->is_stdio && cupsFileFlush(fp))
+      return (-1);
+    else
+      return ((int)bytes);
   }
 }
 
@@ -1563,7 +1615,11 @@
   {
     memcpy(fp->ptr, s, (size_t)bytes);
     fp->ptr += bytes;
-    return ((int)bytes);
+
+    if (fp->is_stdio && cupsFileFlush(fp))
+      return (-1);
+    else
+      return ((int)bytes);
   }
 }
 
@@ -2008,11 +2064,11 @@
   * Unlock...
   */
 
-#ifdef WIN32
+#ifdef _WIN32
   return (_locking(fp->fd, _LK_UNLCK, 0));
 #else
   return (lockf(fp->fd, F_ULOCK, 0));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -2466,6 +2522,8 @@
 	* file header...
 	*/
 
+        inflateEnd(&fp->stream);
+
 	fp->compressed = 0;
       }
       else if (status < Z_OK)
@@ -2540,9 +2598,9 @@
 {
   int		fd;			/* File descriptor */
   struct stat	fileinfo;		/* File information */
-#ifndef WIN32
+#ifndef _WIN32
   struct stat	linkinfo;		/* Link information */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 
  /*
@@ -2570,18 +2628,18 @@
     return (-1);
   }
 
-#ifdef WIN32
+#ifdef _WIN32
   if (fileinfo.st_mode & _S_IFDIR)
 #else
   if (S_ISDIR(fileinfo.st_mode))
-#endif /* WIN32 */
+#endif /* _WIN32 */
   {
     close(fd);
     errno = EISDIR;
     return (-1);
   }
 
-#ifndef WIN32
+#ifndef _WIN32
  /*
   * Then use lstat to determine whether the filename is a symlink...
   */
@@ -2609,7 +2667,7 @@
     errno = EPERM;
     return (-1);
   }
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
   return (fd);
 }
@@ -2635,7 +2693,7 @@
 
   for (;;)
   {
-#ifdef WIN32
+#ifdef _WIN32
     if (fp->mode == 's')
       total = (ssize_t)recv(fp->fd, buf, (unsigned)bytes, 0);
     else
@@ -2645,7 +2703,7 @@
       total = recv(fp->fd, buf, bytes, 0);
     else
       total = read(fp->fd, buf, bytes);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
     DEBUG_printf(("9cups_read: total=" CUPS_LLFMT, CUPS_LLCAST total));
 
@@ -2692,7 +2750,7 @@
   total = 0;
   while (bytes > 0)
   {
-#ifdef WIN32
+#ifdef _WIN32
     if (fp->mode == 's')
       count = (ssize_t)send(fp->fd, buf, (unsigned)bytes, 0);
     else
@@ -2702,7 +2760,7 @@
       count = send(fp->fd, buf, bytes, 0);
     else
       count = write(fp->fd, buf, bytes);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
     DEBUG_printf(("9cups_write: count=" CUPS_LLFMT, CUPS_LLCAST count));
 
diff --git a/cups/file.h b/cups/file.h
index 177c2e9..0d3a2e5 100644
--- a/cups/file.h
+++ b/cups/file.h
@@ -4,9 +4,9 @@
  * Since stdio files max out at 256 files on many systems, we have to
  * write similar functions without this limit.  At the same time, using
  * our own file functions allows us to provide transparent support of
- * gzip'd print files, PPD files, etc.
+ * different line endings, gzip'd print files, PPD files, etc.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -29,11 +29,11 @@
 #  include "versioning.h"
 #  include <stddef.h>
 #  include <sys/types.h>
-#  if defined(WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
+#  if defined(_WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
 #    define __CUPS_SSIZE_T_DEFINED
 /* Windows does not support the ssize_t type, so map it to off_t... */
 typedef off_t ssize_t;			/* @private@ */
-#  endif /* WIN32 && !__CUPS_SSIZE_T_DEFINED */
+#  endif /* _WIN32 && !__CUPS_SSIZE_T_DEFINED */
 
 
 /*
@@ -85,9 +85,7 @@
 			_CUPS_API_1_2;
 extern cups_file_t	*cupsFileOpenFd(int fd, const char *mode) _CUPS_API_1_2;
 extern int		cupsFilePeekChar(cups_file_t *fp) _CUPS_API_1_2;
-extern int		cupsFilePrintf(cups_file_t *fp, const char *format, ...)
-			__attribute__((__format__ (__printf__, 2, 3)))
-			_CUPS_API_1_2;
+extern int		cupsFilePrintf(cups_file_t *fp, const char *format, ...) _CUPS_FORMAT(2, 3) _CUPS_API_1_2;
 extern int		cupsFilePutChar(cups_file_t *fp, int c) _CUPS_API_1_2;
 extern ssize_t		cupsFilePutConf(cups_file_t *fp, const char *directive,
 			                const char *value) _CUPS_API_1_4;
diff --git a/cups/getputfile.c b/cups/getputfile.c
index ae33bc5..7749ae0 100644
--- a/cups/getputfile.c
+++ b/cups/getputfile.c
@@ -1,7 +1,7 @@
 /*
  * Get/put file functions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -20,11 +20,11 @@
 #include "cups-private.h"
 #include <fcntl.h>
 #include <sys/stat.h>
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 
 
 /*
@@ -45,6 +45,8 @@
   http_status_t	status;			/* HTTP status from server */
   char		if_modified_since[HTTP_MAX_VALUE];
 					/* If-Modified-Since header */
+  int		new_auth = 0;		/* Using new auth information? */
+  int		digest;			/* Are we using Digest authentication? */
 
 
  /*
@@ -85,9 +87,33 @@
     }
 
     httpClearFields(http);
-    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
     httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE, if_modified_since);
 
+    digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
+
+    if (digest && !new_auth)
+    {
+     /*
+      * Update the Digest authentication string...
+      */
+
+      _httpSetDigestAuthString(http, http->nextnonce, "GET", resource);
+    }
+
+#ifdef HAVE_GSSAPI
+    if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth)
+    {
+     /*
+      * Do not use cached Kerberos credentials since they will look like a
+      * "replay" attack...
+      */
+
+      _cupsSetNegotiateAuthString(http, "GET", resource);
+    }
+#endif /* HAVE_GSSAPI */
+
+    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
+
     if (httpGet(http, resource))
     {
       if (httpReconnect2(http, 30000, NULL))
@@ -102,6 +128,8 @@
       }
     }
 
+    new_auth = 0;
+
     while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
 
     if (status == HTTP_STATUS_UNAUTHORIZED)
@@ -116,6 +144,8 @@
       * See if we can do authentication...
       */
 
+      new_auth = 1;
+
       if (cupsDoAuthentication(http, "GET", resource))
       {
         status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
@@ -267,6 +297,8 @@
   int		retries;		/* Number of retries */
   char		buffer[8192];		/* Buffer for file */
   http_status_t	status;			/* HTTP status from server */
+  int		new_auth = 0;		/* Using new auth information? */
+  int		digest;			/* Are we using Digest authentication? */
 
 
  /*
@@ -309,10 +341,34 @@
                   http->authstring));
 
     httpClearFields(http);
-    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
     httpSetField(http, HTTP_FIELD_TRANSFER_ENCODING, "chunked");
     httpSetExpect(http, HTTP_STATUS_CONTINUE);
 
+    digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
+
+    if (digest && !new_auth)
+    {
+     /*
+      * Update the Digest authentication string...
+      */
+
+      _httpSetDigestAuthString(http, http->nextnonce, "PUT", resource);
+    }
+
+#ifdef HAVE_GSSAPI
+    if (http->authstring && !strncmp(http->authstring, "Negotiate", 9) && !new_auth)
+    {
+     /*
+      * Do not use cached Kerberos credentials since they will look like a
+      * "replay" attack...
+      */
+
+      _cupsSetNegotiateAuthString(http, "PUT", resource);
+    }
+#endif /* HAVE_GSSAPI */
+
+    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
+
     if (httpPut(http, resource))
     {
       if (httpReconnect2(http, 30000, NULL))
@@ -383,6 +439,8 @@
 
     DEBUG_printf(("2cupsPutFd: status=%d", status));
 
+    new_auth = 0;
+
     if (status == HTTP_STATUS_UNAUTHORIZED)
     {
      /*
@@ -395,6 +453,8 @@
       * See if we can do authentication...
       */
 
+      new_auth = 1;
+
       if (cupsDoAuthentication(http, "PUT", resource))
       {
         status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
diff --git a/cups/globals.c b/cups/globals.c
index 8a05c3e..28c993b 100644
--- a/cups/globals.c
+++ b/cups/globals.c
@@ -34,23 +34,23 @@
 static pthread_once_t	cups_globals_key_once = PTHREAD_ONCE_INIT;
 					/* One-time initialization object */
 #endif /* HAVE_PTHREAD_H */
-#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
 static _cups_mutex_t	cups_global_mutex = _CUPS_MUTEX_INITIALIZER;
 					/* Global critical section */
-#endif /* HAVE_PTHREAD_H || WIN32 */
+#endif /* HAVE_PTHREAD_H || _WIN32 */
 
 
 /*
  * Local functions...
  */
 
-#ifdef WIN32
+#ifdef _WIN32
 static void		cups_fix_path(char *path);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 static _cups_globals_t	*cups_globals_alloc(void);
-#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
 static void		cups_globals_free(_cups_globals_t *g);
-#endif /* HAVE_PTHREAD_H || WIN32 */
+#endif /* HAVE_PTHREAD_H || _WIN32 */
 #ifdef HAVE_PTHREAD_H
 static void		cups_globals_init(void);
 #endif /* HAVE_PTHREAD_H */
@@ -65,7 +65,7 @@
 {
 #ifdef HAVE_PTHREAD_H
   pthread_mutex_lock(&cups_global_mutex);
-#elif defined(WIN32)
+#elif defined(_WIN32)
   EnterCriticalSection(&cups_global_mutex.m_criticalSection);
 #endif /* HAVE_PTHREAD_H */
 }
@@ -120,13 +120,13 @@
 {
 #ifdef HAVE_PTHREAD_H
   pthread_mutex_unlock(&cups_global_mutex);
-#elif defined(WIN32)
+#elif defined(_WIN32)
   LeaveCriticalSection(&cups_global_mutex.m_criticalSection);
 #endif /* HAVE_PTHREAD_H */
 }
 
 
-#ifdef WIN32
+#ifdef _WIN32
 /*
  * 'DllMain()' - Main entry for library.
  */
@@ -170,7 +170,7 @@
 
   return (TRUE);
 }
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -182,13 +182,13 @@
 {
   _cups_globals_t *cg = malloc(sizeof(_cups_globals_t));
 					/* Pointer to global data */
-#ifdef WIN32
+#ifdef _WIN32
   HKEY		key;			/* Registry key */
   DWORD		size;			/* Size of string */
   static char	installdir[1024] = "",	/* Install directory */
 		confdir[1024] = "",	/* Server root directory */
 		localedir[1024] = "";	/* Locale directory */
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
   if (!cg)
@@ -219,7 +219,7 @@
   * Then set directories as appropriate...
   */
 
-#ifdef WIN32
+#ifdef _WIN32
   if (!installdir[0])
   {
    /*
@@ -315,7 +315,7 @@
     if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
       cg->localedir = CUPS_LOCALEDIR;
   }
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
   return (cg);
 }
@@ -325,7 +325,7 @@
  * 'cups_globals_free()' - Free global data.
  */
 
-#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
 static void
 cups_globals_free(_cups_globals_t *cg)	/* I - Pointer to global data */
 {
@@ -360,7 +360,7 @@
 
   free(cg);
 }
-#endif /* HAVE_PTHREAD_H || WIN32 */
+#endif /* HAVE_PTHREAD_H || _WIN32 */
 
 
 #ifdef HAVE_PTHREAD_H
diff --git a/cups/hash.c b/cups/hash.c
index ede5461..50dc5fb 100644
--- a/cups/hash.c
+++ b/cups/hash.c
@@ -1,7 +1,7 @@
 /*
  * Hashing function for CUPS.
  *
- * Copyright 2015-2016 by Apple Inc.
+ * Copyright © 2015-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -21,6 +21,8 @@
 #  include <CommonCrypto/CommonDigest.h>
 #elif defined(HAVE_GNUTLS)
 #  include <gnutls/crypto.h>
+#else
+#  include "md5-private.h"
 #endif /* __APPLE__ */
 
 
@@ -53,7 +55,24 @@
   }
 
 #ifdef __APPLE__
-  if (!strcmp(algorithm, "sha"))
+  if (!strcmp(algorithm, "md5"))
+  {
+   /*
+    * MD5 (deprecated but widely used...)
+    */
+
+    CC_MD5_CTX	ctx;			/* MD5 context */
+
+    if (hashsize < CC_MD5_DIGEST_LENGTH)
+      goto too_small;
+
+    CC_MD5_Init(&ctx);
+    CC_MD5_Update(&ctx, data, (CC_LONG)datalen);
+    CC_MD5_Final(hash, &ctx);
+
+    return (CC_MD5_DIGEST_LENGTH);
+  }
+  else if (!strcmp(algorithm, "sha"))
   {
    /*
     * SHA-1...
@@ -171,7 +190,9 @@
   unsigned char	temp[64];		/* Temporary hash buffer */
   size_t	tempsize = 0;		/* Truncate to this size? */
 
-  if (!strcmp(algorithm, "sha"))
+  if (!strcmp(algorithm, "md5"))
+    alg = GNUTLS_DIG_MD5;
+  else if (!strcmp(algorithm, "sha"))
     alg = GNUTLS_DIG_SHA1;
   else if (!strcmp(algorithm, "sha2-224"))
     alg = GNUTLS_DIG_SHA224;
@@ -219,10 +240,20 @@
 
 #else
  /*
-  * No hash support without CommonCrypto or GNU TLS...
+  * No hash support beyond MD5 without CommonCrypto or GNU TLS...
   */
 
-  if (hashsize < 64)
+  if (!strcmp(algorithm, "md5"))
+  {
+    _cups_md5_state_t	state;		/* MD5 state info */
+
+    _cupsMD5Init(&state);
+    _cupsMD5Append(&state, data, datalen);
+    _cupsMD5Finish(&state, hash);
+
+    return (16);
+  }
+  else if (hashsize < 64)
     goto too_small;
 #endif /* __APPLE__ */
 
@@ -243,3 +274,53 @@
   _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Hash buffer too small."), 1);
   return (-1);
 }
+
+
+/*
+ * 'cupsHashString()' - Format a hash value as a hexadecimal string.
+ *
+ * The passed buffer must be at least 2 * hashsize + 1 characters in length.
+ *
+ * @since CUPS 2.2.7@
+ */
+
+const char *				/* O - Formatted string */
+cupsHashString(
+    const unsigned char *hash,		/* I - Hash */
+    size_t              hashsize,	/* I - Size of hash */
+    char                *buffer,	/* I - String buffer */
+    size_t		bufsize)	/* I - Size of string buffer */
+{
+  char		*bufptr = buffer;	/* Pointer into buffer */
+  static const char *hex = "0123456789abcdef";
+					/* Hex characters (lowercase!) */
+
+
+ /*
+  * Range check input...
+  */
+
+  if (!hash || hashsize < 1 || !buffer || bufsize < (2 * hashsize + 1))
+  {
+    if (buffer)
+      *buffer = '\0';
+    return (NULL);
+  }
+
+ /*
+  * Loop until we've converted the whole hash...
+  */
+
+  while (hashsize > 0)
+  {
+    *bufptr++ = hex[*hash >> 4];
+    *bufptr++ = hex[*hash & 15];
+
+    hash ++;
+    hashsize --;
+  }
+
+  *bufptr = '\0';
+
+  return (buffer);
+}
diff --git a/cups/http-addr.c b/cups/http-addr.c
index 12d13a6..f8b8131 100644
--- a/cups/http-addr.c
+++ b/cups/http-addr.c
@@ -58,9 +58,9 @@
  * 'httpAddrClose()' - Close a socket created by @link httpAddrConnect@ or
  *                     @link httpAddrListen@.
  *
- * Pass @code NULL@ for sockets created with @link httpAddrConnect@ and the
- * listen address for sockets created with @link httpAddrListen@. This will
- * ensure that domain sockets are removed when closed.
+ * Pass @code NULL@ for sockets created with @link httpAddrConnect2@ and the
+ * listen address for sockets created with @link httpAddrListen@.  This function
+ * ensures that domain sockets are removed when closed.
  *
  * @since CUPS 2.0/OS 10.10@
  */
@@ -69,11 +69,11 @@
 httpAddrClose(http_addr_t *addr,		/* I - Listen address or @code NULL@ */
               int         fd)			/* I - Socket file descriptor */
 {
-#ifdef WIN32
+#ifdef _WIN32
   if (closesocket(fd))
 #else
   if (close(fd))
-#endif /* WIN32 */
+#endif /* _WIN32 */
     return (-1);
 
 #ifdef AF_LOCAL
@@ -258,9 +258,9 @@
   * Close on exec...
   */
 
-#ifndef WIN32
+#ifndef _WIN32
   fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 #ifdef SO_NOSIGPIPE
  /*
@@ -648,6 +648,10 @@
 /*
  * 'httpGetAddress()' - Get the address of the connected peer of a connection.
  *
+ * For connections created with @link httpConnect2@, the address is for the
+ * server.  For connections created with @link httpAccept@, the address is for
+ * the client.
+ *
  * Returns @code NULL@ if the socket is currently unconnected.
  *
  * @since CUPS 2.0/OS 10.10@
@@ -667,7 +671,7 @@
  * 'httpGetHostByName()' - Lookup a hostname or IPv4 address, and return
  *                         address records for the specified name.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 struct hostent *			/* O - Host entry */
diff --git a/cups/http-addrlist.c b/cups/http-addrlist.c
index 723bf02..d9a96c4 100644
--- a/cups/http-addrlist.c
+++ b/cups/http-addrlist.c
@@ -1,7 +1,7 @@
 /*
  * HTTP address list routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -24,15 +24,15 @@
 #ifdef HAVE_POLL
 #  include <poll.h>
 #endif /* HAVE_POLL */
-#ifndef WIN32
+#ifndef _WIN32
 #  include <fcntl.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
  * 'httpAddrConnect()' - Connect to any of the addresses in the list.
  *
- * @since CUPS 1.2/macOS 10.5@
+ * @since CUPS 1.2/macOS 10.5@ @exclude all@
  */
 
 http_addrlist_t *			/* O - Connected address or NULL on failure */
@@ -61,14 +61,14 @@
     int             *cancel)		/* I - Pointer to "cancel" variable */
 {
   int			val;		/* Socket option value */
-#ifndef WIN32
-  int			flags;		/* Socket flags */
-#endif /* !WIN32 */
-  int			remaining;	/* Remaining timeout */
-  int			i,		/* Looping var */
-			nfds,		/* Number of file descriptors */
-			fds[100],	/* Socket file descriptors */
+#ifndef _WIN32
+  int			i, j,		/* Looping vars */
+			flags,		/* Socket flags */
 			result;		/* Result from select() or poll() */
+#endif /* !_WIN32 */
+  int			remaining;	/* Remaining timeout */
+  int			nfds,		/* Number of file descriptors */
+			fds[100];	/* Socket file descriptors */
   http_addrlist_t	*addrs[100];	/* Addresses */
 #ifndef HAVE_POLL
   int			max_fd = -1;	/* Highest file descriptor */
@@ -84,8 +84,10 @@
 #  endif /* HAVE_POLL */
 #endif /* O_NONBLOCK */
 #ifdef DEBUG
+#  ifndef _WIN32
   socklen_t		len;		/* Length of value */
   http_addr_t		peer;		/* Peer address */
+#  endif /* !_WIN32 */
   char			temp[256];	/* Temporary address string */
 #endif /* DEBUG */
 
@@ -213,11 +215,11 @@
 	return (addrlist);
       }
 
-#ifdef WIN32
+#ifdef _WIN32
       if (WSAGetLastError() != WSAEINPROGRESS && WSAGetLastError() != WSAEWOULDBLOCK)
 #else
       if (errno != EINPROGRESS && errno != EWOULDBLOCK)
-#endif /* WIN32 */
+#endif /* _WIN32 */
       {
 	DEBUG_printf(("1httpAddrConnect2: Unable to connect to %s:%d: %s", httpAddrString(&(addrlist->addr), temp, sizeof(temp)), httpAddrPort(&(addrlist->addr)), strerror(errno)));
 	httpAddrClose(NULL, fds[nfds]);
@@ -225,9 +227,9 @@
 	continue;
       }
 
-#ifndef WIN32
+#ifndef _WIN32
       fcntl(fds[nfds], F_SETFL, flags);
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 #ifndef HAVE_POLL
       if (fds[nfds] > max_fd)
@@ -296,11 +298,11 @@
       DEBUG_printf(("1httpAddrConnect2: select() returned %d (%d)", result, errno));
 #  endif /* HAVE_POLL */
     }
-#  ifdef WIN32
+#  ifdef _WIN32
     while (result < 0 && (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK));
 #  else
     while (result < 0 && (errno == EINTR || errno == EAGAIN));
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
     if (result > 0)
     {
@@ -323,6 +325,8 @@
 	  if (!getpeername(fds[i], (struct sockaddr *)&peer, &len))
 	    DEBUG_printf(("1httpAddrConnect2: Connected to %s:%d...", httpAddrString(&peer, temp, sizeof(temp)), httpAddrPort(&peer)));
 #  endif /* DEBUG */
+
+          break;
 	}
 #  ifdef HAVE_POLL
 	else if (pfds[i].revents & (POLLERR | POLLHUP))
@@ -346,7 +350,20 @@
       }
 
       if (connaddr)
+      {
+       /*
+        * Connected on one address, close all of the other sockets we have so
+        * far and return...
+        */
+
+        for (j = 0; j < i; j ++)
+          httpAddrClose(NULL, fds[j]);
+
+        for (j ++; j < nfds; j ++)
+          httpAddrClose(NULL, fds[j]);
+
         return (connaddr);
+      }
     }
 #endif /* O_NONBLOCK */
 
@@ -362,11 +379,11 @@
     httpAddrClose(NULL, fds[nfds]);
   }
 
-#ifdef WIN32
+#ifdef _WIN32
   _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, "Connection failed", 0);
 #else
   _cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, strerror(errno), 0);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
   return (NULL);
 }
@@ -603,6 +620,7 @@
 	  if (!temp)
 	  {
 	    httpAddrFreeList(first);
+	    freeaddrinfo(results);
 	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
 	    return (NULL);
 	  }
@@ -833,11 +851,11 @@
 
         temp->addr.ipv6.sin6_family            = AF_INET6;
 	temp->addr.ipv6.sin6_port              = htons(portnum);
-#  ifdef WIN32
+#  ifdef _WIN32
 	temp->addr.ipv6.sin6_addr.u.Byte[15]   = 1;
 #  else
 	temp->addr.ipv6.sin6_addr.s6_addr32[3] = htonl(1);
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
         if (!first)
           first = temp;
diff --git a/cups/http-private.h b/cups/http-private.h
index ec908a6..32cb9ea 100644
--- a/cups/http-private.h
+++ b/cups/http-private.h
@@ -1,7 +1,7 @@
 /*
  * Private HTTP definitions for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -30,7 +30,7 @@
 #  endif /* __sun */
 
 #  include <limits.h>
-#  ifdef WIN32
+#  ifdef _WIN32
 #    include <io.h>
 #    include <winsock2.h>
 #    define CUPS_SOCAST (const char *)
@@ -39,7 +39,7 @@
 #    include <fcntl.h>
 #    include <sys/socket.h>
 #    define CUPS_SOCAST
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
 #  ifdef HAVE_GSSAPI
 #    ifdef HAVE_GSS_GSSAPI_H
@@ -68,7 +68,6 @@
 #  endif /* __APPLE__ && !_SOCKLEN_T */
 
 #  include <cups/http.h>
-#  include "md5-private.h"
 #  include "ipp-private.h"
 
 #  ifdef HAVE_GNUTLS
@@ -141,7 +140,7 @@
 #    include <sspi.h>
 #  endif /* HAVE_GNUTLS */
 
-#  ifndef WIN32
+#  ifndef _WIN32
 #    include <net/if.h>
 #    include <resolv.h>
 #    ifdef HAVE_GETIFADDRS
@@ -152,11 +151,7 @@
 #        include <sys/sockio.h>
 #      endif /* HAVE_SYS_SOCKIO_H */
 #    endif /* HAVE_GETIFADDRS */
-#  endif /* !WIN32 */
-
-#  ifdef HAVE_LIBZ
-#    include <zlib.h>
-#  endif /* HAVE_LIBZ */
+#  endif /* !_WIN32 */
 
 
 /*
@@ -172,18 +167,24 @@
  * Constants...
  */
 
+#  define _HTTP_MAX_SBUFFER	65536	/* Size of (de)compression buffer */
+#  define _HTTP_RESOLVE_DEFAULT	0	/* Just resolve with default options */
+#  define _HTTP_RESOLVE_STDERR	1	/* Log resolve progress to stderr */
+#  define _HTTP_RESOLVE_FQDN	2	/* Resolve to a FQDN */
+#  define _HTTP_RESOLVE_FAXOUT	4	/* Resolve FaxOut service? */
 
-#define _HTTP_MAX_SBUFFER	65536	/* Size of (de)compression buffer */
-#define _HTTP_RESOLVE_DEFAULT	0	/* Just resolve with default options */
-#define _HTTP_RESOLVE_STDERR	1	/* Log resolve progress to stderr */
-#define _HTTP_RESOLVE_FQDN	2	/* Resolve to a FQDN */
-#define _HTTP_RESOLVE_FAXOUT	4	/* Resolve FaxOut service? */
+#  define _HTTP_TLS_NONE	0	/* No TLS options */
+#  define _HTTP_TLS_ALLOW_RC4	1	/* Allow RC4 cipher suites */
+#  define _HTTP_TLS_ALLOW_DH	2	/* Allow DH/DHE key negotiation */
+#  define _HTTP_TLS_DENY_CBC	4	/* Deny CBC cipher suites */
+#  define _HTTP_TLS_SET_DEFAULT 128     /* Setting the default TLS options */
 
-#define _HTTP_TLS_NONE		0	/* No TLS options */
-#define _HTTP_TLS_ALLOW_RC4	1	/* Allow RC4 cipher suites */
-#define _HTTP_TLS_ALLOW_SSL3	2	/* Allow SSL 3.0 */
-#define _HTTP_TLS_ALLOW_DH	4	/* Allow DH/DHE key negotiation */
-#define _HTTP_TLS_DENY_TLS10	16	/* Deny TLS 1.0 */
+#  define _HTTP_TLS_SSL3	0	/* Min/max version is SSL/3.0 */
+#  define _HTTP_TLS_1_0		1	/* Min/max version is TLS/1.0 */
+#  define _HTTP_TLS_1_1		2	/* Min/max version is TLS/1.1 */
+#  define _HTTP_TLS_1_2		3	/* Min/max version is TLS/1.2 */
+#  define _HTTP_TLS_1_3		4	/* Min/max version is TLS/1.3 */
+#  define _HTTP_TLS_MAX		5	/* Highest known TLS version */
 
 
 /*
@@ -295,10 +296,10 @@
   char			buffer[HTTP_MAX_BUFFER];
 					/* Buffer for incoming data */
   int			_auth_type;	/* Authentication in use (deprecated) */
-  _cups_md5_state_t	md5_state;	/* MD5 state */
+  unsigned char		_md5_state[88];	/* MD5 state (deprecated) */
   char			nonce[HTTP_MAX_VALUE];
 					/* Nonce value */
-  int			nonce_count;	/* Nonce count */
+  unsigned		nonce_count;	/* Nonce count */
   http_tls_t		tls;		/* TLS state information */
   http_encryption_t	encryption;	/* Encryption requirements */
 
@@ -359,9 +360,20 @@
 					/* Default field values */
 #  ifdef HAVE_LIBZ
   _http_coding_t	coding;		/* _HTTP_CODING_xxx */
-  z_stream		stream;		/* (De)compression stream */
-  Bytef			*sbuffer;	/* (De)compression buffer */
+  void			*stream;	/* (De)compression stream */
+  unsigned char		*sbuffer;	/* (De)compression buffer */
 #  endif /* HAVE_LIBZ */
+
+  /**** New in CUPS 2.2.9 ****/
+  char			*authentication_info,
+					/* Authentication-Info header */
+			algorithm[65],	/* Algorithm from WWW-Authenticate */
+			nextnonce[HTTP_MAX_VALUE],
+					/* Next nonce value from Authentication-Info */
+			opaque[HTTP_MAX_VALUE],
+					/* Opaque value from WWW-Authenticate */
+			realm[HTTP_MAX_VALUE];
+					/* Realm from WWW-Authenticate */
 };
 #  endif /* !_HTTP_NO_PRIVATE */
 
@@ -380,7 +392,7 @@
  * Some OS's don't have getifaddrs() and freeifaddrs()...
  */
 
-#  if !defined(WIN32) && !defined(HAVE_GETIFADDRS)
+#  if !defined(_WIN32) && !defined(HAVE_GETIFADDRS)
 #    ifdef ifa_dstaddr
 #      undef ifa_dstaddr
 #    endif /* ifa_dstaddr */
@@ -415,7 +427,7 @@
 #    define getifaddrs _cups_getifaddrs
 extern void	_cups_freeifaddrs(struct ifaddrs *addrs);
 #    define freeifaddrs _cups_freeifaddrs
-#  endif /* !WIN32 && !HAVE_GETIFADDRS */
+#  endif /* !_WIN32 && !HAVE_GETIFADDRS */
 
 
 /*
@@ -435,12 +447,13 @@
 			                 size_t resolved_size, int options,
 					 int (*cb)(void *context),
 					 void *context);
+extern int		_httpSetDigestAuthString(http_t *http, const char *nonce, const char *method, const char *resource);
 extern const char	*_httpStatus(cups_lang_t *lang, http_status_t status);
 extern void		_httpTLSInitialize(void);
 extern size_t		_httpTLSPending(http_t *http);
 extern int		_httpTLSRead(http_t *http, char *buf, int len);
 extern int		_httpTLSSetCredentials(http_t *http);
-extern void		_httpTLSSetOptions(int options);
+extern void		_httpTLSSetOptions(int options, int min_version, int max_version);
 extern int		_httpTLSStart(http_t *http);
 extern void		_httpTLSStop(http_t *http);
 extern int		_httpTLSWrite(http_t *http, const char *buf, int len);
diff --git a/cups/http-support.c b/cups/http-support.c
index 1ca01b2..767fbf6 100644
--- a/cups/http-support.c
+++ b/cups/http-support.c
@@ -1,7 +1,7 @@
 /*
  * HTTP support routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -20,13 +20,13 @@
 #include "cups-private.h"
 #ifdef HAVE_DNSSD
 #  include <dns_sd.h>
-#  ifdef WIN32
+#  ifdef _WIN32
 #    include <io.h>
 #  elif defined(HAVE_POLL)
 #    include <poll.h>
 #  else
 #    include <sys/select.h>
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 #elif defined(HAVE_AVAHI)
 #  include <avahi-client/client.h>
 #  include <avahi-client/lookup.h>
@@ -502,7 +502,6 @@
 		 size_t     bufsize)	/* I - Size of buffer */
 {
   char			data[1024];	/* Source string for MD5 */
-  _cups_md5_state_t	md5state;	/* MD5 state */
   unsigned char		md5sum[16];	/* MD5 digest/sum */
 
 
@@ -517,9 +516,7 @@
            port, name ? name : server, number,
 	   (unsigned)CUPS_RAND() & 0xffff, (unsigned)CUPS_RAND() & 0xffff);
 
-  _cupsMD5Init(&md5state);
-  _cupsMD5Append(&md5state, (unsigned char *)data, (int)strlen(data));
-  _cupsMD5Finish(&md5state, md5sum);
+  cupsHashData("md5", (unsigned char *)data, strlen(data), md5sum, sizeof(md5sum));
 
  /*
   * Generate the UUID from the MD5...
@@ -543,7 +540,7 @@
  * This function is deprecated. Use the httpDecode64_2() function instead
  * which provides buffer length arguments.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 char *					/* O - Decoded string */
@@ -566,6 +563,10 @@
 /*
  * 'httpDecode64_2()' - Base64-decode a string.
  *
+ * The caller must initialize "outlen" to the maximum size of the decoded
+ * string before calling @code httpDecode64_2@.  On return "outlen" contains the
+ * decoded length of the string.
+ *
  * @since CUPS 1.1.21/macOS 10.4@
  */
 
@@ -671,7 +672,7 @@
  * This function is deprecated. Use the httpEncode64_2() function instead
  * which provides buffer length arguments.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 char *					/* O - Encoded string */
@@ -690,7 +691,7 @@
 
 char *					/* O - Encoded string */
 httpEncode64_2(char       *out,		/* I - String to write to */
-	       int        outlen,	/* I - Size of output string */
+	       int        outlen,	/* I - Maximum size of output string */
                const char *in,		/* I - String to read from */
 	       int        inlen)	/* I - Size of input string */
 {
@@ -778,11 +779,11 @@
 /*
  * 'httpGetDateString()' - Get a formatted date/time string from a time value.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 const char *				/* O - Date/time string */
-httpGetDateString(time_t t)		/* I - UNIX time */
+httpGetDateString(time_t t)		/* I - Time in seconds */
 {
   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
 
@@ -798,7 +799,7 @@
  */
 
 const char *				/* O - Date/time string */
-httpGetDateString2(time_t t,		/* I - UNIX time */
+httpGetDateString2(time_t t,		/* I - Time in seconds */
                    char   *s,		/* I - String buffer */
 		   int    slen)		/* I - Size of string buffer */
 {
@@ -819,7 +820,7 @@
  * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
  */
 
-time_t					/* O - UNIX time */
+time_t					/* O - Time in seconds */
 httpGetDateTime(const char *s)		/* I - Date/time string */
 {
   int		i;			/* Looping var */
@@ -888,7 +889,7 @@
  *
  * This function is deprecated; use the httpSeparateURI() function instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 void
@@ -912,7 +913,7 @@
  * This function is deprecated; use the httpSeparateURI() function instead.
  *
  * @since CUPS 1.1.21/macOS 10.4@
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 void
@@ -1031,7 +1032,7 @@
 
     *ptr = '\0';
 
-    if (*uri != ':')
+    if (*uri != ':' || *scheme == '.' || !*scheme)
     {
       *scheme = '\0';
       return (HTTP_URI_STATUS_BAD_SCHEME);
@@ -1302,6 +1303,152 @@
 
 
 /*
+ * '_httpSetDigestAuthString()' - Calculate a Digest authentication response
+ *                                using the appropriate RFC 2068/2617/7616
+ *                                algorithm.
+ */
+
+int					/* O - 1 on success, 0 on failure */
+_httpSetDigestAuthString(
+    http_t     *http,			/* I - HTTP connection */
+    const char *nonce,			/* I - Nonce value */
+    const char *method,			/* I - HTTP method */
+    const char *resource)		/* I - HTTP resource path */
+{
+  char		kd[65],			/* Final MD5/SHA-256 digest */
+		ha1[65],		/* Hash of username:realm:password */
+		ha2[65],		/* Hash of method:request-uri */
+		username[HTTP_MAX_VALUE],
+					/* username:password */
+		*password,		/* Pointer to password */
+		temp[1024],		/* Temporary string */
+		digest[1024];		/* Digest auth data */
+  unsigned char	hash[32];		/* Hash buffer */
+  size_t	hashsize;		/* Size of hash */
+
+
+  DEBUG_printf(("2_httpSetDigestAuthString(http=%p, nonce=\"%s\", method=\"%s\", resource=\"%s\")", http, nonce, method, resource));
+
+  if (nonce && *nonce && strcmp(nonce, http->nonce))
+  {
+    strlcpy(http->nonce, nonce, sizeof(http->nonce));
+
+    if (nonce == http->nextnonce)
+      http->nextnonce[0] = '\0';
+
+    http->nonce_count = 1;
+  }
+  else
+    http->nonce_count ++;
+
+  strlcpy(username, http->userpass, sizeof(username));
+  if ((password = strchr(username, ':')) != NULL)
+    *password++ = '\0';
+  else
+    return (0);
+
+  if (http->algorithm[0])
+  {
+   /*
+    * Follow RFC 2617/7616...
+    */
+
+    int		i;			/* Looping var */
+    char	cnonce[65];		/* cnonce value */
+    const char	*hashalg;		/* Hashing algorithm */
+
+    for (i = 0; i < 64; i ++)
+      cnonce[i] = "0123456789ABCDEF"[CUPS_RAND() & 15];
+    cnonce[64] = '\0';
+
+    if (!_cups_strcasecmp(http->algorithm, "MD5"))
+    {
+     /*
+      * RFC 2617 Digest with MD5
+      */
+
+      hashalg = "md5";
+    }
+    else if (!_cups_strcasecmp(http->algorithm, "SHA-256"))
+    {
+     /*
+      * RFC 7616 Digest with SHA-256
+      */
+
+      hashalg = "sha2-256";
+    }
+    else
+    {
+     /*
+      * Some other algorithm we don't support, skip this one...
+      */
+
+      return (0);
+    }
+
+   /*
+    * Calculate digest value...
+    */
+
+    /* H(A1) = H(username:realm:password) */
+    snprintf(temp, sizeof(temp), "%s:%s:%s", username, http->realm, password);
+    hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha1, sizeof(ha1));
+
+    /* H(A2) = H(method:uri) */
+    snprintf(temp, sizeof(temp), "%s:%s", method, resource);
+    hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha2, sizeof(ha2));
+
+    /* KD = H(H(A1):nonce:nc:cnonce:qop:H(A2)) */
+    snprintf(temp, sizeof(temp), "%s:%s:%08x:%s:%s:%s", ha1, http->nonce, http->nonce_count, cnonce, "auth", ha2);
+    hashsize = (size_t)cupsHashData(hashalg, (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, kd, sizeof(kd));
+
+   /*
+    * Pass the RFC 2617/7616 WWW-Authenticate header...
+    */
+
+    if (http->opaque[0])
+      snprintf(digest, sizeof(digest), "username=\"%s\", realm=\"%s\", nonce=\"%s\", algorithm=%s, qop=auth, opaque=\"%s\", cnonce=\"%s\", nc=%08x, uri=\"%s\", response=\"%s\"", cupsUser(), http->realm, http->nonce, http->algorithm, http->opaque, cnonce, http->nonce_count, resource, kd);
+    else
+      snprintf(digest, sizeof(digest), "username=\"%s\", realm=\"%s\", nonce=\"%s\", algorithm=%s, qop=auth, cnonce=\"%s\", nc=%08x, uri=\"%s\", response=\"%s\"", username, http->realm, http->nonce, http->algorithm, cnonce, http->nonce_count, resource, kd);
+  }
+  else
+  {
+   /*
+    * Use old RFC 2069 Digest method...
+    */
+
+    /* H(A1) = H(username:realm:password) */
+    snprintf(temp, sizeof(temp), "%s:%s:%s", username, http->realm, password);
+    hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha1, sizeof(ha1));
+
+    /* H(A2) = H(method:uri) */
+    snprintf(temp, sizeof(temp), "%s:%s", method, resource);
+    hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, ha2, sizeof(ha2));
+
+    /* KD = H(H(A1):nonce:H(A2)) */
+    snprintf(temp, sizeof(temp), "%s:%s:%s", ha1, http->nonce, ha2);
+    hashsize = (size_t)cupsHashData("md5", (unsigned char *)temp, strlen(temp), hash, sizeof(hash));
+    cupsHashString(hash, hashsize, kd, sizeof(kd));
+
+   /*
+    * Pass the old RFC 2069 WWW-Authenticate header...
+    */
+
+    snprintf(digest, sizeof(digest), "username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", username, http->realm, http->nonce, resource, kd);
+  }
+
+  httpSetAuthString(http, "Digest", digest);
+
+  return (1);
+}
+
+
+/*
  * 'httpStateString()' - Return the string describing a HTTP state value.
  *
  * @since CUPS 2.0/OS 10.10@
@@ -1356,6 +1503,9 @@
     case HTTP_STATUS_MOVED_PERMANENTLY :
         s = _("Moved Permanently");
 	break;
+    case HTTP_STATUS_FOUND :
+        s = _("Found");
+	break;
     case HTTP_STATUS_SEE_OTHER :
         s = _("See Other");
 	break;
@@ -1419,7 +1569,7 @@
  * 'httpStatus()' - Return a short string describing a HTTP status code.
  *
  * The returned string is localized to the current POSIX locale and is based
- * on the status strings defined in RFC 2616.
+ * on the status strings defined in RFC 7231.
  */
 
 const char *				/* O - Localized status string */
@@ -1618,9 +1768,6 @@
     _http_uribuf_t	uribuf;		/* URI buffer */
     int			offline = 0;	/* offline-report state set? */
 #  ifdef HAVE_DNSSD
-#    ifdef WIN32
-#      pragma comment(lib, "dnssd.lib")
-#    endif /* WIN32 */
     DNSServiceRef	ref,		/* DNS-SD master service reference */
 			domainref = NULL,/* DNS-SD service reference for domain */
 			ippref = NULL,	/* DNS-SD service reference for network IPP */
@@ -1749,11 +1896,11 @@
 	  FD_ZERO(&input_set);
 	  FD_SET(DNSServiceRefSockFD(ref), &input_set);
 
-#      ifdef WIN32
+#      ifdef _WIN32
 	  stimeout.tv_sec  = (long)timeout;
 #      else
 	  stimeout.tv_sec  = timeout;
-#      endif /* WIN32 */
+#      endif /* _WIN32 */
 	  stimeout.tv_usec = 0;
 
 	  fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
@@ -2310,6 +2457,8 @@
  * Note: This function is needed because avahi_simple_poll_iterate is broken
  *       and always uses a timeout of 0 (!) milliseconds.
  *       (Avahi Ticket #364)
+ *
+ * @private@
  */
 
 static int				/* O - Number of file descriptors matching */
diff --git a/cups/http.c b/cups/http.c
index 7925513..5c14ef6 100644
--- a/cups/http.c
+++ b/cups/http.c
@@ -1,7 +1,7 @@
 /*
  * HTTP routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * This file contains Kerberos support code, copyright 2006 by
@@ -23,22 +23,26 @@
 #include "cups-private.h"
 #include <fcntl.h>
 #include <math.h>
-#ifdef WIN32
+#ifdef _WIN32
 #  include <tchar.h>
 #else
 #  include <signal.h>
 #  include <sys/time.h>
 #  include <sys/resource.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 #ifdef HAVE_POLL
 #  include <poll.h>
 #endif /* HAVE_POLL */
+#  ifdef HAVE_LIBZ
+#    include <zlib.h>
+#  endif /* HAVE_LIBZ */
 
 
 /*
  * Local functions...
  */
 
+static void		http_add_field(http_t *http, http_field_t field, const char *value, int append);
 #ifdef HAVE_LIBZ
 static void		http_content_coding_finish(http_t *http);
 static void		http_content_coding_start(http_t *http,
@@ -105,7 +109,8 @@
 			  "WWW-Authenticate",
 			  "Accept-Encoding",
 			  "Allow",
-			  "Server"
+			  "Server",
+			  "Authentication-Info"
 			};
 
 
@@ -325,6 +330,12 @@
       http->server = NULL;
     }
 
+    if (http->authentication_info)
+    {
+      _cupsStrFree(http->authentication_info);
+      http->authentication_info = NULL;
+    }
+
     http->expect = (http_status_t)0;
   }
 }
@@ -417,7 +428,7 @@
  *
  * This function is deprecated - use @link httpConnect2@ instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 http_t *				/* O - New HTTP connection */
@@ -439,7 +450,7 @@
 httpConnect2(
     const char        *host,		/* I - Host to connect to */
     int               port,		/* I - Port number */
-    http_addrlist_t   *addrlist,	/* I - List of addresses or NULL to lookup */
+    http_addrlist_t   *addrlist,	/* I - List of addresses or @code NULL@ to lookup */
     int               family,		/* I - Address family to use or @code AF_UNSPEC@ for any */
     http_encryption_t encryption,	/* I - Type of encryption to use */
     int               blocking,		/* I - 1 for blocking connection, 0 for non-blocking */
@@ -482,7 +493,7 @@
  * This function is now deprecated. Please use the @link httpConnect2@ function
  * instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 http_t *				/* O - New HTTP connection */
@@ -609,7 +620,7 @@
 
 
 /*
- * 'httpFlush()' - Flush data from a HTTP connection.
+ * 'httpFlush()' - Flush data read from a HTTP connection.
  */
 
 void
@@ -679,7 +690,7 @@
 
 
 /*
- * 'httpFlushWrite()' - Flush data in write buffer.
+ * 'httpFlushWrite()' - Flush data written to a HTTP connection.
  *
  * @since CUPS 1.2/macOS 10.5@
  */
@@ -751,7 +762,7 @@
 /*
  * 'httpGetActivity()' - Get the most recent activity for a connection.
  *
- * The return value is the UNIX time of the last read or write.
+ * The return value is the time in seconds of the last read or write.
  *
  * @since CUPS 2.0/OS 10.10@
  */
@@ -766,10 +777,10 @@
 /*
  * 'httpGetAuthString()' - Get the current authorization string.
  *
- * The authorization string is set by cupsDoAuthentication() and
- * httpSetAuthString().  Use httpGetAuthString() to retrieve the
- * string to use with httpSetField() for the HTTP_FIELD_AUTHORIZATION
- * value.
+ * The authorization string is set by @link cupsDoAuthentication@ and
+ * @link httpSetAuthString@.  Use @link httpGetAuthString@ to retrieve the
+ * string to use with @link httpSetField@ for the
+ * @code HTTP_FIELD_AUTHORIZATION@ value.
  *
  * @since CUPS 1.3/macOS 10.5@
  */
@@ -891,7 +902,7 @@
  * @since CUPS 1.1.19/macOS 10.3@
  */
 
-const char *				/* O - Cookie data or NULL */
+const char *				/* O - Cookie data or @code NULL@ */
 httpGetCookie(http_t *http)		/* I - HTTP connection */
 {
   return (http ? http->cookie : NULL);
@@ -969,11 +980,14 @@
     case HTTP_FIELD_SERVER :
         return (http->server);
 
+    case HTTP_FIELD_AUTHENTICATION_INFO :
+        return (http->authentication_info);
+
     case HTTP_FIELD_AUTHORIZATION :
         if (http->field_authorization)
 	{
 	 /*
-	  * Special case for WWW-Authenticate: as its contents can be
+	  * Special case for Authorization: as its contents can be
 	  * longer than HTTP_MAX_VALUE...
 	  */
 
@@ -1006,7 +1020,7 @@
  * This function is deprecated and will not return lengths larger than
  * 2^31 - 1; use httpGetLength2() instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Content length */
@@ -1150,7 +1164,7 @@
  * 'httpGets()' - Get a line of text from a HTTP connection.
  */
 
-char *					/* O - Line or NULL */
+char *					/* O - Line or @code NULL@ */
 httpGets(char   *line,			/* I - Line to read into */
          int    length,			/* I - Max length of buffer */
 	 http_t *http)			/* I - HTTP connection */
@@ -1183,11 +1197,11 @@
     * Pre-load the buffer as needed...
     */
 
-#ifdef WIN32
+#ifdef _WIN32
     WSASetLastError(0);
 #else
     errno = 0;
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
     while (http->used == 0)
     {
@@ -1201,11 +1215,11 @@
 	  continue;
 
         DEBUG_puts("3httpGets: Timed out!");
-#ifdef WIN32
+#ifdef _WIN32
         http->error = WSAETIMEDOUT;
 #else
         http->error = ETIMEDOUT;
-#endif /* WIN32 */
+#endif /* _WIN32 */
         return (NULL);
       }
 
@@ -1219,7 +1233,7 @@
 	* Nope, can't get a line this time...
 	*/
 
-#ifdef WIN32
+#ifdef _WIN32
         DEBUG_printf(("3httpGets: recv() error %d!", WSAGetLastError()));
 
         if (WSAGetLastError() == WSAEINTR)
@@ -1256,7 +1270,7 @@
 	  http->error = errno;
 	  continue;
 	}
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
         return (NULL);
       }
@@ -1346,10 +1360,10 @@
 /*
  * 'httpGetSubField()' - Get a sub-field value.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
-char *					/* O - Value or NULL */
+char *					/* O - Value or @code NULL@ */
 httpGetSubField(http_t       *http,	/* I - HTTP connection */
                 http_field_t field,	/* I - Field index */
                 const char   *name,	/* I - Name of sub-field */
@@ -1365,7 +1379,7 @@
  * @since CUPS 1.2/macOS 10.5@
  */
 
-char *					/* O - Value or NULL */
+char *					/* O - Value or @code NULL@ */
 httpGetSubField2(http_t       *http,	/* I - HTTP connection */
                  http_field_t field,	/* I - Field index */
                  const char   *name,	/* I - Name of sub-field */
@@ -1523,9 +1537,9 @@
 httpInitialize(void)
 {
   static int	initialized = 0;	/* Have we been called before? */
-#ifdef WIN32
+#ifdef _WIN32
   WSADATA	winsockdata;		/* WinSock data */
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
   _cupsGlobalLock();
@@ -1535,7 +1549,7 @@
     return;
   }
 
-#ifdef WIN32
+#ifdef _WIN32
   WSAStartup(MAKEWORD(2,2), &winsockdata);
 
 #elif !defined(SO_NOSIGPIPE)
@@ -1557,7 +1571,7 @@
 #  else
   signal(SIGPIPE, SIG_IGN);
 #  endif /* !SO_NOSIGPIPE */
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 #  ifdef HAVE_SSL
   _httpTLSInitialize();
@@ -1616,7 +1630,7 @@
  *
  * This function copies available data from the given HTTP connection, reading
  * a buffer as needed.  The data is still available for reading using
- * @link httpRead@ or @link httpRead2@.
+ * @link httpRead2@.
  *
  * For non-blocking connections the usual timeouts apply.
  *
@@ -1713,7 +1727,7 @@
 #ifdef HAVE_LIBZ
   if (http->used == 0 &&
       (http->coding == _HTTP_CODING_IDENTITY ||
-       (http->coding >= _HTTP_CODING_GUNZIP && http->stream.avail_in == 0)))
+       (http->coding >= _HTTP_CODING_GUNZIP && ((z_stream *)http->stream)->avail_in == 0)))
 #else
   if (http->used == 0)
 #endif /* HAVE_LIBZ */
@@ -1762,16 +1776,16 @@
     int		zerr;			/* Decompressor error */
     z_stream	stream;			/* Copy of decompressor stream */
 
-    if (http->used > 0 && http->stream.avail_in < HTTP_MAX_BUFFER)
+    if (http->used > 0 && ((z_stream *)http->stream)->avail_in < HTTP_MAX_BUFFER)
     {
-      size_t buflen = buflen = HTTP_MAX_BUFFER - http->stream.avail_in;
+      size_t buflen = buflen = HTTP_MAX_BUFFER - ((z_stream *)http->stream)->avail_in;
 					/* Number of bytes to copy */
 
-      if (http->stream.avail_in > 0 &&
-	  http->stream.next_in > http->sbuffer)
-        memmove(http->sbuffer, http->stream.next_in, http->stream.avail_in);
+      if (((z_stream *)http->stream)->avail_in > 0 &&
+	  ((z_stream *)http->stream)->next_in > http->sbuffer)
+        memmove(http->sbuffer, ((z_stream *)http->stream)->next_in, ((z_stream *)http->stream)->avail_in);
 
-      http->stream.next_in = http->sbuffer;
+      ((z_stream *)http->stream)->next_in = http->sbuffer;
 
       if (buflen > (size_t)http->data_remaining)
         buflen = (size_t)http->data_remaining;
@@ -1782,8 +1796,8 @@
       DEBUG_printf(("1httpPeek: Copying %d more bytes of data into "
 		    "decompression buffer.", (int)buflen));
 
-      memcpy(http->sbuffer + http->stream.avail_in, http->buffer, buflen);
-      http->stream.avail_in += buflen;
+      memcpy(http->sbuffer + ((z_stream *)http->stream)->avail_in, http->buffer, buflen);
+      ((z_stream *)http->stream)->avail_in += buflen;
       http->used            -= (int)buflen;
       http->data_remaining  -= (off_t)buflen;
 
@@ -1792,9 +1806,9 @@
     }
 
     DEBUG_printf(("2httpPeek: length=%d, avail_in=%d", (int)length,
-                  (int)http->stream.avail_in));
+                  (int)((z_stream *)http->stream)->avail_in));
 
-    if (inflateCopy(&stream, &(http->stream)) != Z_OK)
+    if (inflateCopy(&stream, (z_stream *)http->stream) != Z_OK)
     {
       DEBUG_puts("2httpPeek: Unable to copy decompressor stream.");
       http->error = ENOMEM;
@@ -1811,14 +1825,14 @@
     {
       DEBUG_printf(("2httpPeek: zerr=%d", zerr));
 #ifdef DEBUG
-      http_debug_hex("2httpPeek", (char *)http->sbuffer, (int)http->stream.avail_in);
+      http_debug_hex("2httpPeek", (char *)http->sbuffer, (int)((z_stream *)http->stream)->avail_in);
 #endif /* DEBUG */
 
       http->error = EIO;
       return (-1);
     }
 
-    bytes = (ssize_t)(length - http->stream.avail_out);
+    bytes = (ssize_t)(length - ((z_stream *)http->stream)->avail_out);
 
 #  else
     DEBUG_puts("2httpPeek: No inflateCopy on this platform, httpPeek does not "
@@ -1845,7 +1859,7 @@
 
   if (bytes < 0)
   {
-#ifdef WIN32
+#ifdef _WIN32
     if (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEWOULDBLOCK)
       bytes = 0;
     else
@@ -1855,7 +1869,7 @@
       bytes = 0;
     else
       http->error = errno;
-#endif /* WIN32 */
+#endif /* _WIN32 */
   }
   else if (bytes == 0)
   {
@@ -1939,7 +1953,7 @@
  * This function is deprecated. Use the httpRead2() function which can
  * read more than 2GB of data.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of bytes read */
@@ -1985,31 +1999,31 @@
   {
     do
     {
-      if (http->stream.avail_in > 0)
+      if (((z_stream *)http->stream)->avail_in > 0)
       {
 	int	zerr;			/* Decompressor error */
 
 	DEBUG_printf(("2httpRead2: avail_in=%d, avail_out=%d",
-	              (int)http->stream.avail_in, (int)length));
+	              (int)((z_stream *)http->stream)->avail_in, (int)length));
 
-	http->stream.next_out  = (Bytef *)buffer;
-	http->stream.avail_out = (uInt)length;
+	((z_stream *)http->stream)->next_out  = (Bytef *)buffer;
+	((z_stream *)http->stream)->avail_out = (uInt)length;
 
-	if ((zerr = inflate(&(http->stream), Z_SYNC_FLUSH)) < Z_OK)
+	if ((zerr = inflate((z_stream *)http->stream, Z_SYNC_FLUSH)) < Z_OK)
 	{
 	  DEBUG_printf(("2httpRead2: zerr=%d", zerr));
 #ifdef DEBUG
-          http_debug_hex("2httpRead2", (char *)http->sbuffer, (int)http->stream.avail_in);
+          http_debug_hex("2httpRead2", (char *)http->sbuffer, (int)((z_stream *)http->stream)->avail_in);
 #endif /* DEBUG */
 
 	  http->error = EIO;
 	  return (-1);
 	}
 
-	bytes = (ssize_t)(length - http->stream.avail_out);
+	bytes = (ssize_t)(length - ((z_stream *)http->stream)->avail_out);
 
 	DEBUG_printf(("2httpRead2: avail_in=%d, avail_out=%d, bytes=%d",
-		      http->stream.avail_in, http->stream.avail_out,
+		      ((z_stream *)http->stream)->avail_in, ((z_stream *)http->stream)->avail_out,
 		      (int)bytes));
       }
       else
@@ -2017,16 +2031,16 @@
 
       if (bytes == 0)
       {
-        ssize_t buflen = HTTP_MAX_BUFFER - (ssize_t)http->stream.avail_in;
+        ssize_t buflen = HTTP_MAX_BUFFER - (ssize_t)((z_stream *)http->stream)->avail_in;
 					/* Additional bytes for buffer */
 
         if (buflen > 0)
         {
-          if (http->stream.avail_in > 0 &&
-              http->stream.next_in > http->sbuffer)
-            memmove(http->sbuffer, http->stream.next_in, http->stream.avail_in);
+          if (((z_stream *)http->stream)->avail_in > 0 &&
+              ((z_stream *)http->stream)->next_in > http->sbuffer)
+            memmove(http->sbuffer, ((z_stream *)http->stream)->next_in, ((z_stream *)http->stream)->avail_in);
 
-	  http->stream.next_in = http->sbuffer;
+	  ((z_stream *)http->stream)->next_in = http->sbuffer;
 
           DEBUG_printf(("1httpRead2: Reading up to %d more bytes of data into "
                         "decompression buffer.", (int)buflen));
@@ -2036,10 +2050,10 @@
 	    if (buflen > http->data_remaining)
 	      buflen = (ssize_t)http->data_remaining;
 
-	    bytes = http_read_buffered(http, (char *)http->sbuffer + http->stream.avail_in, (size_t)buflen);
+	    bytes = http_read_buffered(http, (char *)http->sbuffer + ((z_stream *)http->stream)->avail_in, (size_t)buflen);
           }
           else if (http->data_encoding == HTTP_ENCODING_CHUNKED)
-            bytes = http_read_chunk(http, (char *)http->sbuffer + http->stream.avail_in, (size_t)buflen);
+            bytes = http_read_chunk(http, (char *)http->sbuffer + ((z_stream *)http->stream)->avail_in, (size_t)buflen);
           else
             bytes = 0;
 
@@ -2052,7 +2066,7 @@
                         "decompression buffer.", CUPS_LLCAST bytes));
 
           http->data_remaining  -= bytes;
-          http->stream.avail_in += (uInt)bytes;
+          ((z_stream *)http->stream)->avail_in += (uInt)bytes;
 
 	  if (http->data_remaining <= 0 &&
 	      http->data_encoding == HTTP_ENCODING_CHUNKED)
@@ -2131,7 +2145,7 @@
   if (
 #ifdef HAVE_LIBZ
       (http->coding == _HTTP_CODING_IDENTITY ||
-       (http->coding >= _HTTP_CODING_GUNZIP && http->stream.avail_in == 0)) &&
+       (http->coding >= _HTTP_CODING_GUNZIP && ((z_stream *)http->stream)->avail_in == 0)) &&
 #endif /* HAVE_LIBZ */
       ((http->data_remaining <= 0 &&
         http->data_encoding == HTTP_ENCODING_LENGTH) ||
@@ -2326,7 +2340,7 @@
  * This function is deprecated. Please use the @link httpReconnect2@ function
  * instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - 0 on success, non-zero on failure */
@@ -2416,11 +2430,11 @@
     * Unable to connect...
     */
 
-#ifdef WIN32
+#ifdef _WIN32
     http->error  = WSAGetLastError();
 #else
     http->error  = errno;
-#endif /* WIN32 */
+#endif /* _WIN32 */
     http->status = HTTP_STATUS_ERROR;
 
     DEBUG_printf(("1httpReconnect2: httpAddrConnect failed: %s",
@@ -2467,9 +2481,10 @@
  * 'httpSetAuthString()' - Set the current authorization string.
  *
  * This function just stores a copy of the current authorization string in
- * the HTTP connection object.  You must still call httpSetField() to set
- * HTTP_FIELD_AUTHORIZATION prior to issuing a HTTP request using httpGet(),
- * httpHead(), httpOptions(), httpPost, or httpPut().
+ * the HTTP connection object.  You must still call @link httpSetField@ to set
+ * @code HTTP_FIELD_AUTHORIZATION@ prior to issuing a HTTP request using
+ * @link httpGet@, @link httpHead@, @link httpOptions@, @link httpPost@, or
+ * @link httpPut@.
  *
  * @since CUPS 1.3/macOS 10.5@
  */
@@ -2658,105 +2673,7 @@
       value == NULL)
     return;
 
-  switch (field)
-  {
-    case HTTP_FIELD_ACCEPT_ENCODING :
-        if (http->accept_encoding)
-          _cupsStrFree(http->accept_encoding);
-
-        http->accept_encoding = _cupsStrAlloc(value);
-        break;
-
-    case HTTP_FIELD_ALLOW :
-        if (http->allow)
-          _cupsStrFree(http->allow);
-
-        http->allow = _cupsStrAlloc(value);
-        break;
-
-    case HTTP_FIELD_SERVER :
-        if (http->server)
-          _cupsStrFree(http->server);
-
-        http->server = _cupsStrAlloc(value);
-        break;
-
-    case HTTP_FIELD_WWW_AUTHENTICATE :
-       /* CUPS STR #4503 - don't override WWW-Authenticate for unknown auth schemes */
-        if (http->fields[HTTP_FIELD_WWW_AUTHENTICATE][0] &&
-	    _cups_strncasecmp(value, "Basic ", 6) &&
-	    _cups_strncasecmp(value, "Digest ", 7) &&
-	    _cups_strncasecmp(value, "Negotiate ", 10))
-	{
-	  DEBUG_printf(("1httpSetField: Ignoring unknown auth scheme in \"%s\".", value));
-          return;
-	}
-
-	/* Fall through to copy */
-
-    default :
-	strlcpy(http->fields[field], value, HTTP_MAX_VALUE);
-	break;
-  }
-
-  if (field == HTTP_FIELD_AUTHORIZATION)
-  {
-   /*
-    * Special case for Authorization: as its contents can be
-    * longer than HTTP_MAX_VALUE
-    */
-
-    if (http->field_authorization)
-      free(http->field_authorization);
-
-    http->field_authorization = strdup(value);
-  }
-  else if (field == HTTP_FIELD_HOST)
-  {
-   /*
-    * Special-case for Host: as we don't want a trailing "." on the hostname and
-    * need to bracket IPv6 numeric addresses.
-    */
-
-    char *ptr = strchr(value, ':');
-
-    if (value[0] != '[' && ptr && strchr(ptr + 1, ':'))
-    {
-     /*
-      * Bracket IPv6 numeric addresses...
-      *
-      * This is slightly inefficient (basically copying twice), but is an edge
-      * case and not worth optimizing...
-      */
-
-      snprintf(http->fields[HTTP_FIELD_HOST],
-               sizeof(http->fields[HTTP_FIELD_HOST]), "[%s]", value);
-    }
-    else
-    {
-     /*
-      * Check for a trailing dot on the hostname...
-      */
-
-      ptr = http->fields[HTTP_FIELD_HOST];
-
-      if (*ptr)
-      {
-	ptr += strlen(ptr) - 1;
-
-	if (*ptr == '.')
-	  *ptr = '\0';
-      }
-    }
-  }
-#ifdef HAVE_LIBZ
-  else if (field == HTTP_FIELD_CONTENT_ENCODING &&
-           http->data_encoding != HTTP_ENCODING_FIELDS)
-  {
-    DEBUG_puts("1httpSetField: Calling http_content_coding_start.");
-    http_content_coding_start(http, value);
-  }
-#endif /* HAVE_LIBZ */
+  http_add_field(http, field, value, 0);
 }
 
 
@@ -2820,7 +2737,7 @@
     http_t            *http,		/* I - HTTP connection */
     double            timeout,		/* I - Number of seconds for timeout,
                                                must be greater than 0 */
-    http_timeout_cb_t cb,		/* I - Callback function or NULL */
+    http_timeout_cb_t cb,		/* I - Callback function or @code NULL@ */
     void              *user_data)	/* I - User data pointer */
 {
   if (!http || timeout <= 0.0)
@@ -2854,16 +2771,18 @@
     _httpTLSStop(http);
 #endif /* HAVE_SSL */
 
-#ifdef WIN32
+#ifdef _WIN32
   shutdown(http->fd, SD_RECEIVE);	/* Microsoft-ism... */
 #else
   shutdown(http->fd, SHUT_RD);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
 /*
  * 'httpTrace()' - Send an TRACE request to the server.
+ *
+ * @exclude all@
  */
 
 int					/* O - Status of call (0 = success) */
@@ -3032,7 +2951,12 @@
       httpSetCookie(http, value);
     }
     else if ((field = httpFieldValue(line)) != HTTP_FIELD_UNKNOWN)
-      httpSetField(http, field, value);
+    {
+      http_add_field(http, field, value, 1);
+
+      if (field == HTTP_FIELD_AUTHENTICATION_INFO)
+        httpGetSubField2(http, HTTP_FIELD_AUTHENTICATION_INFO, "nextnonce", http->nextnonce, (int)sizeof(http->nextnonce));
+    }
 #ifdef DEBUG
     else
       DEBUG_printf(("1_httpUpdate: unknown field %s seen!", line));
@@ -3185,12 +3109,12 @@
 
     DEBUG_printf(("6_httpWait: select() returned %d...", nfds));
   }
-#  ifdef WIN32
+#  ifdef _WIN32
   while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
                       WSAGetLastError() == WSAEWOULDBLOCK));
 #  else
   while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 #endif /* HAVE_POLL */
 
   DEBUG_printf(("5_httpWait: returning with nfds=%d, errno=%d...", nfds,
@@ -3226,7 +3150,7 @@
   }
 
 #ifdef HAVE_LIBZ
-  if (http->coding >= _HTTP_CODING_GUNZIP && http->stream.avail_in > 0)
+  if (http->coding >= _HTTP_CODING_GUNZIP && ((z_stream *)http->stream)->avail_in > 0)
   {
     DEBUG_puts("3httpWait: Returning 1 since there is buffered data ready.");
     return (1);
@@ -3259,7 +3183,7 @@
  * This function is deprecated. Use the httpWrite2() function which can
  * write more than 2GB of data.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of bytes written */
@@ -3322,17 +3246,17 @@
       size_t	slen;			/* Bytes to write */
       ssize_t	sret;			/* Bytes written */
 
-      http->stream.next_in   = (Bytef *)buffer;
-      http->stream.avail_in  = (uInt)length;
+      ((z_stream *)http->stream)->next_in   = (Bytef *)buffer;
+      ((z_stream *)http->stream)->avail_in  = (uInt)length;
 
-      while (deflate(&(http->stream), Z_NO_FLUSH) == Z_OK)
+      while (deflate((z_stream *)http->stream, Z_NO_FLUSH) == Z_OK)
       {
-        DEBUG_printf(("1httpWrite2: avail_out=%d", http->stream.avail_out));
+        DEBUG_printf(("1httpWrite2: avail_out=%d", ((z_stream *)http->stream)->avail_out));
 
-        if (http->stream.avail_out > 0)
+        if (((z_stream *)http->stream)->avail_out > 0)
 	  continue;
 
-	slen = _HTTP_MAX_SBUFFER - http->stream.avail_out;
+	slen = _HTTP_MAX_SBUFFER - ((z_stream *)http->stream)->avail_out;
 
         DEBUG_printf(("1httpWrite2: Writing intermediate chunk, len=%d", (int)slen));
 
@@ -3349,8 +3273,8 @@
 	  return (-1);
 	}
 
-	http->stream.next_out  = (Bytef *)http->sbuffer;
-	http->stream.avail_out = (uInt)_HTTP_MAX_SBUFFER;
+	((z_stream *)http->stream)->next_out  = (Bytef *)http->sbuffer;
+	((z_stream *)http->stream)->avail_out = (uInt)_HTTP_MAX_SBUFFER;
       }
 
       bytes = (ssize_t)length;
@@ -3686,6 +3610,125 @@
 }
 
 
+/*
+ * 'http_add_field()' - Add a value for a HTTP field, appending if needed.
+ */
+
+static void
+http_add_field(http_t       *http,	/* I - HTTP connection */
+               http_field_t field,	/* I - HTTP field */
+               const char   *value,	/* I - Value string */
+               int          append)	/* I - Append value? */
+{
+  char		newvalue[1024];		/* New value string */
+  const char 	*oldvalue;		/* Old field value */
+
+
+
+ /*
+  * Optionally append the new value to the existing one...
+  */
+
+  if (append && field != HTTP_FIELD_ACCEPT_ENCODING && field != HTTP_FIELD_ACCEPT_LANGUAGE && field != HTTP_FIELD_ACCEPT_RANGES && field != HTTP_FIELD_ALLOW && field != HTTP_FIELD_LINK && field != HTTP_FIELD_TRANSFER_ENCODING && field != HTTP_FIELD_UPGRADE && field != HTTP_FIELD_WWW_AUTHENTICATE)
+    append = 0;
+
+  if (field == HTTP_FIELD_HOST)
+  {
+   /*
+    * Special-case for Host: as we don't want a trailing "." on the hostname and
+    * need to bracket IPv6 numeric addresses.
+    */
+
+    char *ptr = strchr(value, ':');
+
+    if (value[0] != '[' && ptr && strchr(ptr + 1, ':'))
+    {
+     /*
+      * Bracket IPv6 numeric addresses...
+      */
+
+      snprintf(newvalue, sizeof(newvalue), "[%s]", value);
+      value = newvalue;
+    }
+    else if (*value && value[strlen(value) - 1] == '.')
+    {
+     /*
+      * Strip the trailing dot on the hostname...
+      */
+
+      strlcpy(newvalue, value, sizeof(newvalue));
+      newvalue[strlen(newvalue) - 1] = '\0';
+      value = newvalue;
+    }
+  }
+  else if (append && *value && (oldvalue = httpGetField(http, field)) != NULL && *oldvalue)
+  {
+    snprintf(newvalue, sizeof(newvalue), "%s, %s", oldvalue, value);
+    value = newvalue;
+  }
+
+ /*
+  * Save the new value...
+  */
+
+  switch (field)
+  {
+    case HTTP_FIELD_ACCEPT_ENCODING :
+        if (http->accept_encoding)
+          _cupsStrFree(http->accept_encoding);
+
+        http->accept_encoding = _cupsStrAlloc(value);
+        break;
+
+    case HTTP_FIELD_ALLOW :
+        if (http->allow)
+          _cupsStrFree(http->allow);
+
+        http->allow = _cupsStrAlloc(value);
+        break;
+
+    case HTTP_FIELD_SERVER :
+        if (http->server)
+          _cupsStrFree(http->server);
+
+        http->server = _cupsStrAlloc(value);
+        break;
+
+    case HTTP_FIELD_AUTHENTICATION_INFO :
+        if (http->authentication_info)
+          _cupsStrFree(http->authentication_info);
+
+        http->authentication_info = _cupsStrAlloc(value);
+        break;
+
+    default :
+	strlcpy(http->fields[field], value, HTTP_MAX_VALUE);
+	break;
+  }
+
+  if (field == HTTP_FIELD_AUTHORIZATION)
+  {
+   /*
+    * Special case for Authorization: as its contents can be
+    * longer than HTTP_MAX_VALUE
+    */
+
+    if (http->field_authorization)
+      free(http->field_authorization);
+
+    http->field_authorization = strdup(value);
+  }
+#ifdef HAVE_LIBZ
+  else if (field == HTTP_FIELD_CONTENT_ENCODING &&
+           http->data_encoding != HTTP_ENCODING_FIELDS)
+  {
+    DEBUG_puts("1http_add_field: Calling http_content_coding_start.");
+    http_content_coding_start(http, value);
+  }
+#endif /* HAVE_LIBZ */
+}
+
+
 #ifdef HAVE_LIBZ
 /*
  * 'http_content_coding_finish()' - Finish doing any content encoding.
@@ -3707,13 +3750,13 @@
   {
     case _HTTP_CODING_DEFLATE :
     case _HTTP_CODING_GZIP :
-        http->stream.next_in  = dummy;
-        http->stream.avail_in = 0;
+        ((z_stream *)http->stream)->next_in  = dummy;
+        ((z_stream *)http->stream)->avail_in = 0;
 
         do
         {
-          zerr  = deflate(&(http->stream), Z_FINISH);
-	  bytes = _HTTP_MAX_SBUFFER - http->stream.avail_out;
+          zerr  = deflate((z_stream *)http->stream, Z_FINISH);
+	  bytes = _HTTP_MAX_SBUFFER - ((z_stream *)http->stream)->avail_out;
 
           if (bytes > 0)
 	  {
@@ -3725,15 +3768,18 @@
 	      http_write(http, (char *)http->sbuffer, bytes);
           }
 
-          http->stream.next_out  = (Bytef *)http->sbuffer;
-          http->stream.avail_out = (uInt)_HTTP_MAX_SBUFFER;
+          ((z_stream *)http->stream)->next_out  = (Bytef *)http->sbuffer;
+          ((z_stream *)http->stream)->avail_out = (uInt)_HTTP_MAX_SBUFFER;
 	}
         while (zerr == Z_OK);
 
-        deflateEnd(&(http->stream));
+        deflateEnd((z_stream *)http->stream);
 
         free(http->sbuffer);
+        free(http->stream);
+
         http->sbuffer = NULL;
+        http->stream  = NULL;
 
         if (http->wused)
           httpFlushWrite(http);
@@ -3741,9 +3787,13 @@
 
     case _HTTP_CODING_INFLATE :
     case _HTTP_CODING_GUNZIP :
-        inflateEnd(&(http->stream));
+        inflateEnd((z_stream *)http->stream);
+
         free(http->sbuffer);
+        free(http->stream);
+
         http->sbuffer = NULL;
+        http->stream  = NULL;
         break;
 
     default :
@@ -3813,8 +3863,6 @@
     return;
   }
 
-  memset(&(http->stream), 0, sizeof(http->stream));
-
   switch (coding)
   {
     case _HTTP_CODING_DEFLATE :
@@ -3835,18 +3883,30 @@
         * documentation.
         */
 
-        if ((zerr = deflateInit2(&(http->stream), Z_DEFAULT_COMPRESSION,
-                                 Z_DEFLATED,
-				 coding == _HTTP_CODING_DEFLATE ? -11 : 27, 7,
-				 Z_DEFAULT_STRATEGY)) < Z_OK)
+	if ((http->stream = calloc(1, sizeof(z_stream))) == NULL)
+	{
+          free(http->sbuffer);
+
+          http->sbuffer = NULL;
+          http->status  = HTTP_STATUS_ERROR;
+          http->error   = errno;
+          return;
+	}
+
+        if ((zerr = deflateInit2((z_stream *)http->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, coding == _HTTP_CODING_DEFLATE ? -11 : 27, 7, Z_DEFAULT_STRATEGY)) < Z_OK)
         {
-          http->status = HTTP_STATUS_ERROR;
-          http->error  = zerr == Z_MEM_ERROR ? ENOMEM : EINVAL;
+          free(http->sbuffer);
+          free(http->stream);
+
+          http->sbuffer = NULL;
+          http->stream  = NULL;
+          http->status  = HTTP_STATUS_ERROR;
+          http->error   = zerr == Z_MEM_ERROR ? ENOMEM : EINVAL;
           return;
         }
 
-	http->stream.next_out  = (Bytef *)http->sbuffer;
-	http->stream.avail_out = (uInt)_HTTP_MAX_SBUFFER;
+	((z_stream *)http->stream)->next_out  = (Bytef *)http->sbuffer;
+	((z_stream *)http->stream)->avail_out = (uInt)_HTTP_MAX_SBUFFER;
         break;
 
     case _HTTP_CODING_INFLATE :
@@ -3863,19 +3923,30 @@
         * -15 is raw inflate, 31 is gunzip, per ZLIB documentation.
         */
 
-        if ((zerr = inflateInit2(&(http->stream),
-                                 coding == _HTTP_CODING_INFLATE ? -15 : 31))
-		< Z_OK)
+	if ((http->stream = calloc(1, sizeof(z_stream))) == NULL)
+	{
+          free(http->sbuffer);
+
+          http->sbuffer = NULL;
+          http->status  = HTTP_STATUS_ERROR;
+          http->error   = errno;
+          return;
+	}
+
+        if ((zerr = inflateInit2((z_stream *)http->stream, coding == _HTTP_CODING_INFLATE ? -15 : 31)) < Z_OK)
         {
           free(http->sbuffer);
+          free(http->stream);
+
           http->sbuffer = NULL;
+          http->stream  = NULL;
           http->status  = HTTP_STATUS_ERROR;
           http->error   = zerr == Z_MEM_ERROR ? ENOMEM : EINVAL;
           return;
         }
 
-        http->stream.avail_in = 0;
-        http->stream.next_in  = http->sbuffer;
+        ((z_stream *)http->stream)->avail_in = 0;
+        ((z_stream *)http->stream)->next_in  = http->sbuffer;
         break;
 
     default :
@@ -3898,7 +3969,7 @@
 http_create(
     const char        *host,		/* I - Hostname */
     int               port,		/* I - Port number */
-    http_addrlist_t   *addrlist,	/* I - Address list or NULL */
+    http_addrlist_t   *addrlist,	/* I - Address list or @code NULL@ */
     int               family,		/* I - Address family or AF_UNSPEC */
     http_encryption_t encryption,	/* I - Encryption to use */
     int               blocking,		/* I - 1 for blocking mode */
@@ -3941,7 +4012,7 @@
   if ((http = calloc(sizeof(http_t), 1)) == NULL)
   {
     _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
-    httpAddrFreeList(addrlist);
+    httpAddrFreeList(myaddrlist);
     return (NULL);
   }
 
@@ -4053,7 +4124,7 @@
 
   DEBUG_printf(("http_read(http=%p, buffer=%p, length=" CUPS_LLFMT ")", (void *)http, (void *)buffer, CUPS_LLCAST length));
 
-  if (!http->blocking)
+  if (!http->blocking || http->timeout_value > 0.0)
   {
     while (!httpWait(http, http->wait_value))
     {
@@ -4078,7 +4149,7 @@
 
     if (bytes < 0)
     {
-#ifdef WIN32
+#ifdef _WIN32
       if (WSAGetLastError() != WSAEINTR)
       {
 	http->error = WSAGetLastError();
@@ -4114,7 +4185,7 @@
 	http->error = errno;
 	return (-1);
       }
-#endif /* WIN32 */
+#endif /* _WIN32 */
     }
   }
   while (bytes < 0);
@@ -4128,7 +4199,7 @@
 
   if (bytes < 0)
   {
-#ifdef WIN32
+#ifdef _WIN32
     if (WSAGetLastError() == WSAEINTR)
       bytes = 0;
     else
@@ -4138,7 +4209,7 @@
       bytes = 0;
     else
       http->error = errno;
-#endif /* WIN32 */
+#endif /* _WIN32 */
   }
   else if (bytes == 0)
   {
@@ -4502,7 +4573,7 @@
 http_set_timeout(int    fd,		/* I - File descriptor */
                  double timeout)	/* I - Timeout in seconds */
 {
-#ifdef WIN32
+#ifdef _WIN32
   DWORD tv = (DWORD)(timeout * 1000);
 				      /* Timeout in milliseconds */
 
@@ -4517,7 +4588,7 @@
 
   setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CUPS_SOCAST &tv, sizeof(tv));
   setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, CUPS_SOCAST &tv, sizeof(tv));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -4648,7 +4719,7 @@
   {
     DEBUG_printf(("3http_write: About to write %d bytes.", (int)length));
 
-    if (http->timeout_cb)
+    if (http->timeout_value > 0.0)
     {
 #ifdef HAVE_POLL
       struct pollfd	pfd;		/* Polled file descriptor */
@@ -4679,12 +4750,12 @@
 
 	  nfds = select(http->fd + 1, NULL, &output_set, NULL, &timeout);
 	}
-#  ifdef WIN32
+#  ifdef _WIN32
 	while (nfds < 0 && (WSAGetLastError() == WSAEINTR ||
 			    WSAGetLastError() == WSAEWOULDBLOCK));
 #  else
 	while (nfds < 0 && (errno == EINTR || errno == EAGAIN));
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 #endif /* HAVE_POLL */
 
         if (nfds < 0)
@@ -4692,13 +4763,13 @@
 	  http->error = errno;
 	  return (-1);
 	}
-	else if (nfds == 0 && !(*http->timeout_cb)(http, http->timeout_data))
+	else if (nfds == 0 && (!http->timeout_cb || !(*http->timeout_cb)(http, http->timeout_data)))
 	{
-#ifdef WIN32
+#ifdef _WIN32
 	  http->error = WSAEWOULDBLOCK;
 #else
 	  http->error = EWOULDBLOCK;
-#endif /* WIN32 */
+#endif /* _WIN32 */
 	  return (-1);
 	}
       }
@@ -4717,7 +4788,7 @@
 
     if (bytes < 0)
     {
-#ifdef WIN32
+#ifdef _WIN32
       if (WSAGetLastError() == WSAEINTR)
         continue;
       else if (WSAGetLastError() == WSAEWOULDBLOCK)
@@ -4751,7 +4822,7 @@
         http->error = errno;
 	continue;
       }
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
       DEBUG_printf(("3http_write: error writing data (%s).",
                     strerror(http->error)));
diff --git a/cups/http.h b/cups/http.h
index ccbf77e..e94adae 100644
--- a/cups/http.h
+++ b/cups/http.h
@@ -1,7 +1,7 @@
 /*
  * Hyper-Text Transport Protocol definitions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -25,7 +25,7 @@
 #  include <string.h>
 #  include <time.h>
 #  include <sys/types.h>
-#  ifdef WIN32
+#  ifdef _WIN32
 #    ifndef __CUPS_SSIZE_T_DEFINED
 #      define __CUPS_SSIZE_T_DEFINED
 /* Windows does not support the ssize_t type, so map it to off_t... */
@@ -54,7 +54,7 @@
 #    if defined(LOCAL_PEERCRED) && !defined(SO_PEERCRED)
 #      define SO_PEERCRED LOCAL_PEERCRED
 #    endif /* LOCAL_PEERCRED && !SO_PEERCRED */
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
 
 /*
@@ -85,7 +85,7 @@
 #    define s6_addr32	_S6_un._S6_u32
 #  elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)|| defined(__DragonFly__)
 #    define s6_addr32	__u6_addr.__u6_addr32
-#  elif defined(WIN32)
+#  elif defined(_WIN32)
 /*
  * Windows only defines byte and 16-bit word members of the union and
  * requires special casing of all raw address code...
@@ -109,7 +109,7 @@
  * Types and structures...
  */
 
-typedef enum http_auth_e		/**** HTTP authentication types ****/
+typedef enum http_auth_e		/**** HTTP authentication types @exclude all@ ****/
 {
   HTTP_AUTH_NONE,			/* No authentication in use */
   HTTP_AUTH_BASIC,			/* Basic authentication in use */
@@ -181,6 +181,7 @@
   HTTP_FIELD_ACCEPT_ENCODING,		/* Accepting-Encoding field @since CUPS 1.7/macOS 10.9@ */
   HTTP_FIELD_ALLOW,			/* Allow field @since CUPS 1.7/macOS 10.9@ */
   HTTP_FIELD_SERVER,			/* Server field @since CUPS 1.7/macOS 10.9@ */
+  HTTP_FIELD_AUTHENTICATION_INFO,	/* Authentication-Info field (@since CUPS 2.2.9) */
   HTTP_FIELD_MAX			/* Maximum field index */
 } http_field_t;
 
@@ -248,10 +249,11 @@
 
   HTTP_STATUS_MULTIPLE_CHOICES = 300,	/* Multiple files match request */
   HTTP_STATUS_MOVED_PERMANENTLY,	/* Document has moved permanently */
-  HTTP_STATUS_MOVED_TEMPORARILY,	/* Document has moved temporarily */
-  HTTP_STATUS_SEE_OTHER,		/* See this other link... */
+  HTTP_STATUS_FOUND,			/* Document was found at a different URI */
+  HTTP_STATUS_SEE_OTHER,		/* See this other link */
   HTTP_STATUS_NOT_MODIFIED,		/* File not modified */
   HTTP_STATUS_USE_PROXY,		/* Must use a proxy to access this URI */
+  HTTP_STATUS_TEMPORARY_REDIRECT = 307,	/* Temporary redirection */
 
   HTTP_STATUS_BAD_REQUEST = 400,	/* Bad request */
   HTTP_STATUS_UNAUTHORIZED,		/* Unauthorized to access host */
@@ -285,6 +287,8 @@
   HTTP_STATUS_CUPS_PKI_ERROR,		/* Error negotiating a secure connection @since CUPS 1.5/macOS 10.7@ */
   HTTP_STATUS_CUPS_WEBIF_DISABLED	/* Web interface is disabled @private@ */
 
+#  define HTTP_STATUS_MOVED_TEMPORARILY HTTP_STATUS_FOUND /* Renamed in RFC 7231 */
+
 #  ifndef _CUPS_NO_DEPRECATED
 /* Old names for this enumeration */
 #    define HTTP_ERROR			HTTP_STATUS_ERROR
@@ -393,7 +397,7 @@
   HTTP_URI_CODING_RFC6874 = 16		/* Use RFC 6874 address format */
 } http_uri_coding_t;
 
-typedef enum http_version_e		/**** HTTP version numbers ****/
+typedef enum http_version_e		/**** HTTP version numbers @exclude all@ ****/
 {
   HTTP_VERSION_0_9 = 9,			/* HTTP/0.9 */
   HTTP_VERSION_1_0 = 100,		/* HTTP/1.0 */
@@ -427,6 +431,7 @@
 					 **** used to enumerate all of the
 					 **** addresses that are associated
 					 **** with a hostname. @since CUPS 1.2/macOS 10.5@
+                                         **** @exclude all@
 					 ****/
 {
   struct http_addrlist_s *next;		/* Pointer to next address in list */
@@ -435,7 +440,7 @@
 
 typedef struct _http_s http_t;		/**** HTTP connection type ****/
 
-typedef struct http_credential_s	/**** HTTP credential data @since CUPS 1.5/macOS 10.7@ ****/
+typedef struct http_credential_s	/**** HTTP credential data @since CUPS 1.5/macOS 10.7@ @exclude all@ ****/
 {
   void		*data;			/* Pointer to credential data */
   size_t	datalen;		/* Credential length */
@@ -475,8 +480,7 @@
 extern void		httpInitialize(void);
 extern int		httpOptions(http_t *http, const char *uri);
 extern int		httpPost(http_t *http, const char *uri);
-extern int		httpPrintf(http_t *http, const char *format, ...)
-			__attribute__ ((__format__ (__printf__, 2, 3)));
+extern int		httpPrintf(http_t *http, const char *format, ...) _CUPS_FORMAT(2, 3);
 extern int		httpPut(http_t *http, const char *uri);
 extern int		httpRead(http_t *http, char *buffer, int length) _CUPS_DEPRECATED_MSG("Use httpRead2 instead.");
 extern int		httpReconnect(http_t *http) _CUPS_DEPRECATED_1_6_MSG("Use httpReconnect2 instead.");
diff --git a/cups/ipp-support.c b/cups/ipp-support.c
index b49ac0d..d1ad65c 100644
--- a/cups/ipp-support.c
+++ b/cups/ipp-support.c
@@ -1,8 +1,8 @@
 /*
  * Internet Printing Protocol support functions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright © 2007-2018 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
@@ -960,9 +960,12 @@
     "feed-orientation-supported",
     "finishings",
     "finishings-col",
+    "finishings-col-database",
     "finishings-col-default",
+    "finishings-col-ready",
     "finishings-col-supported",
     "finishings-default",
+    "finishings-ready",
     "finishings-supported",
     "font-name-requested",
     "font-name-requested-default",
@@ -1001,6 +1004,7 @@
     "media-bottom-margin-supported",
     "media-col",
     "media-col-default",
+    "media-col-ready",
     "media-col-supported",
     "media-color-supported",
     "media-default",
@@ -1015,6 +1019,7 @@
     "media-left-margin-supported",
     "media-order-count-supported",
     "media-pre-printed-supported",
+    "media-ready",
     "media-recycled-supported",
     "media-right-margin-supported",
     "media-size-supported",
@@ -1277,9 +1282,12 @@
     "feed-orientation-supported",
     "finishings",
     "finishings-col",
+    "finishings-col-database",
     "finishings-col-default",
+    "finishings-col-ready",
     "finishings-col-supported",
     "finishings-default",
+    "finishings-ready",
     "finishings-supported",
     "font-name-requested",
     "font-name-requested-default",
@@ -1383,6 +1391,7 @@
     "media-bottom-margin-supported",
     "media-col",
     "media-col-default",
+    "media-col-ready",
     "media-col-supported",
     "media-color-supported",
     "media-default",
@@ -1397,6 +1406,7 @@
     "media-left-margin-supported",
     "media-order-count-supported",
     "media-pre-printed-supported",
+    "media-ready",
     "media-recycled-supported",
     "media-right-margin-supported",
     "media-size-supported",
@@ -1582,10 +1592,12 @@
     "job-page-limit",			/* CUPS extension */
     "job-password-encryption-supported",
     "job-password-supported",
+    "job-presets-supported",		/* IPP Presets */
     "job-quota-period",			/* CUPS extension */
     "job-resolvers-supported",
     "job-settable-attributes-supported",
     "job-spooling-supported",
+    "job-triggers-supported",		/* IPP Presets */
     "jpeg-k-octets-supported",		/* CUPS extension */
     "jpeg-x-dimension-supported",	/* CUPS extension */
     "jpeg-y-dimension-supported",	/* CUPS extension */
@@ -1599,8 +1611,6 @@
     "marker-message",			/* CUPS extension */
     "marker-names",			/* CUPS extension */
     "marker-types",			/* CUPS extension */
-    "media-col-ready",
-    "media-ready",
     "member-names",			/* CUPS extension */
     "member-uris",			/* CUPS extension */
     "multiple-destination-uris-supported",/* IPP FaxOut */
@@ -1623,6 +1633,8 @@
     "printer-charge-info",
     "printer-charge-info-uri",
     "printer-commands",			/* CUPS extension */
+    "printer-config-change-date-time",
+    "printer-config-change-time",
     "printer-current-time",
     "printer-detailed-status-messages",
     "printer-device-id",
@@ -2243,7 +2255,7 @@
 /*
  * 'ippTagString()' - Return the tag name corresponding to a tag value.
  *
- * The returned names are defined in RFC 2911 and 3382.
+ * The returned names are defined in RFC 8011 and the IANA IPP Registry.
  *
  * @since CUPS 1.4/macOS 10.6@
  */
@@ -2263,7 +2275,7 @@
 /*
  * 'ippTagValue()' - Return the tag value corresponding to a tag name.
  *
- * The tag names are defined in RFC 2911 and 3382.
+ * The tag names are defined in RFC 8011 and the IANA IPP Registry.
  *
  * @since CUPS 1.4/macOS 10.6@
  */
diff --git a/cups/ipp.c b/cups/ipp.c
index 650d33d..cc9c6af 100644
--- a/cups/ipp.c
+++ b/cups/ipp.c
@@ -1,8 +1,8 @@
 /*
  * Internet Printing Protocol functions for CUPS.
  *
- * Copyright 2007-2017 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright © 2007-2018 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
@@ -19,9 +19,9 @@
 
 #include "cups-private.h"
 #include <regex.h>
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -33,12 +33,8 @@
 			              int num_values);
 static void		ipp_free_values(ipp_attribute_t *attr, int element,
 			                int count);
-static char		*ipp_get_code(const char *locale, char *buffer,
-			              size_t bufsize)
-			              __attribute__((nonnull(1,2)));
-static char		*ipp_lang_code(const char *locale, char *buffer,
-			               size_t bufsize)
-			               __attribute__((nonnull(1,2)));
+static char		*ipp_get_code(const char *locale, char *buffer, size_t bufsize) _CUPS_NONNULL((1, 2));
+static char		*ipp_lang_code(const char *locale, char *buffer, size_t bufsize) _CUPS_NONNULL((1, 2));
 static size_t		ipp_length(ipp_t *ipp, int collection);
 static ssize_t		ipp_read_http(http_t *http, ipp_uchar_t *buffer,
 			              size_t length);
@@ -316,7 +312,7 @@
 
 
 /*
- * 'ippAddDate()' - Add a date attribute to an IPP message.
+ * 'ippAddDate()' - Add a dateTime attribute to an IPP message.
  *
  * The @code ipp@ parameter refers to an IPP message previously created using
  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
@@ -1380,7 +1376,7 @@
  *                         specified string value.
  *
  * Returns non-zero when the attribute contains a matching charset, keyword,
- * language, mimeMediaType, name, text, URI, or URI scheme value.
+ * naturalLanguage, mimeMediaType, name, text, uri, or uriScheme value.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -1483,6 +1479,7 @@
     int             quickcopy)		/* I - 1 for a referenced copy, 0 for normal */
 {
   int			i;		/* Looping var */
+  ipp_tag_t		srctag;		/* Source value tag */
   ipp_attribute_t	*dstattr;	/* Destination attribute */
   _ipp_value_t		*srcval,	/* Source value */
 			*dstval;	/* Destination value */
@@ -1501,144 +1498,86 @@
   * Copy it...
   */
 
-  quickcopy = quickcopy ? IPP_TAG_CUPS_CONST : 0;
+  quickcopy = (quickcopy && (srcattr->value_tag & IPP_TAG_CUPS_CONST)) ? IPP_TAG_CUPS_CONST : 0;
+  srctag    = srcattr->value_tag & IPP_TAG_CUPS_MASK;
 
-  switch (srcattr->value_tag & ~IPP_TAG_CUPS_CONST)
+  switch (srctag)
   {
     case IPP_TAG_ZERO :
         dstattr = ippAddSeparator(dst);
 	break;
 
-    case IPP_TAG_INTEGER :
-    case IPP_TAG_ENUM :
-        dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
-	                         srcattr->name, srcattr->num_values, NULL);
-        if (!dstattr)
-          break;
-
-        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
-             i > 0;
-             i --, srcval ++, dstval ++)
-	  dstval->integer = srcval->integer;
+    case IPP_TAG_UNSUPPORTED_VALUE :
+    case IPP_TAG_DEFAULT :
+    case IPP_TAG_UNKNOWN :
+    case IPP_TAG_NOVALUE :
+    case IPP_TAG_NOTSETTABLE :
+    case IPP_TAG_DELETEATTR :
+    case IPP_TAG_ADMINDEFINE :
+        dstattr = ippAddOutOfBand(dst, srcattr->group_tag, srctag, srcattr->name);
         break;
 
+    case IPP_TAG_INTEGER :
+    case IPP_TAG_ENUM :
     case IPP_TAG_BOOLEAN :
-        dstattr = ippAddBooleans(dst, srcattr->group_tag, srcattr->name,
-	                        srcattr->num_values, NULL);
-        if (!dstattr)
-          break;
-
-        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
-             i > 0;
-             i --, srcval ++, dstval ++)
-	  dstval->boolean = srcval->boolean;
+    case IPP_TAG_DATE :
+    case IPP_TAG_RESOLUTION :
+    case IPP_TAG_RANGE :
+        if ((dstattr = ipp_add_attr(dst, srcattr->name, srcattr->group_tag, srctag, srcattr->num_values)) != NULL)
+	  memcpy(dstattr->values, srcattr->values, (size_t)srcattr->num_values * sizeof(_ipp_value_t));
         break;
 
     case IPP_TAG_TEXT :
     case IPP_TAG_NAME :
+    case IPP_TAG_RESERVED_STRING :
     case IPP_TAG_KEYWORD :
     case IPP_TAG_URI :
     case IPP_TAG_URISCHEME :
     case IPP_TAG_CHARSET :
     case IPP_TAG_LANGUAGE :
     case IPP_TAG_MIMETYPE :
-        dstattr = ippAddStrings(dst, srcattr->group_tag,
-	                        (ipp_tag_t)(srcattr->value_tag | quickcopy),
-	                        srcattr->name, srcattr->num_values, NULL, NULL);
-        if (!dstattr)
+        if ((dstattr = ippAddStrings(dst, srcattr->group_tag, (ipp_tag_t)(srctag | quickcopy), srcattr->name, srcattr->num_values, NULL, NULL)) == NULL)
           break;
 
         if (quickcopy)
 	{
-	  for (i = srcattr->num_values, srcval = srcattr->values,
-	           dstval = dstattr->values;
-	       i > 0;
-	       i --, srcval ++, dstval ++)
-	    dstval->string.text = srcval->string.text;
+	 /*
+	  * Can safely quick-copy these string values...
+	  */
+
+	  memcpy(dstattr->values, srcattr->values, (size_t)srcattr->num_values * sizeof(_ipp_value_t));
         }
-	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
-	{
-	  for (i = srcattr->num_values, srcval = srcattr->values,
-	           dstval = dstattr->values;
-	       i > 0;
-	       i --, srcval ++, dstval ++)
-	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
-	}
 	else
 	{
-	  for (i = srcattr->num_values, srcval = srcattr->values,
-	           dstval = dstattr->values;
-	       i > 0;
-	       i --, srcval ++, dstval ++)
-	    dstval->string.text = _cupsStrRetain(srcval->string.text);
-	}
-        break;
+	 /*
+	  * Otherwise do a normal reference counted copy...
+	  */
 
-    case IPP_TAG_DATE :
-        if (srcattr->num_values != 1)
-          return (NULL);
-
-        dstattr = ippAddDate(dst, srcattr->group_tag, srcattr->name,
-	                     srcattr->values[0].date);
-        break;
-
-    case IPP_TAG_RESOLUTION :
-        dstattr = ippAddResolutions(dst, srcattr->group_tag, srcattr->name,
-	                            srcattr->num_values, IPP_RES_PER_INCH,
-				    NULL, NULL);
-        if (!dstattr)
-          break;
-
-        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
-             i > 0;
-             i --, srcval ++, dstval ++)
-	{
-	  dstval->resolution.xres  = srcval->resolution.xres;
-	  dstval->resolution.yres  = srcval->resolution.yres;
-	  dstval->resolution.units = srcval->resolution.units;
-	}
-        break;
-
-    case IPP_TAG_RANGE :
-        dstattr = ippAddRanges(dst, srcattr->group_tag, srcattr->name,
-	                       srcattr->num_values, NULL, NULL);
-        if (!dstattr)
-          break;
-
-        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
-             i > 0;
-             i --, srcval ++, dstval ++)
-	{
-	  dstval->range.lower = srcval->range.lower;
-	  dstval->range.upper = srcval->range.upper;
+	  for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
+	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
 	}
         break;
 
     case IPP_TAG_TEXTLANG :
     case IPP_TAG_NAMELANG :
-        dstattr = ippAddStrings(dst, srcattr->group_tag,
-	                        (ipp_tag_t)(srcattr->value_tag | quickcopy),
-	                        srcattr->name, srcattr->num_values, NULL, NULL);
-        if (!dstattr)
+        if ((dstattr = ippAddStrings(dst, srcattr->group_tag, (ipp_tag_t)(srctag | quickcopy), srcattr->name, srcattr->num_values, NULL, NULL)) == NULL)
           break;
 
         if (quickcopy)
 	{
-	  for (i = srcattr->num_values, srcval = srcattr->values,
-	           dstval = dstattr->values;
-	       i > 0;
-	       i --, srcval ++, dstval ++)
-	  {
-            dstval->string.language = srcval->string.language;
-	    dstval->string.text     = srcval->string.text;
-          }
+	 /*
+	  * Can safely quick-copy these string values...
+	  */
+
+	  memcpy(dstattr->values, srcattr->values, (size_t)srcattr->num_values * sizeof(_ipp_value_t));
         }
 	else if (srcattr->value_tag & IPP_TAG_CUPS_CONST)
 	{
-	  for (i = srcattr->num_values, srcval = srcattr->values,
-	           dstval = dstattr->values;
-	       i > 0;
-	       i --, srcval ++, dstval ++)
+	 /*
+	  * Otherwise do a normal reference counted copy...
+	  */
+
+	  for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
 	  {
 	    if (srcval == srcattr->values)
               dstval->string.language = _cupsStrAlloc(srcval->string.language);
@@ -1648,32 +1587,13 @@
 	    dstval->string.text = _cupsStrAlloc(srcval->string.text);
           }
         }
-	else
-	{
-	  for (i = srcattr->num_values, srcval = srcattr->values,
-	           dstval = dstattr->values;
-	       i > 0;
-	       i --, srcval ++, dstval ++)
-	  {
-	    if (srcval == srcattr->values)
-              dstval->string.language = _cupsStrRetain(srcval->string.language);
-	    else
-              dstval->string.language = dstattr->values[0].string.language;
-
-	    dstval->string.text = _cupsStrRetain(srcval->string.text);
-          }
-        }
         break;
 
     case IPP_TAG_BEGIN_COLLECTION :
-        dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name,
-	                            srcattr->num_values, NULL);
-        if (!dstattr)
+        if ((dstattr = ippAddCollections(dst, srcattr->group_tag, srcattr->name, srcattr->num_values, NULL)) == NULL)
           break;
 
-        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
-             i > 0;
-             i --, srcval ++, dstval ++)
+        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
 	{
 	  dstval->collection = srcval->collection;
 	  srcval->collection->use ++;
@@ -1682,15 +1602,10 @@
 
     case IPP_TAG_STRING :
     default :
-        /* TODO: Implement quick copy for unknown/octetString values */
-        dstattr = ippAddIntegers(dst, srcattr->group_tag, srcattr->value_tag,
-	                         srcattr->name, srcattr->num_values, NULL);
-        if (!dstattr)
+        if ((dstattr = ipp_add_attr(dst, srcattr->name, srcattr->group_tag, srctag, srcattr->num_values)) == NULL)
           break;
 
-        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values;
-             i > 0;
-             i --, srcval ++, dstval ++)
+        for (i = srcattr->num_values, srcval = srcattr->values, dstval = dstattr->values; i > 0; i --, srcval ++, dstval ++)
 	{
 	  dstval->unknown.length = srcval->unknown.length;
 
@@ -1759,12 +1674,12 @@
 
 
 /*
- * 'ippDateToTime()' - Convert from RFC 1903 Date/Time format to UNIX time
- *                     in seconds.
+ * 'ippDateToTime()' - Convert from RFC 2579 Date/Time format to time in
+ *                     seconds.
  */
 
 time_t					/* O - UNIX time value */
-ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 1903 date info */
+ippDateToTime(const ipp_uchar_t *date)	/* I - RFC 2579 date info */
 {
   struct tm	unixdate;		/* UNIX date/time info */
   time_t	t;			/* Computed time */
@@ -1776,7 +1691,7 @@
   memset(&unixdate, 0, sizeof(unixdate));
 
  /*
-  * RFC-1903 date/time format is:
+  * RFC-2579 date/time format is:
   *
   *    Byte(s)  Description
   *    -------  -----------
@@ -1828,12 +1743,19 @@
 
   ipp->use --;
   if (ipp->use > 0)
+  {
+    DEBUG_printf(("4debug_retain: %p IPP message (use=%d)", (void *)ipp, ipp->use));
     return;
+  }
+
+  DEBUG_printf(("4debug_free: %p IPP message", (void *)ipp));
 
   for (attr = ipp->attrs; attr != NULL; attr = next)
   {
     next = attr->next;
 
+    DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
+
     ipp_free_values(attr, 0, attr->num_values);
 
     if (attr->name)
@@ -1870,6 +1792,8 @@
   if (!attr)
     return;
 
+  DEBUG_printf(("4debug_free: %p %s %s%s (%d values)", (void *)attr, attr->name, attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), attr->num_values));
+
  /*
   * Find the attribute in the list...
   */
@@ -2152,7 +2076,7 @@
  * 'ippGetBoolean()' - Get a boolean value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2181,7 +2105,7 @@
  * 'ippGetCollection()' - Get a collection value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2232,15 +2156,15 @@
 
 
 /*
- * 'ippGetDate()' - Get a date value for an attribute.
+ * 'ippGetDate()' - Get a dateTime value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
 
-const ipp_uchar_t *			/* O - Date value or @code NULL@ */
+const ipp_uchar_t *			/* O - dateTime value or @code NULL@ */
 ippGetDate(ipp_attribute_t *attr,	/* I - IPP attribute */
            int             element)	/* I - Value number (0-based) */
 {
@@ -2288,7 +2212,7 @@
  * 'ippGetInteger()' - Get the integer/enum value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2341,7 +2265,7 @@
  * 'ippGetOctetString()' - Get an octetString value from an IPP attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -2404,7 +2328,7 @@
  * 'ippGetRange()' - Get a rangeOfInteger value from an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2466,7 +2390,7 @@
  * 'ippGetResolution()' - Get a resolution value for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2560,7 +2484,7 @@
  * 'ippGetString()' - Get the string and optionally the language code for an attribute.
  *
  * The @code element@ parameter specifies which value to get from 0 to
- * @link ippGetCount(attr)@ - 1.
+ * @code ippGetCount(attr)@ - 1.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -2625,7 +2549,7 @@
 
 int					/* O - Major version number or 0 on error */
 ippGetVersion(ipp_t *ipp,		/* I - IPP message */
-              int   *minor)		/* O - Minor version number or @code NULL@ */
+              int   *minor)		/* O - Minor version number or @code NULL@ for don't care */
 {
  /*
   * Range check input...
@@ -2705,6 +2629,8 @@
     * Set default version - usually 2.0...
     */
 
+    DEBUG_printf(("4debug_alloc: %p IPP message", (void *)temp));
+
     if (cg->server_version == 0)
       _cupsSetDefaults();
 
@@ -2722,9 +2648,9 @@
 /*
  *  'ippNewRequest()' - Allocate a new IPP request message.
  *
- * The new request message is initialized with the attributes-charset and
- * attributes-natural-language attributes added. The
- * attributes-natural-language value is derived from the current locale.
+ * The new request message is initialized with the "attributes-charset" and
+ * "attributes-natural-language" attributes added. The
+ * "attributes-natural-language" value is derived from the current locale.
  *
  * @since CUPS 1.2/macOS 10.5@
  */
@@ -2786,11 +2712,11 @@
 /*
  * 'ippNewResponse()' - Allocate a new IPP response message.
  *
- * The new response message is initialized with the same version-number,
- * request-id, attributes-charset, and attributes-natural-language as the
- * provided request message.  If the attributes-charset or
- * attributes-natural-language attributes are missing from the request,
- * "utf-8" and a value derived from the current locale are substituted,
+ * The new response message is initialized with the same "version-number",
+ * "request-id", "attributes-charset", and "attributes-natural-language" as the
+ * provided request message.  If the "attributes-charset" or
+ * "attributes-natural-language" attributes are missing from the request,
+ * 'utf-8' and a value derived from the current locale are substituted,
  * respectively.
  *
  * @since CUPS 1.7/macOS 10.9@
@@ -3068,6 +2994,13 @@
 	    ipp->state = IPP_STATE_DATA;
 	    break;
 	  }
+	  else if (tag == IPP_TAG_ZERO || (tag == IPP_TAG_OPERATION && ipp->curtag != IPP_TAG_ZERO))
+	  {
+	    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Invalid group tag."), 1);
+	    DEBUG_printf(("1ippReadIO: bad tag 0x%02x.", tag));
+	    _cupsBufferRelease((char *)buffer);
+	    return (IPP_STATE_ERROR);
+	  }
           else if (tag < IPP_TAG_UNSUPPORTED_VALUE)
 	  {
 	   /*
@@ -3365,7 +3298,10 @@
                 value->boolean = (char)buffer[0];
 	        break;
 
-            case IPP_TAG_NOVALUE :
+	    case IPP_TAG_UNSUPPORTED_VALUE :
+	    case IPP_TAG_DEFAULT :
+	    case IPP_TAG_UNKNOWN :
+	    case IPP_TAG_NOVALUE :
 	    case IPP_TAG_NOTSETTABLE :
 	    case IPP_TAG_DELETEATTR :
 	    case IPP_TAG_ADMINDEFINE :
@@ -3385,6 +3321,7 @@
 
 	    case IPP_TAG_TEXT :
 	    case IPP_TAG_NAME :
+	    case IPP_TAG_RESERVED_STRING :
 	    case IPP_TAG_KEYWORD :
 	    case IPP_TAG_URI :
 	    case IPP_TAG_URISCHEME :
@@ -3706,7 +3643,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3748,7 +3685,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3789,7 +3726,7 @@
 
 
 /*
- * 'ippSetDate()' - Set a date value in an attribute.
+ * 'ippSetDate()' - Set a dateTime value in an attribute.
  *
  * The @code ipp@ parameter refers to an IPP message previously created using
  * the @link ippNew@, @link ippNewRequest@, or  @link ippNewResponse@ functions.
@@ -3797,7 +3734,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3806,7 +3743,7 @@
 ippSetDate(ipp_t             *ipp,	/* I  - IPP message */
            ipp_attribute_t   **attr,	/* IO - IPP attribute */
            int               element,	/* I  - Value number (0-based) */
-           const ipp_uchar_t *datevalue)/* I  - Date value */
+           const ipp_uchar_t *datevalue)/* I  - dateTime value */
 {
   _ipp_value_t	*value;			/* Current value */
 
@@ -3854,7 +3791,7 @@
     ipp_tag_t       group_tag)		/* I  - Group tag */
 {
  /*
-  * Range check input - group tag must be 0x01 to 0x0F, per RFC 2911...
+  * Range check input - group tag must be 0x01 to 0x0F, per RFC 8011...
   */
 
   if (!ipp || !attr || !*attr ||
@@ -3881,7 +3818,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -3966,7 +3903,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -4084,7 +4021,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -4164,7 +4101,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -4272,7 +4209,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * @since CUPS 1.6/macOS 10.8@
  */
@@ -4285,17 +4222,21 @@
 {
   char		*temp;			/* Temporary string */
   _ipp_value_t	*value;			/* Current value */
+  ipp_tag_t	value_tag;		/* Value tag */
 
 
  /*
   * Range check input...
   */
 
+  if (attr && *attr)
+    value_tag = (*attr)->value_tag & IPP_TAG_CUPS_MASK;
+  else
+    value_tag = IPP_TAG_ZERO;
+
   if (!ipp || !attr || !*attr ||
-      ((*attr)->value_tag != IPP_TAG_TEXTLANG &&
-      (*attr)->value_tag != IPP_TAG_NAMELANG &&
-       ((*attr)->value_tag < IPP_TAG_TEXT ||
-        (*attr)->value_tag > IPP_TAG_MIMETYPE)) ||
+      (value_tag < IPP_TAG_TEXT && value_tag != IPP_TAG_TEXTLANG &&
+       value_tag != IPP_TAG_NAMELANG) || value_tag > IPP_TAG_MIMETYPE ||
       element < 0 || element > (*attr)->num_values || !strvalue)
     return (0);
 
@@ -4334,7 +4275,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * The @code format@ parameter uses formatting characters compatible with the
  * printf family of standard functions.  Additional arguments follow it as
@@ -4372,7 +4313,7 @@
  * The @code attr@ parameter may be modified as a result of setting the value.
  *
  * The @code element@ parameter specifies which value to set from 0 to
- * @link ippGetCount(attr)@.
+ * @code ippGetCount(attr)@.
  *
  * The @code format@ parameter uses formatting characters compatible with the
  * printf family of standard functions.  Additional arguments follow it as
@@ -4710,19 +4651,19 @@
 
 
 /*
- * 'ippTimeToDate()' - Convert from UNIX time to RFC 1903 format.
+ * 'ippTimeToDate()' - Convert from time in seconds to RFC 2579 format.
  */
 
-const ipp_uchar_t *			/* O - RFC-1903 date/time data */
-ippTimeToDate(time_t t)			/* I - UNIX time value */
+const ipp_uchar_t *			/* O - RFC-2579 date/time data */
+ippTimeToDate(time_t t)			/* I - Time in seconds */
 {
   struct tm	*unixdate;		/* UNIX unixdate/time info */
   ipp_uchar_t	*date = _cupsGlobals()->ipp_date;
-					/* RFC-1903 date/time data */
+					/* RFC-2579 date/time data */
 
 
  /*
-  * RFC-1903 date/time format is:
+  * RFC-2579 date/time format is:
   *
   *    Byte(s)  Description
   *    -------  -----------
@@ -4762,7 +4703,7 @@
  *
  * This function validates the contents of an attribute based on the name and
  * value tag.  1 is returned if the attribute is valid, 0 otherwise.  On
- * failure, cupsLastErrorString() is set to a human-readable message.
+ * failure, @link cupsLastErrorString@ is set to a human-readable message.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -4782,21 +4723,6 @@
   ipp_attribute_t *colattr;		/* Collection attribute */
   regex_t	re;			/* Regular expression */
   ipp_uchar_t	*date;			/* Current date value */
-  static const char * const uri_status_strings[] =
-  {					/* URI status strings */
-    "URI too large",
-    "Bad arguments to function",
-    "Bad resource in URI",
-    "Bad port number in URI",
-    "Bad hostname/address in URI",
-    "Bad username in URI",
-    "Bad scheme in URI",
-    "Bad/empty URI",
-    "OK",
-    "Missing scheme in URI",
-    "Unknown scheme in URI",
-    "Missing resource in URI"
-  };
 
 
  /*
@@ -4818,7 +4744,7 @@
   {
     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
                   _("\"%s\": Bad attribute name - invalid character "
-		    "(RFC 2911 section 4.1.3)."), attr->name);
+		    "(RFC 8011 section 5.1.4)."), attr->name);
     return (0);
   }
 
@@ -4826,7 +4752,7 @@
   {
     ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
                   _("\"%s\": Bad attribute name - bad length %d "
-		    "(RFC 2911 section 4.1.3)."), attr->name,
+		    "(RFC 8011 section 5.1.4)."), attr->name,
 		  (int)(ptr - attr->name));
     return (0);
   }
@@ -4844,7 +4770,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
                           _("\"%s\": Bad boolen value %d "
-			    "(RFC 2911 section 4.1.11)."), attr->name,
+			    "(RFC 8011 section 5.1.21)."), attr->name,
 			  attr->values[i].boolean);
 	    return (0);
 	  }
@@ -4858,7 +4784,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad enum value %d - out of range "
-			    "(RFC 2911 section 4.1.4)."), attr->name,
+			    "(RFC 8011 section 5.1.5)."), attr->name,
 			    attr->values[i].integer);
             return (0);
 	  }
@@ -4872,7 +4798,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad octetString value - bad length %d "
-			    "(RFC 2911 section 4.1.10)."), attr->name,
+			    "(RFC 8011 section 5.1.20)."), attr->name,
 			    attr->values[i].unknown.length);
 	    return (0);
 	  }
@@ -4888,7 +4814,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime month %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[2]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[2]);
 	    return (0);
 	  }
 
@@ -4896,7 +4822,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime day %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[3]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[3]);
 	    return (0);
 	  }
 
@@ -4904,7 +4830,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime hours %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[4]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[4]);
 	    return (0);
 	  }
 
@@ -4912,7 +4838,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime minutes %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[5]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[5]);
 	    return (0);
 	  }
 
@@ -4920,7 +4846,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime seconds %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[6]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[6]);
 	    return (0);
 	  }
 
@@ -4928,7 +4854,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime deciseconds %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[7]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[7]);
 	    return (0);
 	  }
 
@@ -4936,7 +4862,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime UTC sign '%c' "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[8]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[8]);
 	    return (0);
 	  }
 
@@ -4944,7 +4870,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime UTC hours %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[9]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[9]);
 	    return (0);
 	  }
 
@@ -4952,7 +4878,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad dateTime UTC minutes %u "
-			    "(RFC 2911 section 4.1.14)."), attr->name, date[10]);
+			    "(RFC 8011 section 5.1.15)."), attr->name, date[10]);
 	    return (0);
 	  }
 	}
@@ -4966,7 +4892,7 @@
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad resolution value %dx%d%s - cross "
 			    "feed resolution must be positive "
-			    "(RFC 2911 section 4.1.15)."), attr->name,
+			    "(RFC 8011 section 5.1.16)."), attr->name,
 			  attr->values[i].resolution.xres,
 			  attr->values[i].resolution.yres,
 			  attr->values[i].resolution.units ==
@@ -4981,7 +4907,7 @@
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad resolution value %dx%d%s - feed "
 			    "resolution must be positive "
-			    "(RFC 2911 section 4.1.15)."), attr->name,
+			    "(RFC 8011 section 5.1.16)."), attr->name,
 			  attr->values[i].resolution.xres,
 			  attr->values[i].resolution.yres,
 			  attr->values[i].resolution.units ==
@@ -4996,7 +4922,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad resolution value %dx%d%s - bad "
-			    "units value (RFC 2911 section 4.1.15)."),
+			    "units value (RFC 8011 section 5.1.16)."),
 			  attr->name, attr->values[i].resolution.xres,
 			  attr->values[i].resolution.yres,
 			  attr->values[i].resolution.units ==
@@ -5015,7 +4941,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad rangeOfInteger value %d-%d - lower "
-			    "greater than upper (RFC 2911 section 4.1.13)."),
+			    "greater than upper (RFC 8011 section 5.1.14)."),
 			  attr->name, attr->values[i].range.lower,
 			  attr->values[i].range.upper);
 	    return (0);
@@ -5071,22 +4997,29 @@
 	    }
 	    else if (*ptr & 0x80)
 	      break;
+	    else if ((*ptr < ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') || *ptr == 0x7f)
+	      break;
 	  }
 
-	  if (*ptr)
-	  {
-	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
-			  _("\"%s\": Bad text value \"%s\" - bad UTF-8 "
-			    "sequence (RFC 2911 section 4.1.1)."), attr->name,
-			  attr->values[i].string.text);
-	    return (0);
-	  }
+          if (*ptr)
+          {
+	    if (*ptr < ' ' || *ptr == 0x7f)
+	    {
+	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad control character (PWG 5100.14 section 8.3)."), attr->name, attr->values[i].string.text);
+	      return (0);
+	    }
+	    else
+	    {
+	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad text value \"%s\" - bad UTF-8 sequence (RFC 8011 section 5.1.2)."), attr->name, attr->values[i].string.text);
+	      return (0);
+	    }
+          }
 
 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_TEXT - 1))
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad text value \"%s\" - bad length %d "
-			    "(RFC 2911 section 4.1.1)."), attr->name,
+			    "(RFC 8011 section 5.1.2)."), attr->name,
 			  attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5129,22 +5062,29 @@
 	    }
 	    else if (*ptr & 0x80)
 	      break;
+	    else if (*ptr < ' ' || *ptr == 0x7f)
+	      break;
 	  }
 
 	  if (*ptr)
 	  {
-	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
-			  _("\"%s\": Bad name value \"%s\" - bad UTF-8 "
-			    "sequence (RFC 2911 section 4.1.2)."), attr->name,
-			  attr->values[i].string.text);
-	    return (0);
-	  }
+	    if (*ptr < ' ' || *ptr == 0x7f)
+	    {
+	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad control character (PWG 5100.14 section 8.1)."), attr->name, attr->values[i].string.text);
+	      return (0);
+	    }
+	    else
+	    {
+	      ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad name value \"%s\" - bad UTF-8 sequence (RFC 8011 section 5.1.3)."), attr->name, attr->values[i].string.text);
+	      return (0);
+	    }
+          }
 
 	  if ((ptr - attr->values[i].string.text) > (IPP_MAX_NAME - 1))
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad name value \"%s\" - bad length %d "
-			    "(RFC 2911 section 4.1.2)."), attr->name,
+			    "(RFC 8011 section 5.1.3)."), attr->name,
 			  attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5164,7 +5104,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad keyword value \"%s\" - invalid "
-			    "character (RFC 2911 section 4.1.3)."),
+			    "character (RFC 8011 section 5.1.4)."),
 			  attr->name, attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5173,7 +5113,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad keyword value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.3)."),
+			    "length %d (RFC 8011 section 5.1.4)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5193,12 +5133,7 @@
 
 	  if (uri_status < HTTP_URI_STATUS_OK)
 	  {
-	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
-			  _("\"%s\": Bad URI value \"%s\" - %s "
-			    "(RFC 2911 section 4.1.5)."), attr->name,
-			  attr->values[i].string.text,
-			  uri_status_strings[uri_status -
-					     HTTP_URI_STATUS_OVERFLOW]);
+	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST, _("\"%s\": Bad URI value \"%s\" - %s (RFC 8011 section 5.1.6)."), attr->name, attr->values[i].string.text, httpURIStatusString(uri_status));
 	    return (0);
 	  }
 
@@ -5206,7 +5141,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad URI value \"%s\" - bad length %d "
-			    "(RFC 2911 section 4.1.5)."), attr->name,
+			    "(RFC 8011 section 5.1.6)."), attr->name,
 			  attr->values[i].string.text,
 			  (int)strlen(attr->values[i].string.text));
 	  }
@@ -5229,7 +5164,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.6)."),
+			    "characters (RFC 8011 section 5.1.7)."),
 			  attr->name, attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5238,7 +5173,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad uriScheme value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.6)."),
+			    "length %d (RFC 8011 section 5.1.7)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5258,7 +5193,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad charset value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.7)."),
+			    "characters (RFC 8011 section 5.1.8)."),
 			  attr->name, attr->values[i].string.text);
 	    return (0);
 	  }
@@ -5267,7 +5202,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad charset value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.7)."),
+			    "length %d (RFC 8011 section 5.1.8)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)(ptr - attr->values[i].string.text));
 	    return (0);
@@ -5313,7 +5248,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.8)."),
+			    "characters (RFC 8011 section 5.1.9)."),
 			  attr->name, attr->values[i].string.text);
 	    regfree(&re);
 	    return (0);
@@ -5323,7 +5258,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad naturalLanguage value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.8)."),
+			    "length %d (RFC 8011 section 5.1.9)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)strlen(attr->values[i].string.text));
 	    regfree(&re);
@@ -5367,7 +5302,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
-			    "characters (RFC 2911 section 4.1.9)."),
+			    "characters (RFC 8011 section 5.1.10)."),
 			  attr->name, attr->values[i].string.text);
 	    regfree(&re);
 	    return (0);
@@ -5377,7 +5312,7 @@
 	  {
 	    ipp_set_error(IPP_STATUS_ERROR_BAD_REQUEST,
 			  _("\"%s\": Bad mimeMediaType value \"%s\" - bad "
-			    "length %d (RFC 2911 section 4.1.9)."),
+			    "length %d (RFC 8011 section 5.1.10)."),
 			  attr->name, attr->values[i].string.text,
 			  (int)strlen(attr->values[i].string.text));
 	    regfree(&re);
@@ -5400,8 +5335,8 @@
  * 'ippValidateAttributes()' - Validate all attributes in an IPP message.
  *
  * This function validates the contents of the IPP message, including each
- * attribute.  Like @link ippValidateAttribute@, cupsLastErrorString() is set
- * to a human-readable message on failure.
+ * attribute.  Like @link ippValidateAttribute@, @link cupsLastErrorString@ is
+ * set to a human-readable message on failure.
  *
  * @since CUPS 1.7/macOS 10.9@
  */
@@ -6397,6 +6332,8 @@
     * Initialize attribute...
     */
 
+    DEBUG_printf(("4debug_alloc: %p %s %s%s (%d values)", (void *)attr, name, num_values > 1 ? "1setOf " : "", ippTagString(value_tag), num_values));
+
     if (name)
       attr->name = _cupsStrAlloc(name);
 
@@ -6474,6 +6411,7 @@
 	  }
 	  break;
 
+      case IPP_TAG_UNSUPPORTED_VALUE :
       case IPP_TAG_DEFAULT :
       case IPP_TAG_UNKNOWN :
       case IPP_TAG_NOVALUE :
@@ -6815,14 +6753,14 @@
 
     if ((bytes = httpRead2(http, (char *)buffer, length - (size_t)tbytes)) < 0)
     {
-#ifdef WIN32
+#ifdef _WIN32
       break;
 #else
       if (errno != EAGAIN && errno != EINTR)
 	break;
 
       bytes = 0;
-#endif /* WIN32 */
+#endif /* _WIN32 */
     }
     else if (bytes == 0)
       break;
@@ -6850,11 +6788,11 @@
               ipp_uchar_t *buffer,	/* O - Read buffer */
 	      size_t      length)	/* I - Number of bytes to read */
 {
-#ifdef WIN32
+#ifdef _WIN32
   return ((ssize_t)read(*fd, buffer, (unsigned)length));
 #else
   return (read(*fd, buffer, length));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -6953,6 +6891,9 @@
     * Reset pointers in the list...
     */
 
+    DEBUG_printf(("4debug_free: %p %s", (void *)*attr, temp->name));
+    DEBUG_printf(("4debug_alloc: %p %s %s%s (%d)", (void *)temp, temp->name, temp->num_values > 1 ? "1setOf " : "", ippTagString(temp->value_tag), temp->num_values));
+
     if (ipp->current == *attr && ipp->prev)
     {
      /*
@@ -7019,9 +6960,9 @@
                ipp_uchar_t *buffer,	/* I - Data to write */
                size_t      length)	/* I - Number of bytes to write */
 {
-#ifdef WIN32
+#ifdef _WIN32
   return ((ssize_t)write(*fd, buffer, (unsigned)length));
 #else
   return (write(*fd, buffer, length));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
diff --git a/cups/ipp.h b/cups/ipp.h
index 54d9d4b..2ee5f1d 100644
--- a/cups/ipp.h
+++ b/cups/ipp.h
@@ -1,7 +1,7 @@
 /*
  * Internet Printing Protocol definitions for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -77,13 +77,13 @@
  * Types and structures...
  */
 
-typedef enum ipp_dstate_e		/**** Document states ****/
+typedef enum ipp_dstate_e		/**** Document states @exclude all@ ****/
 {
-  IPP_DOCUMENT_PENDING = 3,		/* Document is pending */
-  IPP_DOCUMENT_PROCESSING = 5,		/* Document is processing */
-  IPP_DOCUMENT_CANCELED = 7,		/* Document is canceled */
-  IPP_DOCUMENT_ABORTED,			/* Document is aborted */
-  IPP_DOCUMENT_COMPLETED		/* Document is completed */
+  IPP_DSTATE_PENDING = 3,		/* Document is pending */
+  IPP_DSTATE_PROCESSING = 5,		/* Document is processing */
+  IPP_DSTATE_CANCELED = 7,		/* Document is canceled */
+  IPP_DSTATE_ABORTED,			/* Document is aborted */
+  IPP_DSTATE_COMPLETED			/* Document is completed */
 
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_DOCUMENT_PENDING	IPP_DSTATE_PENDING
@@ -94,10 +94,10 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_dstate_t;
 
-typedef enum ipp_finishings_e		/**** Finishings ****/
+typedef enum ipp_finishings_e		/**** Finishings values ****/
 {
   IPP_FINISHINGS_NONE = 3,		/* No finishing */
-  IPP_FINISHINGS_STAPLE,		/* Staple (any location) */
+  IPP_FINISHINGS_STAPLE,		/* Staple (any location/method) */
   IPP_FINISHINGS_PUNCH,			/* Punch (any location/count) */
   IPP_FINISHINGS_COVER,			/* Add cover */
   IPP_FINISHINGS_BIND,			/* Bind */
@@ -169,38 +169,38 @@
 
   /* CUPS extensions for finishings (pre-standard versions of values above) */
   IPP_FINISHINGS_CUPS_PUNCH_TOP_LEFT = 0x40000046,
-					/* Punch 1 hole top left */
-  IPP_FINISHINGS_CUPS_PUNCH_BOTTOM_LEFT,/* Punch 1 hole bottom left */
-  IPP_FINISHINGS_CUPS_PUNCH_TOP_RIGHT,	/* Punch 1 hole top right */
+					/* Punch 1 hole top left @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_BOTTOM_LEFT,/* Punch 1 hole bottom left @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_TOP_RIGHT,	/* Punch 1 hole top right @exclude all@ */
   IPP_FINISHINGS_CUPS_PUNCH_BOTTOM_RIGHT,
-					/* Punch 1 hole bottom right */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_LEFT,	/* Punch 2 holes left side */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_TOP,	/* Punch 2 holes top edge */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_RIGHT,	/* Punch 2 holes right side */
-  IPP_FINISHINGS_CUPS_PUNCH_DUAL_BOTTOM,/* Punch 2 holes bottom edge */
-  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_LEFT,/* Punch 3 holes left side */
-  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_TOP,	/* Punch 3 holes top edge */
+					/* Punch 1 hole bottom right @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_LEFT,	/* Punch 2 holes left side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_TOP,	/* Punch 2 holes top edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_RIGHT,	/* Punch 2 holes right side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_DUAL_BOTTOM,/* Punch 2 holes bottom edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_LEFT,/* Punch 3 holes left side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_TOP,	/* Punch 3 holes top edge @exclude all@ */
   IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_RIGHT,
-					/* Punch 3 holes right side */
+					/* Punch 3 holes right side @exclude all@ */
   IPP_FINISHINGS_CUPS_PUNCH_TRIPLE_BOTTOM,
-					/* Punch 3 holes bottom edge */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_LEFT,	/* Punch 4 holes left side */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_TOP,	/* Punch 4 holes top edge */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_RIGHT,	/* Punch 4 holes right side */
-  IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM,/* Punch 4 holes bottom edge */
+					/* Punch 3 holes bottom edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_LEFT,	/* Punch 4 holes left side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_TOP,	/* Punch 4 holes top edge @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_RIGHT,	/* Punch 4 holes right side @exclude all@ */
+  IPP_FINISHINGS_CUPS_PUNCH_QUAD_BOTTOM,/* Punch 4 holes bottom edge @exclude all@ */
 
   IPP_FINISHINGS_CUPS_FOLD_ACCORDIAN = 0x4000005A,
-					/* Accordian-fold the paper vertically into four sections */
-  IPP_FINISHINGS_CUPS_FOLD_DOUBLE_GATE,	/* Fold the top and bottom quarters of the paper towards the midline, then fold in half vertically */
-  IPP_FINISHINGS_CUPS_FOLD_GATE,	/* Fold the top and bottom quarters of the paper towards the midline */
-  IPP_FINISHINGS_CUPS_FOLD_HALF,	/* Fold the paper in half vertically */
-  IPP_FINISHINGS_CUPS_FOLD_HALF_Z,	/* Fold the paper in half horizontally, then Z-fold the paper vertically */
-  IPP_FINISHINGS_CUPS_FOLD_LEFT_GATE,	/* Fold the top quarter of the paper towards the midline */
-  IPP_FINISHINGS_CUPS_FOLD_LETTER,	/* Fold the paper into three sections vertically; sometimes also known as a C fold*/
-  IPP_FINISHINGS_CUPS_FOLD_PARALLEL,	/* Fold the paper in half vertically two times, yielding four sections */
-  IPP_FINISHINGS_CUPS_FOLD_POSTER,	/* Fold the paper in half horizontally and vertically; sometimes also called a cross fold */
-  IPP_FINISHINGS_CUPS_FOLD_RIGHT_GATE,	/* Fold the bottom quarter of the paper towards the midline */
-  IPP_FINISHINGS_CUPS_FOLD_Z		/* Fold the paper vertically into three sections, forming a Z */
+					/* Accordian-fold the paper vertically into four sections @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_DOUBLE_GATE,	/* Fold the top and bottom quarters of the paper towards the midline, then fold in half vertically @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_GATE,	/* Fold the top and bottom quarters of the paper towards the midline @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_HALF,	/* Fold the paper in half vertically @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_HALF_Z,	/* Fold the paper in half horizontally, then Z-fold the paper vertically @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_LEFT_GATE,	/* Fold the top quarter of the paper towards the midline @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_LETTER,	/* Fold the paper into three sections vertically; sometimes also known as a C fold @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_PARALLEL,	/* Fold the paper in half vertically two times, yielding four sections @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_POSTER,	/* Fold the paper in half horizontally and vertically; sometimes also called a cross fold @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_RIGHT_GATE,	/* Fold the bottom quarter of the paper towards the midline @exclude all@ */
+  IPP_FINISHINGS_CUPS_FOLD_Z		/* Fold the paper vertically into three sections, forming a Z @exclude all@ */
 } ipp_finishings_t;
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_FINISHINGS_JOB_OFFSET	IPP_FINISHINGS_JOG_OFFSET
@@ -208,7 +208,7 @@
 typedef enum ipp_finishings_e ipp_finish_t;
 #  endif /* !_CUPS_NO_DEPRECATED */
 
-typedef enum ipp_jcollate_e		/**** Job collation types ****/
+typedef enum ipp_jcollate_e		/**** Job collation types @deprecated@ @exclude all@ ****/
 {
   IPP_JCOLLATE_UNCOLLATED_SHEETS = 3,
   IPP_JCOLLATE_COLLATED_DOCUMENTS,
@@ -248,98 +248,98 @@
 {
   IPP_OP_CUPS_INVALID = -1,		/* Invalid operation name for @link ippOpValue@ */
   IPP_OP_CUPS_NONE = 0,			/* No operation @private@ */
-  IPP_OP_PRINT_JOB = 0x0002,		/* Print a single file */
-  IPP_OP_PRINT_URI,			/* Print a single URL */
-  IPP_OP_VALIDATE_JOB,			/* Validate job options */
-  IPP_OP_CREATE_JOB,			/* Create an empty print job */
-  IPP_OP_SEND_DOCUMENT,			/* Add a file to a job */
-  IPP_OP_SEND_URI,			/* Add a URL to a job */
-  IPP_OP_CANCEL_JOB,			/* Cancel a job */
-  IPP_OP_GET_JOB_ATTRIBUTES,		/* Get job attributes */
-  IPP_OP_GET_JOBS,			/* Get a list of jobs */
-  IPP_OP_GET_PRINTER_ATTRIBUTES,	/* Get printer attributes */
-  IPP_OP_HOLD_JOB,			/* Hold a job for printing */
-  IPP_OP_RELEASE_JOB,			/* Release a job for printing */
-  IPP_OP_RESTART_JOB,			/* Reprint a job */
-  IPP_OP_PAUSE_PRINTER = 0x0010,	/* Stop a printer */
-  IPP_OP_RESUME_PRINTER,		/* Start a printer */
-  IPP_OP_PURGE_JOBS,			/* Cancel all jobs */
-  IPP_OP_SET_PRINTER_ATTRIBUTES,	/* Set printer attributes */
-  IPP_OP_SET_JOB_ATTRIBUTES,		/* Set job attributes */
-  IPP_OP_GET_PRINTER_SUPPORTED_VALUES,	/* Get supported attribute values */
-  IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,	/* Create one or more printer subscriptions @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_CREATE_JOB_SUBSCRIPTIONS,	/* Create one of more job subscriptions @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES,	/* Get subscription attributes @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_GET_SUBSCRIPTIONS,		/* Get list of subscriptions @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_RENEW_SUBSCRIPTION,		/* Renew a printer subscription @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_CANCEL_SUBSCRIPTION,		/* Cancel a subscription @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_GET_NOTIFICATIONS,		/* Get notification events @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_SEND_NOTIFICATIONS,		/* Send notification events @private@ */
-  IPP_OP_GET_RESOURCE_ATTRIBUTES,	/* Get resource attributes @private@ */
-  IPP_OP_GET_RESOURCE_DATA,		/* Get resource data @private@ */
-  IPP_OP_GET_RESOURCES,			/* Get list of resources @private@ */
-  IPP_OP_GET_PRINT_SUPPORT_FILES,	/* Get printer support files @private@ */
-  IPP_OP_ENABLE_PRINTER,		/* Start a printer */
-  IPP_OP_DISABLE_PRINTER,		/* Stop a printer */
+  IPP_OP_PRINT_JOB = 0x0002,		/* Print-Job: Print a single file */
+  IPP_OP_PRINT_URI,			/* Print-URI: Print a single URL @exclude all@ */
+  IPP_OP_VALIDATE_JOB,			/* Validate-Job: Validate job values prior to submission */
+  IPP_OP_CREATE_JOB,			/* Create-Job: Create an empty print job */
+  IPP_OP_SEND_DOCUMENT,			/* Send-Document: Add a file to a job */
+  IPP_OP_SEND_URI,			/* Send-URI: Add a URL to a job @exclude all@ */
+  IPP_OP_CANCEL_JOB,			/* Cancel-Job: Cancel a job */
+  IPP_OP_GET_JOB_ATTRIBUTES,		/* Get-Job-Attribute: Get information about a job */
+  IPP_OP_GET_JOBS,			/* Get-Jobs: Get a list of jobs */
+  IPP_OP_GET_PRINTER_ATTRIBUTES,	/* Get-Printer-Attributes: Get information about a printer */
+  IPP_OP_HOLD_JOB,			/* Hold-Job: Hold a job for printing */
+  IPP_OP_RELEASE_JOB,			/* Release-Job: Release a job for printing */
+  IPP_OP_RESTART_JOB,			/* Restart-Job: Reprint a job @deprecated@ */
+  IPP_OP_PAUSE_PRINTER = 0x0010,	/* Pause-Printer: Stop a printer */
+  IPP_OP_RESUME_PRINTER,		/* Resume-Printer: Start a printer */
+  IPP_OP_PURGE_JOBS,			/* Purge-Jobs: Delete all jobs @deprecated@ @exclude all@ */
+  IPP_OP_SET_PRINTER_ATTRIBUTES,	/* Set-Printer-Attributes: Set printer values */
+  IPP_OP_SET_JOB_ATTRIBUTES,		/* Set-Job-Attributes: Set job values */
+  IPP_OP_GET_PRINTER_SUPPORTED_VALUES,	/* Get-Printer-Supported-Values: Get supported values */
+  IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS,	/* Create-Printer-Subscriptions: Create one or more printer subscriptions @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_CREATE_JOB_SUBSCRIPTIONS,	/* Create-Job-Subscriptions: Create one of more job subscriptions @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES,	/* Get-Subscription-Attributes: Get subscription information @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_GET_SUBSCRIPTIONS,		/* Get-Subscriptions: Get list of subscriptions @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_RENEW_SUBSCRIPTION,		/* Renew-Subscription: Renew a printer subscription @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_CANCEL_SUBSCRIPTION,		/* Cancel-Subscription: Cancel a subscription @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_GET_NOTIFICATIONS,		/* Get-Notifications: Get notification events @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_SEND_NOTIFICATIONS,		/* Send-Notifications: Send notification events @private@ */
+  IPP_OP_GET_RESOURCE_ATTRIBUTES,	/* Get-Resource-Attributes: Get resource information @private@ */
+  IPP_OP_GET_RESOURCE_DATA,		/* Get-Resource-Data: Get resource data @private@ @deprecated@ */
+  IPP_OP_GET_RESOURCES,			/* Get-Resources: Get list of resources @private@ */
+  IPP_OP_GET_PRINT_SUPPORT_FILES,	/* Get-Printer-Support-Files: Get printer support files @private@ */
+  IPP_OP_ENABLE_PRINTER,		/* Enable-Printer: Accept new jobs for a printer */
+  IPP_OP_DISABLE_PRINTER,		/* Disable-Printer: Reject new jobs for a printer */
   IPP_OP_PAUSE_PRINTER_AFTER_CURRENT_JOB,
-					/* Stop printer after the current job */
-  IPP_OP_HOLD_NEW_JOBS,			/* Hold new jobs */
-  IPP_OP_RELEASE_HELD_NEW_JOBS,		/* Release new jobs */
-  IPP_OP_DEACTIVATE_PRINTER,		/* Stop a printer */
-  IPP_OP_ACTIVATE_PRINTER,		/* Start a printer */
-  IPP_OP_RESTART_PRINTER,		/* Restart a printer */
-  IPP_OP_SHUTDOWN_PRINTER,		/* Turn a printer off */
-  IPP_OP_STARTUP_PRINTER,		/* Turn a printer on */
-  IPP_OP_REPROCESS_JOB,			/* Reprint a job */
-  IPP_OP_CANCEL_CURRENT_JOB,		/* Cancel the current job */
-  IPP_OP_SUSPEND_CURRENT_JOB,		/* Suspend the current job */
-  IPP_OP_RESUME_JOB,			/* Resume the current job */
-  IPP_OP_PROMOTE_JOB,			/* Promote a job to print sooner */
-  IPP_OP_SCHEDULE_JOB_AFTER,		/* Schedule a job to print after another */
-  IPP_OP_CANCEL_DOCUMENT = 0x0033,	/* Cancel-Document */
-  IPP_OP_GET_DOCUMENT_ATTRIBUTES,	/* Get-Document-Attributes */
-  IPP_OP_GET_DOCUMENTS,			/* Get-Documents */
-  IPP_OP_DELETE_DOCUMENT,		/* Delete-Document */
-  IPP_OP_SET_DOCUMENT_ATTRIBUTES,	/* Set-Document-Attributes */
-  IPP_OP_CANCEL_JOBS,			/* Cancel-Jobs */
-  IPP_OP_CANCEL_MY_JOBS,		/* Cancel-My-Jobs */
-  IPP_OP_RESUBMIT_JOB,			/* Resubmit-Job */
-  IPP_OP_CLOSE_JOB,			/* Close-Job */
-  IPP_OP_IDENTIFY_PRINTER,		/* Identify-Printer */
-  IPP_OP_VALIDATE_DOCUMENT,		/* Validate-Document */
-  IPP_OP_ADD_DOCUMENT_IMAGES,		/* Add-Document-Images */
-  IPP_OP_ACKNOWLEDGE_DOCUMENT,		/* Acknowledge-Document */
-  IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER,	/* Acknowledge-Identify-Printer */
-  IPP_OP_ACKNOWLEDGE_JOB,		/* Acknowledge-Job */
-  IPP_OP_FETCH_DOCUMENT,		/* Fetch-Document */
-  IPP_OP_FETCH_JOB,			/* Fetch-Job */
-  IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES,	/* Get-Output-Device-Attributes */
-  IPP_OP_UPDATE_ACTIVE_JOBS,		/* Update-Active-Jobs */
-  IPP_OP_DEREGISTER_OUTPUT_DEVICE,	/* Deregister-Output-Device */
-  IPP_OP_UPDATE_DOCUMENT_STATUS,	/* Update-Document-Status */
-  IPP_OP_UPDATE_JOB_STATUS,		/* Update-Job-Status */
+					/* Pause-Printer-After-Current-Job: Stop printer after the current job */
+  IPP_OP_HOLD_NEW_JOBS,			/* Hold-New-Jobs: Hold new jobs */
+  IPP_OP_RELEASE_HELD_NEW_JOBS,		/* Release-Held-New-Jobs: Release new jobs that were previously held */
+  IPP_OP_DEACTIVATE_PRINTER,		/* Deactivate-Printer: Stop a printer and do not accept jobs @deprecated@ @exclude all@ */
+  IPP_OP_ACTIVATE_PRINTER,		/* Activate-Printer: Start a printer and accept jobs @deprecated@ @exclude all@ */
+  IPP_OP_RESTART_PRINTER,		/* Restart-Printer: Restart a printer @exclude all@ */
+  IPP_OP_SHUTDOWN_PRINTER,		/* Shutdown-Printer: Turn a printer off @exclude all@ */
+  IPP_OP_STARTUP_PRINTER,		/* Startup-Printer: Turn a printer on @exclude all@ */
+  IPP_OP_REPROCESS_JOB,			/* Reprocess-Job: Reprint a job @deprecated@ @exclude all@*/
+  IPP_OP_CANCEL_CURRENT_JOB,		/* Cancel-Current-Job: Cancel the current job */
+  IPP_OP_SUSPEND_CURRENT_JOB,		/* Suspend-Current-Job: Suspend the current job */
+  IPP_OP_RESUME_JOB,			/* Resume-Job: Resume the current job */
+  IPP_OP_PROMOTE_JOB,			/* Promote-Job: Promote a job to print sooner */
+  IPP_OP_SCHEDULE_JOB_AFTER,		/* Schedule-Job-After: Schedule a job to print after another */
+  IPP_OP_CANCEL_DOCUMENT = 0x0033,	/* Cancel-Document: Cancel a document @exclude all@ */
+  IPP_OP_GET_DOCUMENT_ATTRIBUTES,	/* Get-Document-Attributes: Get document information @exclude all@ */
+  IPP_OP_GET_DOCUMENTS,			/* Get-Documents: Get a list of documents in a job @exclude all@ */
+  IPP_OP_DELETE_DOCUMENT,		/* Delete-Document: Delete a document @deprecated@  @exclude all@ */
+  IPP_OP_SET_DOCUMENT_ATTRIBUTES,	/* Set-Document-Attributes: Set document values @exclude all@ */
+  IPP_OP_CANCEL_JOBS,			/* Cancel-Jobs: Cancel all jobs (administrative) */
+  IPP_OP_CANCEL_MY_JOBS,		/* Cancel-My-Jobs: Cancel a user's jobs */
+  IPP_OP_RESUBMIT_JOB,			/* Resubmit-Job: Copy and reprint a job @exclude all@ */
+  IPP_OP_CLOSE_JOB,			/* Close-Job: Close a job and start printing */
+  IPP_OP_IDENTIFY_PRINTER,		/* Identify-Printer: Make the printer beep, flash, or display a message for identification */
+  IPP_OP_VALIDATE_DOCUMENT,		/* Validate-Document: Validate document values prior to submission @exclude all@ */
+  IPP_OP_ADD_DOCUMENT_IMAGES,		/* Add-Document-Images: Add image(s) from the specified scanner source @exclude all@ */
+  IPP_OP_ACKNOWLEDGE_DOCUMENT,		/* Acknowledge-Document: Acknowledge processing of a document @exclude all@ */
+  IPP_OP_ACKNOWLEDGE_IDENTIFY_PRINTER,	/* Acknowledge-Identify-Printer: Acknowledge action on an Identify-Printer request @exclude all@ */
+  IPP_OP_ACKNOWLEDGE_JOB,		/* Acknowledge-Job: Acknowledge processing of a job @exclude all@ */
+  IPP_OP_FETCH_DOCUMENT,		/* Fetch-Document: Fetch a document for processing @exclude all@ */
+  IPP_OP_FETCH_JOB,			/* Fetch-Job: Fetch a job for processing @exclude all@ */
+  IPP_OP_GET_OUTPUT_DEVICE_ATTRIBUTES,	/* Get-Output-Device-Attributes: Get printer information for a specific output device @exclude all@ */
+  IPP_OP_UPDATE_ACTIVE_JOBS,		/* Update-Active-Jobs: Update the list of active jobs that a proxy has processed @exclude all@ */
+  IPP_OP_DEREGISTER_OUTPUT_DEVICE,	/* Deregister-Output-Device: Remove an output device @exclude all@ */
+  IPP_OP_UPDATE_DOCUMENT_STATUS,	/* Update-Document-Status: Update document values @exclude all@ */
+  IPP_OP_UPDATE_JOB_STATUS,		/* Update-Job-Status: Update job values @exclude all@ */
   IPP_OP_UPDATE_OUTPUT_DEVICE_ATTRIBUTES,
-					/* Update-Output-Device-Attributes */
-  IPP_OP_GET_NEXT_DOCUMENT_DATA,	/* Get-Next-Document-Data */
+					/* Update-Output-Device-Attributes: Update output device values @exclude all@ */
+  IPP_OP_GET_NEXT_DOCUMENT_DATA,	/* Get-Next-Document-Data: Scan more document data @exclude all@ */
 
   IPP_OP_PRIVATE = 0x4000,		/* Reserved @private@ */
-  IPP_OP_CUPS_GET_DEFAULT,		/* Get the default printer */
-  IPP_OP_CUPS_GET_PRINTERS,		/* Get a list of printers and/or classes */
-  IPP_OP_CUPS_ADD_MODIFY_PRINTER,	/* Add or modify a printer */
-  IPP_OP_CUPS_DELETE_PRINTER,		/* Delete a printer */
-  IPP_OP_CUPS_GET_CLASSES,		/* Get a list of classes @deprecated@ */
-  IPP_OP_CUPS_ADD_MODIFY_CLASS,		/* Add or modify a class */
-  IPP_OP_CUPS_DELETE_CLASS,		/* Delete a class */
-  IPP_OP_CUPS_ACCEPT_JOBS,		/* Accept new jobs on a printer */
-  IPP_OP_CUPS_REJECT_JOBS,		/* Reject new jobs on a printer */
-  IPP_OP_CUPS_SET_DEFAULT,		/* Set the default printer */
-  IPP_OP_CUPS_GET_DEVICES,		/* Get a list of supported devices @deprecated@ */
-  IPP_OP_CUPS_GET_PPDS,			/* Get a list of supported drivers @deprecated@ */
-  IPP_OP_CUPS_MOVE_JOB,			/* Move a job to a different printer */
-  IPP_OP_CUPS_AUTHENTICATE_JOB,		/* Authenticate a job @since CUPS 1.2/macOS 10.5@ */
-  IPP_OP_CUPS_GET_PPD,			/* Get a PPD file @deprecated@ */
-  IPP_OP_CUPS_GET_DOCUMENT = 0x4027,	/* Get a document file @since CUPS 1.4/macOS 10.6@ */
-  IPP_OP_CUPS_CREATE_LOCAL_PRINTER	/* Create a local (temporary) printer @since CUPS 2.2 */
+  IPP_OP_CUPS_GET_DEFAULT,		/* CUPS-Get-Default: Get the default printer */
+  IPP_OP_CUPS_GET_PRINTERS,		/* CUPS-Get-Printers: Get a list of printers and/or classes */
+  IPP_OP_CUPS_ADD_MODIFY_PRINTER,	/* CUPS-Add-Modify-Printer: Add or modify a printer */
+  IPP_OP_CUPS_DELETE_PRINTER,		/* CUPS-Delete-Printer: Delete a printer */
+  IPP_OP_CUPS_GET_CLASSES,		/* CUPS-Get-Classes: Get a list of classes @deprecated@ @exclude all@ */
+  IPP_OP_CUPS_ADD_MODIFY_CLASS,		/* CUPS-Add-Modify-Class: Add or modify a class */
+  IPP_OP_CUPS_DELETE_CLASS,		/* CUPS-Delete-Class: Delete a class */
+  IPP_OP_CUPS_ACCEPT_JOBS,		/* CUPS-Accept-Jobs: Accept new jobs on a printer @exclude all@ */
+  IPP_OP_CUPS_REJECT_JOBS,		/* CUPS-Reject-Jobs: Reject new jobs on a printer @exclude all@ */
+  IPP_OP_CUPS_SET_DEFAULT,		/* CUPS-Set-Default: Set the default printer */
+  IPP_OP_CUPS_GET_DEVICES,		/* CUPS-Get-Devices: Get a list of supported devices @deprecated@ */
+  IPP_OP_CUPS_GET_PPDS,			/* CUPS-Get-PPDs: Get a list of supported drivers @deprecated@ */
+  IPP_OP_CUPS_MOVE_JOB,			/* CUPS-Move-Job: Move a job to a different printer */
+  IPP_OP_CUPS_AUTHENTICATE_JOB,		/* CUPS-Authenticate-Job: Authenticate a job @since CUPS 1.2/macOS 10.5@ */
+  IPP_OP_CUPS_GET_PPD,			/* CUPS-Get-PPD: Get a PPD file @deprecated@ */
+  IPP_OP_CUPS_GET_DOCUMENT = 0x4027,	/* CUPS-Get-Document: Get a document file @since CUPS 1.4/macOS 10.6@ */
+  IPP_OP_CUPS_CREATE_LOCAL_PRINTER	/* CUPS-Create-Local-Printer: Create a local (temporary) printer @since CUPS 2.2@ */
 
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_PRINT_JOB			IPP_OP_PRINT_JOB
@@ -364,7 +364,7 @@
 #    define IPP_CREATE_PRINTER_SUBSCRIPTION	IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
 #    define IPP_CREATE_JOB_SUBSCRIPTION		IPP_OP_CREATE_JOB_SUBSCRIPTIONS
 #    define IPP_OP_CREATE_PRINTER_SUBSCRIPTION	IPP_OP_CREATE_PRINTER_SUBSCRIPTIONS
-#    define IPP_OP_CREATE_JOB_SUBSCRIPTION		IPP_OP_CREATE_JOB_SUBSCRIPTIONS
+#    define IPP_OP_CREATE_JOB_SUBSCRIPTION	IPP_OP_CREATE_JOB_SUBSCRIPTIONS
 #    define IPP_GET_SUBSCRIPTION_ATTRIBUTES	IPP_OP_GET_SUBSCRIPTION_ATTRIBUTES
 #    define IPP_GET_SUBSCRIPTIONS		IPP_OP_GET_SUBSCRIPTIONS
 #    define IPP_RENEW_SUBSCRIPTION		IPP_OP_RENEW_SUBSCRIPTION
@@ -442,7 +442,7 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_orient_t;
 
-typedef enum ipp_pstate_e		/**** Printer states ****/
+typedef enum ipp_pstate_e		/**** Printer state values ****/
 {
   IPP_PSTATE_IDLE = 3,			/* Printer is idle */
   IPP_PSTATE_PROCESSING,		/* Printer is working */
@@ -455,7 +455,7 @@
 #  endif /* _CUPS_NO_DEPRECATED */
 } ipp_pstate_t;
 
-typedef enum ipp_quality_e		/**** Qualities ****/
+typedef enum ipp_quality_e		/**** Print quality values ****/
 {
   IPP_QUALITY_DRAFT = 3,		/* Draft quality */
   IPP_QUALITY_NORMAL,			/* Normal quality */
@@ -468,7 +468,7 @@
   IPP_RES_PER_CM			/* Pixels per centimeter */
 } ipp_res_t;
 
-typedef enum ipp_state_e		/**** IPP states ****/
+typedef enum ipp_state_e		/**** ipp_t state values ****/
 {
   IPP_STATE_ERROR = -1,			/* An error occurred */
   IPP_STATE_IDLE,			/* Nothing is happening/request completed */
@@ -485,7 +485,7 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_state_t;
 
-typedef enum ipp_status_e		/**** IPP status codes ****/
+typedef enum ipp_status_e		/**** IPP status code values ****/
 {
   IPP_STATUS_CUPS_INVALID = -1,		/* Invalid status name for @link ippErrorValue@ */
   IPP_STATUS_OK = 0x0000,		/* successful-ok */
@@ -498,7 +498,7 @@
   IPP_STATUS_OK_EVENTS_COMPLETE,	/* successful-ok-events-complete */
   IPP_STATUS_REDIRECTION_OTHER_SITE = 0x0200,
 					/* redirection-other-site @private@ */
-  IPP_STATUS_CUPS_SEE_OTHER = 0x0280,	/* cups-see-other */
+  IPP_STATUS_CUPS_SEE_OTHER = 0x0280,	/* cups-see-other @private@ */
   IPP_STATUS_ERROR_BAD_REQUEST = 0x0400,/* client-error-bad-request */
   IPP_STATUS_ERROR_FORBIDDEN,		/* client-error-forbidden */
   IPP_STATUS_ERROR_NOT_AUTHENTICATED,	/* client-error-not-authenticated */
@@ -574,7 +574,7 @@
   IPP_STATUS_ERROR_CUPS_AUTHENTICATION_CANCELED = 0x1000,
 					/* cups-authentication-canceled - Authentication canceled by user @since CUPS 1.5/macOS 10.7@ */
   IPP_STATUS_ERROR_CUPS_PKI,		/* cups-pki-error - Error negotiating a secure connection @since CUPS 1.5/macOS 10.7@ */
-  IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED/* cups-upgrade-required - TLS upgrade required */
+  IPP_STATUS_ERROR_CUPS_UPGRADE_REQUIRED/* cups-upgrade-required - TLS upgrade required @since CUPS 1.5/macOS 10.7@ */
 
 #  ifndef _CUPS_NO_DEPRECATED
 #    define IPP_OK				IPP_STATUS_OK
@@ -636,7 +636,7 @@
 #  endif /* _CUPS_NO_DEPRECATED */
 } ipp_status_t;
 
-typedef enum ipp_tag_e			/**** Format tags for attributes ****/
+typedef enum ipp_tag_e			/**** Value and group tag values for attributes ****/
 {
   IPP_TAG_CUPS_INVALID = -1,		/* Invalid tag name for @link ippTagValue@ */
   IPP_TAG_ZERO = 0x00,			/* Zero tag - used for separators */
@@ -648,7 +648,7 @@
   IPP_TAG_SUBSCRIPTION,			/* Subscription group */
   IPP_TAG_EVENT_NOTIFICATION,		/* Event group */
   IPP_TAG_RESOURCE,			/* Resource group @private@ */
-  IPP_TAG_DOCUMENT,			/* Document group */
+  IPP_TAG_DOCUMENT,			/* Document group @exclude all@ */
   IPP_TAG_UNSUPPORTED_VALUE = 0x10,	/* Unsupported value */
   IPP_TAG_DEFAULT,			/* Default value */
   IPP_TAG_UNKNOWN,			/* Unknown value */
@@ -663,10 +663,10 @@
   IPP_TAG_DATE,				/* Date/time value */
   IPP_TAG_RESOLUTION,			/* Resolution value */
   IPP_TAG_RANGE,			/* Range value */
-  IPP_TAG_BEGIN_COLLECTION,		/* Beginning of collection value */
+  IPP_TAG_BEGIN_COLLECTION,		/* Beginning of collection value @exclude all@ */
   IPP_TAG_TEXTLANG,			/* Text-with-language value */
   IPP_TAG_NAMELANG,			/* Name-with-language value */
-  IPP_TAG_END_COLLECTION,		/* End of collection value */
+  IPP_TAG_END_COLLECTION,		/* End of collection value @exclude all@ */
   IPP_TAG_TEXT = 0x41,			/* Text value */
   IPP_TAG_NAME,				/* Name value */
   IPP_TAG_RESERVED_STRING,		/* Reserved for future string value @private@ */
@@ -676,8 +676,8 @@
   IPP_TAG_CHARSET,			/* Character set value */
   IPP_TAG_LANGUAGE,			/* Language value */
   IPP_TAG_MIMETYPE,			/* MIME media type value */
-  IPP_TAG_MEMBERNAME,			/* Collection member name value */
-  IPP_TAG_EXTENSION = 0x7f,		/* Extension point for 32-bit tags */
+  IPP_TAG_MEMBERNAME,			/* Collection member name value @exclude all@ */
+  IPP_TAG_EXTENSION = 0x7f,		/* Extension point for 32-bit tags @exclude all@ */
   IPP_TAG_CUPS_MASK = 0x7fffffff,	/* Mask for copied attribute values @private@ */
   /* The following expression is used to avoid compiler warnings with +/-0x80000000 */
   IPP_TAG_CUPS_CONST = -0x7fffffff-1	/* Bitflag for copied/const attribute values @private@ */
@@ -688,17 +688,18 @@
 #  endif /* !_CUPS_NO_DEPRECATED */
 } ipp_tag_t;
 
-typedef unsigned char ipp_uchar_t;	/**** Unsigned 8-bit integer/character ****/
+typedef unsigned char ipp_uchar_t;	/**** Unsigned 8-bit integer/character @exclude all@ ****/
 typedef struct _ipp_s ipp_t;		/**** IPP request/response data ****/
 typedef struct _ipp_attribute_s ipp_attribute_t;
 					/**** IPP attribute ****/
 
 /**** New in CUPS 1.2/macOS 10.5 ****/
 typedef ssize_t	(*ipp_iocb_t)(void *context, ipp_uchar_t *buffer, size_t bytes);
-					/**** IPP IO Callback Function @since CUPS 1.2/macOS 10.5@ ****/
+					/**** ippReadIO/ippWriteIO callback function @since CUPS 1.2/macOS 10.5@ ****/
 
 /**** New in CUPS 1.6/macOS 10.8 ****/
 typedef int (*ipp_copycb_t)(void *context, ipp_t *dst, ipp_attribute_t *attr);
+                                        /**** ippCopyAttributes callback function @since CUPS 1.6/macOS 10.8 ****/
 
 
 /*
@@ -805,7 +806,7 @@
 } _ipp_value_t;
 typedef _ipp_value_t ipp_value_t;	/**** Convenience typedef that will be removed @private@ ****/
 
-struct _ipp_attribute_s			/**** Attribute ****/
+struct _ipp_attribute_s			/**** IPP attribute ****/
 {
   ipp_attribute_t *next;		/* Next attribute in list */
   ipp_tag_t	group_tag,		/* Job/Printer/Operation group tag */
diff --git a/cups/language-private.h b/cups/language-private.h
index 49e4b71..f447ef9 100644
--- a/cups/language-private.h
+++ b/cups/language-private.h
@@ -1,7 +1,7 @@
 /*
  * Private localization support for CUPS.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -20,6 +20,7 @@
  * Include necessary headers...
  */
 
+#  include "config.h"
 #  include <stdio.h>
 #  include <cups/transcode.h>
 #  ifdef __APPLE__
@@ -59,16 +60,11 @@
 #  endif /* __APPLE__ */
 extern void		_cupsCharmapFlush(void);
 extern const char	*_cupsEncodingName(cups_encoding_t encoding);
-extern void		_cupsLangPrintError(const char *prefix,
-			                    const char *message);
-extern int		_cupsLangPrintFilter(FILE *fp, const char *prefix,
-			                     const char *message, ...)
-			__attribute__ ((__format__ (__printf__, 3, 4)));
-extern int		_cupsLangPrintf(FILE *fp, const char *message, ...)
-			__attribute__ ((__format__ (__printf__, 2, 3)));
+extern void		_cupsLangPrintError(const char *prefix, const char *message);
+extern int		_cupsLangPrintFilter(FILE *fp, const char *prefix, const char *message, ...) _CUPS_FORMAT(3, 4);
+extern int		_cupsLangPrintf(FILE *fp, const char *message, ...) _CUPS_FORMAT(2, 3);
 extern int		_cupsLangPuts(FILE *fp, const char *message);
-extern const char	*_cupsLangString(cups_lang_t *lang,
-			                 const char *message);
+extern const char	*_cupsLangString(cups_lang_t *lang, const char *message);
 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);
diff --git a/cups/language.c b/cups/language.c
index f3a3496..61280c0 100644
--- a/cups/language.c
+++ b/cups/language.c
@@ -21,11 +21,11 @@
 #ifdef HAVE_LANGINFO_H
 #  include <langinfo.h>
 #endif /* HAVE_LANGINFO_H */
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 #ifdef HAVE_COREFOUNDATION_H
 #  include <CoreFoundation/CoreFoundation.h>
 #endif /* HAVE_COREFOUNDATION_H */
@@ -123,7 +123,9 @@
   { "nb",         "no" },
   { "nb_NO",      "no" },
   { "zh-Hans",    "zh_CN" },
+  { "zh_HANS",    "zh_CN" },
   { "zh-Hant",    "zh_TW" },
+  { "zh_HANT",    "zh_TW" },
   { "zh-Hant_CN", "zh_TW" }
 };
 #endif /* __APPLE__ */
@@ -254,8 +256,16 @@
 {
   int		i;			/* Looping var */
   CFStringRef	localeName;		/* Locale as a CF string */
+#ifdef DEBUG
+  char          temp[1024];             /* Temporary string */
 
 
+  if (!CFStringGetCString(languageName, temp, (CFIndex)sizeof(temp), kCFStringEncodingASCII))
+    temp[0] = '\0';
+
+  DEBUG_printf(("_cupsAppleLocale(languageName=%p(%s), locale=%p, localsize=%d)", (void *)languageName, temp, (void *)locale, (int)localesize));
+#endif /* DEBUG */
+
   localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, languageName);
 
   if (localeName)
@@ -267,6 +277,8 @@
     if (!CFStringGetCString(localeName, locale, (CFIndex)localesize, kCFStringEncodingASCII))
       *locale = '\0';
 
+    DEBUG_printf(("_cupsAppleLocale: locale=\"%s\"", locale));
+
     CFRelease(localeName);
 
    /*
@@ -278,8 +290,12 @@
 		   sizeof(apple_language_locale[0]));
 	 i ++)
     {
-      if (!strcmp(locale, apple_language_locale[i].language))
+      size_t len = strlen(apple_language_locale[i].language);
+
+      if (!strcmp(locale, apple_language_locale[i].language) ||
+          (!strncmp(locale, apple_language_locale[i].language, len) && (locale[len] == '_' || locale[len] == '-')))
       {
+        DEBUG_printf(("_cupsAppleLocale: Updating locale to \"%s\".", apple_language_locale[i].locale));
 	strlcpy(locale, apple_language_locale[i].locale, localesize);
 	break;
       }
@@ -296,7 +312,10 @@
   }
 
   if (!*locale)
+  {
+    DEBUG_puts("_cupsAppleLocale: Returning NULL.");
     return (NULL);
+  }
 
  /*
   * Convert language subtag into region subtag...
@@ -304,10 +323,14 @@
 
   if (locale[2] == '-')
     locale[2] = '_';
+  else if (locale[3] == '-')
+    locale[3] = '_';
 
   if (!strchr(locale, '.'))
     strlcat(locale, ".UTF-8", localesize);
 
+  DEBUG_printf(("_cupsAppleLocale: Returning \"%s\".", locale));
+
   return (locale);
 }
 #endif /* __APPLE__ */
@@ -669,6 +692,15 @@
           *ptr++ = (char)toupper(*language & 255);
 
       *ptr = '\0';
+
+     /*
+      * Map Chinese region codes to legacy country codes.
+      */
+
+      if (!strcmp(language, "zh") && !strcmp(country, "HANS"))
+        strlcpy(country, "CN", sizeof(country));
+      if (!strcmp(language, "zh") && !strcmp(country, "HANT"))
+        strlcpy(country, "TW", sizeof(country));
     }
 
     if (*language == '.' && !charset[0])
@@ -688,7 +720,7 @@
     * Force a POSIX locale for an invalid language name...
     */
 
-    if (strlen(langname) != 2)
+    if (strlen(langname) != 2 && strlen(langname) != 3)
     {
       strlcpy(langname, "C", sizeof(langname));
       country[0] = '\0';
@@ -828,6 +860,9 @@
 {
   const char *s;			/* Localized message */
 
+
+  DEBUG_printf(("_cupsLangString(lang=%p, message=\"%s\")", (void *)lang, message));
+
  /*
   * Range check input...
   */
@@ -1126,6 +1161,8 @@
 			*match;		/* Matching message */
 
 
+  DEBUG_printf(("_cupsMessageLookup(a=%p, m=\"%s\")", (void *)a, m));
+
  /*
   * Lookup the message string; if it doesn't exist in the catalog,
   * then return the message that was passed to us...
@@ -1336,11 +1373,13 @@
 {
   char			filename[1024],	/* Path to cups.strings file */
 			applelang[256],	/* Apple language ID */
-			baselang[3];	/* Base language */
+			baselang[4];	/* Base language */
   CFURLRef		url;		/* URL to cups.strings file */
   CFReadStreamRef	stream = NULL;	/* File stream */
   CFPropertyListRef	plist = NULL;	/* Localization file */
 #ifdef DEBUG
+  const char            *cups_strings = getenv("CUPS_STRINGS");
+                                        /* Test strings file */
   CFErrorRef		error = NULL;	/* Error when opening file */
 #endif /* DEBUG */
 
@@ -1351,6 +1390,15 @@
   * Load the cups.strings file...
   */
 
+#ifdef DEBUG
+  if (cups_strings)
+  {
+    DEBUG_puts("1appleMessageLoad: Using debug CUPS_STRINGS file.");
+    strlcpy(filename, cups_strings, sizeof(filename));
+  }
+  else
+#endif /* DEBUG */
+
   snprintf(filename, sizeof(filename),
            CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
 	   _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
@@ -1363,6 +1411,7 @@
     * Try with original locale string...
     */
 
+    DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
     snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
   }
 
@@ -1374,18 +1423,23 @@
     * Try with just the language code...
     */
 
+    DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+
     strlcpy(baselang, locale, sizeof(baselang));
+    if (baselang[3] == '-' || baselang[3] == '_')
+      baselang[3] = '\0';
+
     snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", baselang);
   }
 
-  DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
-
   if (access(filename, 0))
   {
    /*
     * Try alternate lproj directory names...
     */
 
+    DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+
     if (!strncmp(locale, "en", 2))
       locale = "English";
     else if (!strncmp(locale, "nb", 2))
@@ -1402,7 +1456,7 @@
       locale = "Japanese";
     else if (!strncmp(locale, "es", 2))
       locale = "Spanish";
-    else if (!strcmp(locale, "zh_HK") || !strncmp(locale, "zh-Hant", 7))
+    else if (!strcmp(locale, "zh_HK") || !strncasecmp(locale, "zh-Hant", 7) || !strncasecmp(locale, "zh_Hant", 7))
     {
      /*
       * <rdar://problem/22130168>
@@ -1423,14 +1477,18 @@
       */
 
       strlcpy(baselang, locale, sizeof(baselang));
+      if (baselang[2] == '-' || baselang[2] == '_')
+        baselang[2] = '\0';
+
       locale = baselang;
     }
 
     snprintf(filename, sizeof(filename),
 	     CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
-    DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename));
   }
 
+  DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
+
   url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
                                                 (UInt8 *)filename,
 						(CFIndex)strlen(filename), false);
diff --git a/cups/language.h b/cups/language.h
index 0a3da77..c96cbfe 100644
--- a/cups/language.h
+++ b/cups/language.h
@@ -32,7 +32,7 @@
  * Types...
  */
 
-typedef enum cups_encoding_e		/**** Language Encodings ****/
+typedef enum cups_encoding_e		/**** Language Encodings @exclude all@ ****/
 {
   CUPS_AUTO_ENCODING = -1,		/* Auto-detect the encoding @private@ */
   CUPS_US_ASCII,			/* US ASCII */
diff --git a/cups/libcups2.def b/cups/libcups2.def
index a7be679..fa4aaa3 100644
--- a/cups/libcups2.def
+++ b/cups/libcups2.def
@@ -1,5 +1,5 @@
 LIBRARY libcups2
-VERSION 2.12
+VERSION 2.13
 EXPORTS
 _cupsArrayAddStrings
 _cupsArrayNewStrings
@@ -13,6 +13,7 @@
 _cupsConvertOptions
 _cupsCreateDest
 _cupsEncodingName
+_cupsFilePeekAhead
 _cupsGet1284Values
 _cupsGetDestResource
 _cupsGetDests
@@ -24,9 +25,6 @@
 _cupsLangPrintf
 _cupsLangPuts
 _cupsLangString
-_cupsMD5Append
-_cupsMD5Finish
-_cupsMD5Init
 _cupsMessageFree
 _cupsMessageLoad
 _cupsMessageLookup
@@ -39,18 +37,6 @@
 _cupsRWLockRead
 _cupsRWLockWrite
 _cupsRWUnlock
-_cupsSNMPClose
-_cupsSNMPCopyOID
-_cupsSNMPDefaultCommunity
-_cupsSNMPIsOID
-_cupsSNMPIsOIDPrefixed
-_cupsSNMPOIDToString
-_cupsSNMPOpen
-_cupsSNMPRead
-_cupsSNMPSetDebug
-_cupsSNMPStringToOID
-_cupsSNMPWalk
-_cupsSNMPWrite
 _cupsSetDefaults
 _cupsSetError
 _cupsSetHTTPError
@@ -65,6 +51,7 @@
 _cupsStrStatistics
 _cupsThreadCancel
 _cupsThreadCreate
+_cupsThreadDetach
 _cupsThreadWait
 _cupsUserDefault
 _cups_safe_vsnprintf
@@ -81,6 +68,7 @@
 _httpEncodeURI
 _httpFreeCredentials
 _httpResolveURI
+_httpSetDigestAuthString
 _httpStatus
 _httpTLSInitialize
 _httpTLSPending
@@ -124,6 +112,7 @@
 _pwgMediaTypeForType
 _pwgPageSizeForMedia
 cupsAddDest
+cupsAddIntegerOption
 cupsAddOption
 cupsAdminCreateWindowsPPD
 cupsAdminExportSamba
@@ -227,6 +216,7 @@
 cupsGetDevices
 cupsGetFd
 cupsGetFile
+cupsGetIntegerOption
 cupsGetJobs
 cupsGetJobs2
 cupsGetNamedDest
@@ -240,6 +230,7 @@
 cupsGetResponse
 cupsGetServerPPD
 cupsHashData
+cupsHashString
 cupsLangDefault
 cupsLangEncoding
 cupsLangFlush
diff --git a/cups/md5.c b/cups/md5.c
index c0f5dd7..d5057bf 100644
--- a/cups/md5.c
+++ b/cups/md5.c
@@ -1,7 +1,7 @@
 /*
  * Private MD5 implementation for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 2005 by Easy Software Products
  * Copyright (C) 1999 Aladdin Enterprises.  All rights reserved.
  *
@@ -43,70 +43,71 @@
 #include "md5-private.h"
 #include "string-private.h"
 
-#define T1 0xd76aa478
-#define T2 0xe8c7b756
-#define T3 0x242070db
-#define T4 0xc1bdceee
-#define T5 0xf57c0faf
-#define T6 0x4787c62a
-#define T7 0xa8304613
-#define T8 0xfd469501
-#define T9 0x698098d8
-#define T10 0x8b44f7af
-#define T11 0xffff5bb1
-#define T12 0x895cd7be
-#define T13 0x6b901122
-#define T14 0xfd987193
-#define T15 0xa679438e
-#define T16 0x49b40821
-#define T17 0xf61e2562
-#define T18 0xc040b340
-#define T19 0x265e5a51
-#define T20 0xe9b6c7aa
-#define T21 0xd62f105d
-#define T22 0x02441453
-#define T23 0xd8a1e681
-#define T24 0xe7d3fbc8
-#define T25 0x21e1cde6
-#define T26 0xc33707d6
-#define T27 0xf4d50d87
-#define T28 0x455a14ed
-#define T29 0xa9e3e905
-#define T30 0xfcefa3f8
-#define T31 0x676f02d9
-#define T32 0x8d2a4c8a
-#define T33 0xfffa3942
-#define T34 0x8771f681
-#define T35 0x6d9d6122
-#define T36 0xfde5380c
-#define T37 0xa4beea44
-#define T38 0x4bdecfa9
-#define T39 0xf6bb4b60
-#define T40 0xbebfbc70
-#define T41 0x289b7ec6
-#define T42 0xeaa127fa
-#define T43 0xd4ef3085
-#define T44 0x04881d05
-#define T45 0xd9d4d039
-#define T46 0xe6db99e5
-#define T47 0x1fa27cf8
-#define T48 0xc4ac5665
-#define T49 0xf4292244
-#define T50 0x432aff97
-#define T51 0xab9423a7
-#define T52 0xfc93a039
-#define T53 0x655b59c3
-#define T54 0x8f0ccc92
-#define T55 0xffeff47d
-#define T56 0x85845dd1
-#define T57 0x6fa87e4f
-#define T58 0xfe2ce6e0
-#define T59 0xa3014314
-#define T60 0x4e0811a1
-#define T61 0xf7537e82
-#define T62 0xbd3af235
-#define T63 0x2ad7d2bb
-#define T64 0xeb86d391
+#if !defined(__APPLE__) && !defined(HAVE_GNUTLS)
+#  define T1 0xd76aa478
+#  define T2 0xe8c7b756
+#  define T3 0x242070db
+#  define T4 0xc1bdceee
+#  define T5 0xf57c0faf
+#  define T6 0x4787c62a
+#  define T7 0xa8304613
+#  define T8 0xfd469501
+#  define T9 0x698098d8
+#  define T10 0x8b44f7af
+#  define T11 0xffff5bb1
+#  define T12 0x895cd7be
+#  define T13 0x6b901122
+#  define T14 0xfd987193
+#  define T15 0xa679438e
+#  define T16 0x49b40821
+#  define T17 0xf61e2562
+#  define T18 0xc040b340
+#  define T19 0x265e5a51
+#  define T20 0xe9b6c7aa
+#  define T21 0xd62f105d
+#  define T22 0x02441453
+#  define T23 0xd8a1e681
+#  define T24 0xe7d3fbc8
+#  define T25 0x21e1cde6
+#  define T26 0xc33707d6
+#  define T27 0xf4d50d87
+#  define T28 0x455a14ed
+#  define T29 0xa9e3e905
+#  define T30 0xfcefa3f8
+#  define T31 0x676f02d9
+#  define T32 0x8d2a4c8a
+#  define T33 0xfffa3942
+#  define T34 0x8771f681
+#  define T35 0x6d9d6122
+#  define T36 0xfde5380c
+#  define T37 0xa4beea44
+#  define T38 0x4bdecfa9
+#  define T39 0xf6bb4b60
+#  define T40 0xbebfbc70
+#  define T41 0x289b7ec6
+#  define T42 0xeaa127fa
+#  define T43 0xd4ef3085
+#  define T44 0x04881d05
+#  define T45 0xd9d4d039
+#  define T46 0xe6db99e5
+#  define T47 0x1fa27cf8
+#  define T48 0xc4ac5665
+#  define T49 0xf4292244
+#  define T50 0x432aff97
+#  define T51 0xab9423a7
+#  define T52 0xfc93a039
+#  define T53 0x655b59c3
+#  define T54 0x8f0ccc92
+#  define T55 0xffeff47d
+#  define T56 0x85845dd1
+#  define T57 0x6fa87e4f
+#  define T58 0xfe2ce6e0
+#  define T59 0xa3014314
+#  define T60 0x4e0811a1
+#  define T61 0xf7537e82
+#  define T62 0xbd3af235
+#  define T63 0x2ad7d2bb
+#  define T64 0xeb86d391
 
 static void
 _cups_md5_process(_cups_md5_state_t *pms, const unsigned char *data /*[64]*/)
@@ -116,10 +117,10 @@
 	c = pms->abcd[2], d = pms->abcd[3];
     unsigned int t;
 
-#ifndef ARCH_IS_BIG_ENDIAN
-# define ARCH_IS_BIG_ENDIAN 1	/* slower, default implementation */
-#endif
-#if ARCH_IS_BIG_ENDIAN
+#  ifndef ARCH_IS_BIG_ENDIAN
+#    define ARCH_IS_BIG_ENDIAN 1	/* slower, default implementation */
+#  endif
+#  if ARCH_IS_BIG_ENDIAN
 
     /*
      * On big-endian machines, we must arrange the bytes in the right
@@ -133,7 +134,7 @@
 	X[i] = (unsigned)xp[0] + ((unsigned)xp[1] << 8) +
 	       ((unsigned)xp[2] << 16) + ((unsigned)xp[3] << 24);
 
-#else  /* !ARCH_IS_BIG_ENDIAN */
+#  else  /* !ARCH_IS_BIG_ENDIAN */
 
     /*
      * On little-endian machines, we can process properly aligned data
@@ -150,15 +151,15 @@
 	memcpy(xbuf, data, 64);
 	X = xbuf;
     }
-#endif
+#  endif
 
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+#  define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
 
     /* Round 1. */
     /* Let [abcd k s i] denote the operation
        a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
-#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
-#define SET(a, b, c, d, k, s, Ti)\
+#  define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#  define SET(a, b, c, d, k, s, Ti)\
   t = a + F(b,c,d) + X[k] + Ti;\
   a = ROTATE_LEFT(t, s) + b
     /* Do the following 16 operations. */
@@ -178,13 +179,13 @@
     SET(d, a, b, c, 13, 12, T14);
     SET(c, d, a, b, 14, 17, T15);
     SET(b, c, d, a, 15, 22, T16);
-#undef SET
+#  undef SET
 
      /* Round 2. */
      /* Let [abcd k s i] denote the operation
           a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
-#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
+#  define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#  define SET(a, b, c, d, k, s, Ti)\
   t = a + G(b,c,d) + X[k] + Ti;\
   a = ROTATE_LEFT(t, s) + b
      /* Do the following 16 operations. */
@@ -204,13 +205,13 @@
     SET(d, a, b, c,  2,  9, T30);
     SET(c, d, a, b,  7, 14, T31);
     SET(b, c, d, a, 12, 20, T32);
-#undef SET
+#  undef SET
 
      /* Round 3. */
      /* Let [abcd k s t] denote the operation
           a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define SET(a, b, c, d, k, s, Ti)\
+#  define H(x, y, z) ((x) ^ (y) ^ (z))
+#  define SET(a, b, c, d, k, s, Ti)\
   t = a + H(b,c,d) + X[k] + Ti;\
   a = ROTATE_LEFT(t, s) + b
      /* Do the following 16 operations. */
@@ -230,13 +231,13 @@
     SET(d, a, b, c, 12, 11, T46);
     SET(c, d, a, b, 15, 16, T47);
     SET(b, c, d, a,  2, 23, T48);
-#undef SET
+#  undef SET
 
      /* Round 4. */
      /* Let [abcd k s t] denote the operation
           a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
+#  define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#  define SET(a, b, c, d, k, s, Ti)\
   t = a + I(b,c,d) + X[k] + Ti;\
   a = ROTATE_LEFT(t, s) + b
      /* Do the following 16 operations. */
@@ -256,7 +257,7 @@
     SET(d, a, b, c, 11, 10, T62);
     SET(c, d, a, b,  2, 15, T63);
     SET(b, c, d, a,  9, 21, T64);
-#undef SET
+#  undef SET
 
      /* Then perform the following additions. (That is increment each
         of the four registers by the value it had before this block
@@ -337,3 +338,4 @@
     for (i = 0; i < 16; ++i)
 	digest[i] = (unsigned char)(pms->abcd[i >> 2] >> ((i & 3) << 3));
 }
+#endif /* !__APPLE__ && !HAVE_GNUTLS */
diff --git a/cups/md5passwd.c b/cups/md5passwd.c
index a9817aa..49add7d 100644
--- a/cups/md5passwd.c
+++ b/cups/md5passwd.c
@@ -1,7 +1,7 @@
 /*
- * MD5 password support for CUPS.
+ * MD5 password support for CUPS (deprecated).
  *
- * Copyright 2007-2010 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2005 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -17,12 +17,15 @@
  * Include necessary headers...
  */
 
+#include <cups/cups.h>
 #include "http-private.h"
 #include "string-private.h"
 
 
 /*
  * 'httpMD5()' - Compute the MD5 sum of the username:group:password.
+ *
+ * @deprecated@
  */
 
 char *					/* O - MD5 sum */
@@ -31,7 +34,6 @@
         const char *passwd,		/* I - Password string */
 	char       md5[33])		/* O - MD5 string */
 {
-  _cups_md5_state_t	state;		/* MD5 state info */
   unsigned char		sum[16];	/* Sum data */
   char			line[256];	/* Line to sum */
 
@@ -41,15 +43,13 @@
   */
 
   snprintf(line, sizeof(line), "%s:%s:%s", username, realm, passwd);
-  _cupsMD5Init(&state);
-  _cupsMD5Append(&state, (unsigned char *)line, (int)strlen(line));
-  _cupsMD5Finish(&state, sum);
+  cupsHashData("md5", (unsigned char *)line, strlen(line), sum, sizeof(sum));
 
  /*
   * Return the sum...
   */
 
-  return (httpMD5String(sum, md5));
+  return ((char *)cupsHashString(sum, sizeof(sum), md5, 33));
 }
 
 
@@ -57,6 +57,8 @@
  * 'httpMD5Final()' - Combine the MD5 sum of the username, group, and password
  *                    with the server-supplied nonce value, method, and
  *                    request-uri.
+ *
+ * @deprecated@
  */
 
 char *					/* O - New sum */
@@ -65,7 +67,6 @@
 	     const char *resource,	/* I - Resource path */
              char       md5[33])	/* IO - MD5 sum */
 {
-  _cups_md5_state_t	state;		/* MD5 state info */
   unsigned char		sum[16];	/* Sum data */
   char			line[1024];	/* Line of data */
   char			a2[33];		/* Hash of method and resource */
@@ -76,10 +77,8 @@
   */
 
   snprintf(line, sizeof(line), "%s:%s", method, resource);
-  _cupsMD5Init(&state);
-  _cupsMD5Append(&state, (unsigned char *)line, (int)strlen(line));
-  _cupsMD5Finish(&state, sum);
-  httpMD5String(sum, a2);
+  cupsHashData("md5", (unsigned char *)line, strlen(line), sum, sizeof(sum));
+  cupsHashString(sum, sizeof(sum), a2, sizeof(a2));
 
  /*
   * Then combine A1 (MD5 of username, realm, and password) with the nonce
@@ -88,17 +87,16 @@
   */
 
   snprintf(line, sizeof(line), "%s:%s:%s", md5, nonce, a2);
+  cupsHashData("md5", (unsigned char *)line, strlen(line), sum, sizeof(sum));
 
-  _cupsMD5Init(&state);
-  _cupsMD5Append(&state, (unsigned char *)line, (int)strlen(line));
-  _cupsMD5Finish(&state, sum);
-
-  return (httpMD5String(sum, md5));
+  return ((char *)cupsHashString(sum, sizeof(sum), md5, 33));
 }
 
 
 /*
  * 'httpMD5String()' - Convert an MD5 sum to a character string.
+ *
+ * @deprecated@
  */
 
 char *					/* O - MD5 sum in hex */
@@ -106,23 +104,5 @@
               char                md5[33])
 					/* O - MD5 sum in hex */
 {
-  int		i;			/* Looping var */
-  char		*md5ptr;		/* Pointer into MD5 string */
-  static const char hex[] = "0123456789abcdef";
-					/* Hex digits */
-
-
- /*
-  * Convert the MD5 sum to hexadecimal...
-  */
-
-  for (i = 16, md5ptr = md5; i > 0; i --, sum ++)
-  {
-    *md5ptr++ = hex[*sum >> 4];
-    *md5ptr++ = hex[*sum & 15];
-  }
-
-  *md5ptr = '\0';
-
-  return (md5);
+  return ((char *)cupsHashString(sum, 16, md5, 33));
 }
diff --git a/cups/options.c b/cups/options.c
index aa70992..db14bec 100644
--- a/cups/options.c
+++ b/cups/options.c
@@ -1,7 +1,7 @@
 /*
  * Option routines for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -30,6 +30,31 @@
 
 
 /*
+ * 'cupsAddIntegerOption()' - Add an integer option to an option array.
+ *
+ * New option arrays can be initialized simply by passing 0 for the
+ * "num_options" parameter.
+ *
+ * @since CUPS 2.2.4/macOS 10.13@
+ */
+
+int					/* O  - Number of options */
+cupsAddIntegerOption(
+    const char    *name,		/* I  - Name of option */
+    int           value,		/* I  - Value of option */
+    int           num_options,		/* I  - Number of options */
+    cups_option_t **options)		/* IO - Pointer to options */
+{
+  char	strvalue[32];			/* String value */
+
+
+  snprintf(strvalue, sizeof(strvalue), "%d", value);
+
+  return (cupsAddOption(name, strvalue, num_options, options));
+}
+
+
+/*
  * 'cupsAddOption()' - Add an option to an option array.
  *
  * New option arrays can be initialized simply by passing 0 for the
@@ -55,6 +80,11 @@
     return (num_options);
   }
 
+  if (!_cups_strcasecmp(name, "cupsPrintQuality"))
+    num_options = cupsRemoveOption("print-quality", num_options, options);
+  else if (!_cups_strcasecmp(name, "print-quality"))
+    num_options = cupsRemoveOption("cupsPrintQuality", num_options, options);
+
  /*
   * Look for an existing option with the same name...
   */
@@ -155,6 +185,38 @@
 
 
 /*
+ * 'cupsGetIntegerOption()' - Get an integer option value.
+ *
+ * INT_MIN is returned when the option does not exist, is not an integer, or
+ * exceeds the range of values for the "int" type.
+ *
+ * @since CUPS 2.2.4/macOS 10.13@
+ */
+
+int					/* O - Option value or @code INT_MIN@ */
+cupsGetIntegerOption(
+    const char    *name,		/* I - Name of option */
+    int           num_options,		/* I - Number of options */
+    cups_option_t *options)		/* I - Options */
+{
+  const char	*value = cupsGetOption(name, num_options, options);
+					/* String value of option */
+  char		*ptr;			/* Pointer into string value */
+  long		intvalue;		/* Integer value */
+
+
+  if (!value || !*value)
+    return (INT_MIN);
+
+  intvalue = strtol(value, &ptr, 10);
+  if (intvalue < INT_MIN || intvalue > INT_MAX || *ptr)
+    return (INT_MIN);
+
+  return ((int)intvalue);
+}
+
+
+/*
  * 'cupsGetOption()' - Get an option value.
  */
 
diff --git a/cups/ppd-cache.c b/cups/ppd-cache.c
index e712341..9ed946e 100644
--- a/cups/ppd-cache.c
+++ b/cups/ppd-cache.c
@@ -1,7 +1,7 @@
 /*
  * PPD cache implementation for CUPS.
  *
- * Copyright 2010-2017 by Apple Inc.
+ * Copyright © 2010-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -78,6 +78,8 @@
   int		num_finishings = 0,	/* Number of finishing values */
 		finishings[10];		/* Finishing enum values */
   ppd_choice_t	*choice;		/* Marked choice */
+  int           finishings_copies = copies;
+                                        /* Number of copies for finishings */
 
 
  /*
@@ -366,13 +368,13 @@
   {
     ippAddIntegers(request, IPP_TAG_JOB, IPP_TAG_ENUM, "finishings", num_finishings, finishings);
 
-    if (copies > 1 && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
+    if (copies != finishings_copies && (keyword = cupsGetOption("job-impressions", num_options, options)) != NULL)
     {
      /*
       * Send job-pages-per-set attribute to apply finishings correctly...
       */
 
-      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / copies);
+      ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-pages-per-set", atoi(keyword) / finishings_copies);
     }
   }
 
@@ -2084,11 +2086,16 @@
 
   DEBUG_printf(("_ppdCacheGetFinishingValues(pc=%p, num_options=%d, options=%p, max_values=%d, values=%p)", pc, num_options, options, max_values, values));
 
-  if (!pc || !pc->finishings || num_options < 1 || max_values < 1 || !values)
+  if (!pc || max_values < 1 || !values)
   {
     DEBUG_puts("_ppdCacheGetFinishingValues: Bad arguments, returning 0.");
     return (0);
   }
+  else if (!pc->finishings)
+  {
+    DEBUG_puts("_ppdCacheGetFinishingValues: No finishings support, returning 0.");
+    return (0);
+  }
 
  /*
   * Go through the finishings options and see what is set...
@@ -2114,7 +2121,7 @@
 
     if (i == 0)
     {
-      DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d.", f->value));
+      DEBUG_printf(("_ppdCacheGetFinishingValues: Adding %d (%s)", f->value, ippEnumString("finishings", f->value)));
 
       values[num_values ++] = f->value;
 
@@ -2123,6 +2130,17 @@
     }
   }
 
+  if (num_values == 0)
+  {
+   /*
+    * Always have at least "finishings" = 'none'...
+    */
+
+    DEBUG_puts("_ppdCacheGetFinishingValues: Adding 3 (none).");
+    values[0] = IPP_FINISHINGS_NONE;
+    num_values ++;
+  }
+
   DEBUG_printf(("_ppdCacheGetFinishingValues: Returning %d.", num_values));
 
   return (num_values);
@@ -2949,6 +2967,8 @@
 			is_pwg = 0;	/* Does the printer support PWG Raster? */
   pwg_media_t		*pwg;		/* PWG media size */
   int			xres, yres;	/* Resolution values */
+  int                   resolutions[1000];
+                                        /* Array of resolution indices */
   cups_lang_t		*lang = cupsLangDefault();
 					/* Localization info */
   struct lconv		*loc = localeconv();
@@ -3085,8 +3105,8 @@
   cupsFilePrintf(fp, "*Manufacturer: \"%s\"\n", make);
   cupsFilePrintf(fp, "*ModelName: \"%s\"\n", model);
   cupsFilePrintf(fp, "*Product: \"(%s)\"\n", model);
-  cupsFilePrintf(fp, "*NickName: \"%s\"\n", model);
-  cupsFilePrintf(fp, "*ShortNickName: \"%s\"\n", model);
+  cupsFilePrintf(fp, "*NickName: \"%s - IPP Everywhere\"\n", model);
+  cupsFilePrintf(fp, "*ShortNickName: \"%s - IPP Everywhere\"\n", model);
 
   if ((attr = ippFindAttribute(response, "color-supported", IPP_TAG_BOOLEAN)) != NULL && ippGetBoolean(attr, 0))
     cupsFilePuts(fp, "*ColorDevice: True\n");
@@ -3098,6 +3118,41 @@
   cupsFilePuts(fp, "*cupsLanguages: \"en\"\n");
 
  /*
+  * Password/PIN printing...
+  */
+
+  if ((attr = ippFindAttribute(response, "job-password-supported", IPP_TAG_INTEGER)) != NULL)
+  {
+    char	pattern[33];		/* Password pattern */
+    int		maxlen = ippGetInteger(attr, 0);
+					/* Maximum length */
+    const char	*repertoire = ippGetString(ippFindAttribute(response, "job-password-repertoire-configured", IPP_TAG_KEYWORD), 0, NULL);
+					/* Type of password */
+
+    if (maxlen > (int)(sizeof(pattern) - 1))
+      maxlen = (int)sizeof(pattern) - 1;
+
+    if (!repertoire || !strcmp(repertoire, "iana_us-ascii_digits"))
+      memset(pattern, '1', (size_t)maxlen);
+    else if (!strcmp(repertoire, "iana_us-ascii_letters"))
+      memset(pattern, 'A', (size_t)maxlen);
+    else if (!strcmp(repertoire, "iana_us-ascii_complex"))
+      memset(pattern, 'C', (size_t)maxlen);
+    else if (!strcmp(repertoire, "iana_us-ascii_any"))
+      memset(pattern, '.', (size_t)maxlen);
+    else if (!strcmp(repertoire, "iana_utf-8_digits"))
+      memset(pattern, 'N', (size_t)maxlen);
+    else if (!strcmp(repertoire, "iana_utf-8_letters"))
+      memset(pattern, 'U', (size_t)maxlen);
+    else
+      memset(pattern, '*', (size_t)maxlen);
+
+    pattern[maxlen] = '\0';
+
+    cupsFilePrintf(fp, "*cupsPassword: \"%s\"\n", pattern);
+  }
+
+ /*
   * Filters...
   */
 
@@ -3105,24 +3160,28 @@
   {
     is_apple = ippContainsString(attr, "image/urf");
     is_pdf   = ippContainsString(attr, "application/pdf");
-    is_pwg   = ippContainsString(attr, "image/pwg-raster");
+    is_pwg   = ippContainsString(attr, "image/pwg-raster") && !is_apple;
 
-    for (i = 0, count = ippGetCount(attr); i < count; i ++)
+    if (ippContainsString(attr, "image/jpeg"))
+      cupsFilePuts(fp, "*cupsFilter2: \"image/jpeg image/jpeg 0 -\"\n");
+    if (ippContainsString(attr, "image/png"))
+      cupsFilePuts(fp, "*cupsFilter2: \"image/png image/png 0 -\"\n");
+    if (is_pdf)
     {
-      const char *format = ippGetString(attr, i, NULL);
-					/* PDL */
-
      /*
-      * Write cupsFilter2 lines for supported formats...
+      * Don't locally filter PDF content when printing to a CUPS shared
+      * printer, otherwise the options will be applied twice...
       */
 
-      if (!_cups_strcasecmp(format, "application/pdf"))
+      if (ippContainsString(attr, "application/vnd.cups-pdf"))
+        cupsFilePuts(fp, "*cupsFilter2: \"application/pdf application/pdf 0 -\"\n");
+      else
         cupsFilePuts(fp, "*cupsFilter2: \"application/vnd.cups-pdf application/pdf 10 -\"\n");
-      else if (!_cups_strcasecmp(format, "image/jpeg") || !_cups_strcasecmp(format, "image/png"))
-        cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 0 -\"\n", format, format);
-      else if (!_cups_strcasecmp(format, "image/pwg-raster") || !_cups_strcasecmp(format, "image/urf"))
-        cupsFilePrintf(fp, "*cupsFilter2: \"%s %s 100 -\"\n", format, format);
     }
+    if (is_apple)
+      cupsFilePuts(fp, "*cupsFilter2: \"image/urf image/urf 100 -\"\n");
+    if (is_pwg)
+      cupsFilePuts(fp, "*cupsFilter2: \"image/pwg-raster image/pwg-raster 100 -\"\n");
   }
 
   if (!is_apple && !is_pdf && !is_pwg)
@@ -3394,7 +3453,8 @@
       for (j = 0; j < (int)(sizeof(sources) / sizeof(sources[0])); j ++)
         if (!strcmp(sources[j][0], ppdname))
 	{
-	  cupsFilePrintf(fp, "*InputSlot %s/%s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, _cupsLangString(lang, sources[j][1]), j);
+	  cupsFilePrintf(fp, "*InputSlot %s: \"<</MediaPosition %d>>setpagedevice\"\n", ppdname, j);
+	  cupsFilePrintf(fp, "*%s.InputSlot %s/%s: \"\"\n", lang->language, ppdname, _cupsLangString(lang, sources[j][1]));
 	  break;
 	}
     }
@@ -3570,9 +3630,14 @@
           break;
 
       if (j < (int)(sizeof(media_types) / sizeof(media_types[0])))
-        cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, _cupsLangString(lang, media_types[j][1]), ppdname);
+      {
+        cupsFilePrintf(fp, "*MediaType %s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, ppdname);
+        cupsFilePrintf(fp, "*%s.MediaType %s/%s: \"\"\n", lang->language, ppdname, _cupsLangString(lang, media_types[j][1]));
+      }
       else
+      {
         cupsFilePrintf(fp, "*MediaType %s/%s: \"<</MediaType(%s)>>setpagedevice\"\n", ppdname, keyword, ppdname);
+      }
     }
     cupsFilePuts(fp, "*CloseUI: *MediaType\n");
   }
@@ -3581,8 +3646,8 @@
   * ColorModel...
   */
 
-  if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
-    if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
+  if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) == NULL)
+    if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) == NULL)
       if ((attr = ippFindAttribute(response, "print-color-mode-supported", IPP_TAG_KEYWORD)) == NULL)
         attr = ippFindAttribute(response, "output-mode-supported", IPP_TAG_KEYWORD);
 
@@ -3595,45 +3660,49 @@
       const char *keyword = ippGetString(attr, i, NULL);
 					/* Keyword for color/bit depth */
 
-      if (!strcmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
+      if (!strcasecmp(keyword, "black_1") || !strcmp(keyword, "bi-level") || !strcmp(keyword, "process-bi-level"))
       {
         if (!default_color)
-	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
-			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
+	  cupsFilePrintf(fp, "*OpenUI *ColorModel: PickOne\n"
+			     "*OrderDependency: 10 AnySetup *ColorModel\n"
+			     "*%s.Translation ColorModel/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Color Mode")));
 
-        cupsFilePrintf(fp, "*ColorModel FastGray/%s: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Fast Grayscale")));
+        cupsFilePrintf(fp, "*ColorModel FastGray: \"<</cupsColorSpace 3/cupsBitsPerColor 1/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n*%s.ColorModel FastGray/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Fast Grayscale")));
 
         if (!default_color)
 	  default_color = "FastGray";
       }
-      else if (!strcmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
+      else if (!strcasecmp(keyword, "sgray_8") || !strcmp(keyword, "W8") || !strcmp(keyword, "monochrome") || !strcmp(keyword, "process-monochrome"))
       {
         if (!default_color)
-	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
-			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
+	  cupsFilePrintf(fp, "*OpenUI *ColorModel: PickOne\n"
+			     "*OrderDependency: 10 AnySetup *ColorModel\n"
+			     "*%s.Translation ColorModel/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Color Mode")));
 
-        cupsFilePrintf(fp, "*ColorModel Gray/%s: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Grayscale")));
+        cupsFilePrintf(fp, "*ColorModel Gray: \"<</cupsColorSpace 18/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n*%s.ColorModel Gray/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Grayscale")));
 
         if (!default_color || !strcmp(default_color, "FastGray"))
 	  default_color = "Gray";
       }
-      else if (!strcmp(keyword, "srgb_8") || !strcmp(keyword, "SRGB24") || !strcmp(keyword, "color"))
+      else if (!strcasecmp(keyword, "srgb_8") || !strcmp(keyword, "SRGB24") || !strcmp(keyword, "color"))
       {
         if (!default_color)
-	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
-			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
+	  cupsFilePrintf(fp, "*OpenUI *ColorModel: PickOne\n"
+			     "*OrderDependency: 10 AnySetup *ColorModel\n"
+			     "*%s.Translation ColorModel/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Color Mode")));
 
-        cupsFilePrintf(fp, "*ColorModel RGB/%s: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Color")));
+        cupsFilePrintf(fp, "*ColorModel RGB: \"<</cupsColorSpace 19/cupsBitsPerColor 8/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n*%s.ColorModel RGB/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Color")));
 
 	default_color = "RGB";
       }
-      else if (!strcmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48"))
+      else if (!strcasecmp(keyword, "adobe-rgb_16") || !strcmp(keyword, "ADOBERGB48"))
       {
         if (!default_color)
-	  cupsFilePrintf(fp, "*OpenUI *ColorModel/%s: PickOne\n"
-			     "*OrderDependency: 10 AnySetup *ColorModel\n", _cupsLangString(lang, _("Color Mode")));
+	  cupsFilePrintf(fp, "*OpenUI *ColorModel: PickOne\n"
+			     "*OrderDependency: 10 AnySetup *ColorModel\n"
+			     "*%s.Translation ColorModel/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Color Mode")));
 
-        cupsFilePrintf(fp, "*ColorModel AdobeRGB/%s: \"<</cupsColorSpace 20/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n", _cupsLangString(lang, _("Deep Color")));
+        cupsFilePrintf(fp, "*ColorModel AdobeRGB: \"<</cupsColorSpace 20/cupsBitsPerColor 16/cupsColorOrder 0/cupsCompression 0>>setpagedevice\"\n*%s.ColorModel AdobeRGB/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Deep Color")));
 
         if (!default_color)
 	  default_color = "AdobeRGB";
@@ -3653,15 +3722,48 @@
 
   if ((attr = ippFindAttribute(response, "sides-supported", IPP_TAG_KEYWORD)) != NULL && ippContainsString(attr, "two-sided-long-edge"))
   {
-    cupsFilePrintf(fp, "*OpenUI *Duplex/%s: PickOne\n"
+    cupsFilePrintf(fp, "*OpenUI *Duplex: PickOne\n"
 		       "*OrderDependency: 10 AnySetup *Duplex\n"
+		       "*%s.Translation Duplex/%s: \"\"\n"
 		       "*DefaultDuplex: None\n"
-		       "*Duplex None/%s: \"<</Duplex false>>setpagedevice\"\n"
-		       "*Duplex DuplexNoTumble/%s: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
-		       "*Duplex DuplexTumble/%s: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
-		       "*CloseUI: *Duplex\n", _cupsLangString(lang, _("2-Sided Printing")), _cupsLangString(lang, _("Off (1-Sided)")), _cupsLangString(lang, _("Long-Edge (Portrait)")), _cupsLangString(lang, _("Short-Edge (Landscape)")));
+		       "*Duplex None: \"<</Duplex false>>setpagedevice\"\n"
+		       "*%s.Duplex None/%s: \"\"\n"
+		       "*Duplex DuplexNoTumble: \"<</Duplex true/Tumble false>>setpagedevice\"\n"
+		       "*%s.Duplex DuplexNoTumble/%s: \"\"\n"
+		       "*Duplex DuplexTumble: \"<</Duplex true/Tumble true>>setpagedevice\"\n"
+		       "*%s.Duplex DuplexTumble/%s: \"\"\n"
+		       "*CloseUI: *Duplex\n", lang->language, _cupsLangString(lang, _("2-Sided Printing")), lang->language, _cupsLangString(lang, _("Off (1-Sided)")), lang->language, _cupsLangString(lang, _("Long-Edge (Portrait)")), lang->language, _cupsLangString(lang, _("Short-Edge (Landscape)")));
 
-    if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
+    if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
+    {
+      for (i = 0, count = ippGetCount(attr); i < count; i ++)
+      {
+        const char *dm = ippGetString(attr, i, NULL);
+                                        /* DM value */
+
+        if (!_cups_strcasecmp(dm, "DM1"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: Normal\n");
+          break;
+        }
+        else if (!_cups_strcasecmp(dm, "DM2"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
+          break;
+        }
+        else if (!_cups_strcasecmp(dm, "DM3"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
+          break;
+        }
+        else if (!_cups_strcasecmp(dm, "DM4"))
+        {
+          cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
+          break;
+        }
+      }
+    }
+    else if ((attr = ippFindAttribute(response, "pwg-raster-document-sheet-back", IPP_TAG_KEYWORD)) != NULL)
     {
       const char *keyword = ippGetString(attr, 0, NULL);
 					/* Keyword value */
@@ -3675,35 +3777,6 @@
       else
         cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
     }
-    else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
-    {
-      for (i = 0, count = ippGetCount(attr); i < count; i ++)
-      {
-	const char *dm = ippGetString(attr, i, NULL);
-					  /* DM value */
-
-	if (!_cups_strcasecmp(dm, "DM1"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: Normal\n");
-	  break;
-	}
-	else if (!_cups_strcasecmp(dm, "DM2"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: Flipped\n");
-	  break;
-	}
-	else if (!_cups_strcasecmp(dm, "DM3"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: Rotated\n");
-	  break;
-	}
-	else if (!_cups_strcasecmp(dm, "DM4"))
-	{
-	  cupsFilePuts(fp, "*cupsBackSide: ManualTumble\n");
-	  break;
-	}
-      }
-    }
   }
 
  /*
@@ -3717,6 +3790,12 @@
 
   if ((attr = ippFindAttribute(response, "output-bin-supported", IPP_TAG_ZERO)) != NULL && (count = ippGetCount(attr)) > 1)
   {
+    ipp_attribute_t	*trays = ippFindAttribute(response, "printer-output-tray", IPP_TAG_STRING);
+					/* printer-output-tray attribute, if any */
+    const char		*tray_ptr;	/* printer-output-tray value */
+    int			tray_len;	/* Len of printer-output-tray value */
+    char		tray[IPP_MAX_OCTETSTRING];
+					/* printer-output-tray string value */
     static const char * const output_bins[][2] =
     {					/* "output-bin" strings */
       { "auto", _("Automatic") },
@@ -3767,6 +3846,11 @@
     cupsFilePrintf(fp, "*OpenUI *OutputBin: PickOne\n"
                        "*OrderDependency: 10 AnySetup *OutputBin\n"
                        "*DefaultOutputBin: %s\n", ppdname);
+    if (!strcmp(ppdname, "FaceUp"))
+      cupsFilePuts(fp, "*DefaultOutputOrder: Reverse\n");
+    else
+      cupsFilePuts(fp, "*DefaultOutputOrder: Normal\n");
+
     for (i = 0; i < (int)(sizeof(output_bins) / sizeof(output_bins[0])); i ++)
     {
       if (!ippContainsString(attr, output_bins[i][0]))
@@ -3774,7 +3858,26 @@
 
       pwg_ppdize_name(output_bins[i][0], ppdname, sizeof(ppdname));
 
-      cupsFilePrintf(fp, "*OutputBin %s/%s: \"\"\n", ppdname, _cupsLangString(lang, output_bins[i][1]));
+      cupsFilePrintf(fp, "*OutputBin %s: \"\"\n", ppdname);
+      cupsFilePrintf(fp, "*%s.OutputBin %s/%s: \"\"\n", lang->language, ppdname, _cupsLangString(lang, output_bins[i][1]));
+
+      if ((tray_ptr = ippGetOctetString(trays, i, &tray_len)) != NULL)
+      {
+        if (tray_len >= (int)sizeof(tray))
+          tray_len = (int)sizeof(tray) - 1;
+
+        memcpy(tray, tray_ptr, (size_t)tray_len);
+        tray[tray_len] = '\0';
+
+        if (strstr(tray, "stackingorder=lastToFirst;"))
+          cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
+        else
+          cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
+      }
+      else if (!strcmp(ppdname, "FaceUp"))
+	cupsFilePrintf(fp, "*PageStackOrder %s: Reverse\n", ppdname);
+      else
+	cupsFilePrintf(fp, "*PageStackOrder %s: Normal\n", ppdname);
     }
     cupsFilePuts(fp, "*CloseUI: *OutputBin\n");
   }
@@ -3812,10 +3915,12 @@
 
     if (i < count)
     {
-      cupsFilePrintf(fp, "*OpenUI *StapleLocation/%s: PickOne\n", _cupsLangString(lang, _("Staple")));
+      cupsFilePuts(fp, "*OpenUI *StapleLocation: PickOne\n");
       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *StapleLocation\n");
+      cupsFilePrintf(fp, "*%s.Translation StapleLocation/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Staple")));
       cupsFilePuts(fp, "*DefaultStapleLocation: None\n");
-      cupsFilePrintf(fp, "*StapleLocation None/%s: \"\"\n", _cupsLangString(lang, _("None")));
+      cupsFilePuts(fp, "*StapleLocation None: \"\"\n");
+      cupsFilePrintf(fp, "*%s.StapleLocation None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
 
       for (; i < count; i ++)
       {
@@ -3834,7 +3939,8 @@
         {
           if (!strcmp(finishings[j][0], name))
           {
-            cupsFilePrintf(fp, "*StapleLocation %s/%s: \"\"\n", name, _cupsLangString(lang, finishings[j][1]));
+            cupsFilePrintf(fp, "*StapleLocation %s: \"\"\n", name);
+            cupsFilePrintf(fp, "*%s.StapleLocation %s/%s: \"\"\n", lang->language, name, _cupsLangString(lang, finishings[j][1]));
             cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*StapleLocation %s\"\n", value, name, name);
             break;
           }
@@ -3859,10 +3965,12 @@
 
     if (i < count)
     {
-      cupsFilePrintf(fp, "*OpenUI *FoldType/%s: PickOne\n", _cupsLangString(lang, _("Fold")));
+      cupsFilePuts(fp, "*OpenUI *FoldType: PickOne\n");
       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *FoldType\n");
+      cupsFilePrintf(fp, "*%s.Translation FoldType/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Fold")));
       cupsFilePuts(fp, "*DefaultFoldType: None\n");
-      cupsFilePrintf(fp, "*FoldType None/%s: \"\"\n", _cupsLangString(lang, _("None")));
+      cupsFilePuts(fp, "*FoldType None: \"\"\n");
+      cupsFilePrintf(fp, "*%s.FoldType None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
 
       for (; i < count; i ++)
       {
@@ -3881,7 +3989,8 @@
         {
           if (!strcmp(finishings[j][0], name))
           {
-            cupsFilePrintf(fp, "*FoldType %s/%s: \"\"\n", name, _cupsLangString(lang, finishings[j][1]));
+            cupsFilePrintf(fp, "*FoldType %s: \"\"\n", name);
+            cupsFilePrintf(fp, "*%s.FoldType %s/%s: \"\"\n", lang->language, name, _cupsLangString(lang, finishings[j][1]));
             cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*FoldType %s\"\n", value, name, name);
             break;
           }
@@ -3906,10 +4015,12 @@
 
     if (i < count)
     {
-      cupsFilePrintf(fp, "*OpenUI *PunchMedia/%s: PickOne\n", _cupsLangString(lang, _("Punch")));
+      cupsFilePuts(fp, "*OpenUI *PunchMedia: PickOne\n");
       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *PunchMedia\n");
+      cupsFilePrintf(fp, "*%s.Translation PunchMedia/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Punch")));
       cupsFilePuts(fp, "*DefaultPunchMedia: None\n");
-      cupsFilePrintf(fp, "*PunchMedia None/%s: \"\"\n", _cupsLangString(lang, _("None")));
+      cupsFilePuts(fp, "*PunchMedia None: \"\"\n");
+      cupsFilePrintf(fp, "*%s.PunchMedia None/%s: \"\"\n", lang->language, _cupsLangString(lang, _("None")));
 
       for (i = 0; i < count; i ++)
       {
@@ -3928,7 +4039,8 @@
         {
           if (!strcmp(finishings[j][0], name))
           {
-            cupsFilePrintf(fp, "*PunchMedia %s/%s: \"\"\n", name, _cupsLangString(lang, finishings[j][1]));
+            cupsFilePrintf(fp, "*PunchMedia %s: \"\"\n", name);
+            cupsFilePrintf(fp, "*%s.PunchMedia %s/%s: \"\"\n", lang->language, name, _cupsLangString(lang, finishings[j][1]));
             cupsFilePrintf(fp, "*cupsIPPFinishings %d/%s: \"*PunchMedia %s\"\n", value, name, name);
             break;
           }
@@ -3944,8 +4056,9 @@
 
     if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER))
     {
-      cupsFilePrintf(fp, "*OpenUI *Booklet/%s: Boolean\n", _cupsLangString(lang, _("Booklet")));
+      cupsFilePuts(fp, "*OpenUI *Booklet: Boolean\n");
       cupsFilePuts(fp, "*OrderDependency: 10 AnySetup *Booklet\n");
+      cupsFilePrintf(fp, "*%s.Translation Booklet/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Booklet")));
       cupsFilePuts(fp, "*DefaultBooklet: False\n");
       cupsFilePuts(fp, "*Booklet False: \"\"\n");
       cupsFilePuts(fp, "*Booklet True: \"\"\n");
@@ -3962,42 +4075,14 @@
 
   quality = ippFindAttribute(response, "print-quality-supported", IPP_TAG_ENUM);
 
-  if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+  if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
   {
-    count = ippGetCount(attr);
-
-    pwg_ppdize_resolution(attr, count / 2, &xres, &yres, ppdname, sizeof(ppdname));
-    cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
-
-    cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
-		       "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
-		       "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
-    if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
-    {
-      pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
-      cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
-    }
-    pwg_ppdize_resolution(attr, count / 2, &xres, &yres, NULL, 0);
-    cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
-    if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
-    {
-      if (count > 1)
-        pwg_ppdize_resolution(attr, count - 1, &xres, &yres, NULL, 0);
-      else
-        pwg_ppdize_resolution(attr, 0, &xres, &yres, NULL, 0);
-      cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
-    }
-
-    cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
-  }
-  else if ((attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
-  {
-    int lowdpi = 0, hidpi = 0;		/* Lower and higher resolution */
+    int lowdpi = 0, hidpi = 0;    /* Lower and higher resolution */
 
     for (i = 0, count = ippGetCount(attr); i < count; i ++)
     {
       const char *rs = ippGetString(attr, i, NULL);
-					/* RS value */
+          /* RS value */
 
       if (_cups_strncasecmp(rs, "RS", 2))
         continue;
@@ -4026,19 +4111,92 @@
 
       cupsFilePrintf(fp, "*DefaultResolution: %ddpi\n", lowdpi);
 
-      cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
+      cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
 			 "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
-			 "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
+			 "*%s.Translation cupsPrintQuality/%s: \"\"\n"
+			 "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
       if ((lowdpi & 1) == 0)
-	cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi / 2);
+	cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi / 2, lang->language, _cupsLangString(lang, _("Draft")));
       else if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
-	cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), lowdpi, lowdpi);
-      cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), lowdpi, lowdpi);
+	cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Draft")));
+
+      cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", lowdpi, lowdpi, lang->language, _cupsLangString(lang, _("Normal")));
+
       if (hidpi > lowdpi || ippContainsInteger(quality, IPP_QUALITY_HIGH))
-	cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), hidpi, hidpi);
+  cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", hidpi, hidpi, lang->language, _cupsLangString(lang, _("High")));
       cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
     }
   }
+  else if ((attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+  {
+   /*
+    * Make a sorted list of resolutions.
+    */
+
+    count = ippGetCount(attr);
+    if (count > (int)(sizeof(resolutions) / sizeof(resolutions[0])))
+      count = (int)(sizeof(resolutions) / sizeof(resolutions[0]));
+
+    resolutions[0] = 0; /* Not in loop to silence Clang static analyzer... */
+    for (i = 1; i < count; i ++)
+      resolutions[i] = i;
+
+    for (i = 0; i < (count - 1); i ++)
+    {
+      for (j = i + 1; j < count; j ++)
+      {
+        int       ix, iy,               /* First X and Y resolution */
+                  jx, jy,               /* Second X and Y resolution */
+                  temp;                 /* Swap variable */
+        ipp_res_t units;                /* Resolution units */
+
+        ix = ippGetResolution(attr, resolutions[i], &iy, &units);
+        jx = ippGetResolution(attr, resolutions[j], &jy, &units);
+
+        if (ix > jx || (ix == jx && iy > jy))
+        {
+         /*
+          * Swap these two resolutions...
+          */
+
+          temp           = resolutions[i];
+          resolutions[i] = resolutions[j];
+          resolutions[j] = temp;
+        }
+      }
+    }
+
+   /*
+    * Generate print quality options...
+    */
+
+    pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, ppdname, sizeof(ppdname));
+    cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
+
+    cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
+		       "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
+		       "*%s.Translation cupsPrintQuality/%s: \"\"\n"
+		       "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
+    if (count > 2 || ippContainsInteger(quality, IPP_QUALITY_DRAFT))
+    {
+      pwg_ppdize_resolution(attr, resolutions[0], &xres, &yres, NULL, 0);
+      cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
+      cupsFilePrintf(fp, "*%s.cupsPrintQuality Draft/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Draft")));
+    }
+
+    pwg_ppdize_resolution(attr, resolutions[count / 2], &xres, &yres, NULL, 0);
+    cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
+    cupsFilePrintf(fp, "*%s.cupsPrintQuality Normal/%s: \"\"\n", lang->language, _cupsLangString(lang, _("Normal")));
+
+    if (count > 1 || ippContainsInteger(quality, IPP_QUALITY_HIGH))
+    {
+      pwg_ppdize_resolution(attr, resolutions[count - 1], &xres, &yres, NULL, 0);
+      cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n", xres, yres);
+      cupsFilePrintf(fp, "*%s.cupsPrintQuality High/%s: \"\"\n", lang->language, _cupsLangString(lang, _("High")));
+    }
+
+    cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
+  }
   else if (is_apple || is_pwg)
     goto bad_ppd;
   else
@@ -4055,14 +4213,17 @@
 
     cupsFilePrintf(fp, "*DefaultResolution: %s\n", ppdname);
 
-    cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality/%s: PickOne\n"
+    cupsFilePrintf(fp, "*OpenUI *cupsPrintQuality: PickOne\n"
                        "*OrderDependency: 10 AnySetup *cupsPrintQuality\n"
-                       "*DefaultcupsPrintQuality: Normal\n", _cupsLangString(lang, _("Print Quality")));
+                       "*%s.Translation cupsPrintQuality/%s: \"\"\n"
+                       "*DefaultcupsPrintQuality: Normal\n", lang->language, _cupsLangString(lang, _("Print Quality")));
     if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
-      cupsFilePrintf(fp, "*cupsPrintQuality Draft/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Draft")), xres, yres);
-    cupsFilePrintf(fp, "*cupsPrintQuality Normal/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("Normal")), xres, yres);
+      cupsFilePrintf(fp, "*cupsPrintQuality Draft: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Draft/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Draft")));
+
+    cupsFilePrintf(fp, "*cupsPrintQuality Normal: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality Normal/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("Normal")));
+
     if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
-      cupsFilePrintf(fp, "*cupsPrintQuality High/%s: \"<</HWResolution[%d %d]>>setpagedevice\"\n", _cupsLangString(lang, _("High")), xres, yres);
+      cupsFilePrintf(fp, "*cupsPrintQuality High: \"<</HWResolution[%d %d]>>setpagedevice\"\n*%s.cupsPrintQuality High/%s: \"\"\n", xres, yres, lang->language, _cupsLangString(lang, _("High")));
     cupsFilePuts(fp, "*CloseUI: *cupsPrintQuality\n");
   }
 
diff --git a/cups/ppd-conflicts.c b/cups/ppd-conflicts.c
index 8f875a5..24330ec 100644
--- a/cups/ppd-conflicts.c
+++ b/cups/ppd-conflicts.c
@@ -1,7 +1,7 @@
 /*
  * Option conflict management routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -29,7 +29,6 @@
 
 enum
 {
-  _PPD_NORMAL_CONSTRAINTS,
   _PPD_OPTION_CONSTRAINTS,
   _PPD_INSTALLABLE_CONSTRAINTS,
   _PPD_ALL_CONSTRAINTS
@@ -998,7 +997,7 @@
     if (!consts->installable && which == _PPD_INSTALLABLE_CONSTRAINTS)
       continue;				/* Skip non-installable option constraint */
 
-    if (which == _PPD_OPTION_CONSTRAINTS && option)
+    if ((which == _PPD_OPTION_CONSTRAINTS || which == _PPD_INSTALLABLE_CONSTRAINTS) && option)
     {
      /*
       * Skip constraints that do not involve the current option...
diff --git a/cups/ppd-emit.c b/cups/ppd-emit.c
index 36e5bca..4b153ac 100644
--- a/cups/ppd-emit.c
+++ b/cups/ppd-emit.c
@@ -21,11 +21,11 @@
 
 #include "cups-private.h"
 #include "ppd.h"
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 
 
 /*
@@ -328,11 +328,11 @@
 
     while (buflength > 0)
     {
-#ifdef WIN32
+#ifdef _WIN32
       if ((bytes = (ssize_t)write(fd, bufptr, (unsigned)buflength)) < 0)
 #else
       if ((bytes = write(fd, bufptr, buflength)) < 0)
-#endif /* WIN32 */
+#endif /* _WIN32 */
       {
         if (errno == EAGAIN || errno == EINTR)
 	  continue;
diff --git a/cups/ppd-localize.c b/cups/ppd-localize.c
index ed75bf8..9537ef5 100644
--- a/cups/ppd-localize.c
+++ b/cups/ppd-localize.c
@@ -1,7 +1,7 @@
 /*
  * PPD localization routines for CUPS.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -277,7 +277,7 @@
 
   if (!locattr)
   {
-    if (lang && (!scheme || !strcmp(scheme, "text")))
+    if (lang && (!scheme || !strcmp(scheme, "text")) && strcmp(reason, "none"))
     {
      /*
       * Try to localize a standard printer-state-reason keyword...
diff --git a/cups/ppd-mark.c b/cups/ppd-mark.c
index 9fdaf0b..464c09a 100644
--- a/cups/ppd-mark.c
+++ b/cups/ppd-mark.c
@@ -1,7 +1,7 @@
 /*
  * Option marking routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -253,6 +253,7 @@
   */
 
   for (i = num_options, optptr = options; i > 0; i --, optptr ++)
+  {
     if (!_cups_strcasecmp(optptr->name, "media") ||
         !_cups_strcasecmp(optptr->name, "output-bin") ||
 	!_cups_strcasecmp(optptr->name, "output-mode") ||
@@ -341,6 +342,19 @@
       ppd_mark_option(ppd, "MirrorPrint", optptr->value);
     else
       ppd_mark_option(ppd, optptr->name, optptr->value);
+  }
+
+  if (print_quality)
+  {
+    int pq = atoi(print_quality);       /* print-quaity value */
+
+    if (pq == IPP_QUALITY_DRAFT)
+      ppd_mark_option(ppd, "cupsPrintQuality", "Draft");
+    else if (pq == IPP_QUALITY_HIGH)
+      ppd_mark_option(ppd, "cupsPrintQuality", "High");
+    else
+      ppd_mark_option(ppd, "cupsPrintQuality", "Normal");
+  }
 
   ppd_debug_marked(ppd, "After...");
 
diff --git a/cups/ppd-util.c b/cups/ppd-util.c
index d0194c8..0974bae 100644
--- a/cups/ppd-util.c
+++ b/cups/ppd-util.c
@@ -1,8 +1,8 @@
 /*
  * PPD utilities for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
- * Copyright 1997-2006 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -21,11 +21,11 @@
 #include "ppd-private.h"
 #include <fcntl.h>
 #include <sys/stat.h>
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 
 
 /*
@@ -171,7 +171,7 @@
     return (HTTP_STATUS_NOT_ACCEPTABLE);
   }
 
-#ifndef WIN32
+#ifndef _WIN32
  /*
   * See if the PPD file is available locally...
   */
@@ -216,6 +216,27 @@
         const char	*tmpdir;	/* TMPDIR environment variable */
 	struct timeval	curtime;	/* Current time */
 
+#ifdef __APPLE__
+       /*
+	* On macOS and iOS, the TMPDIR environment variable is not always the
+	* best location to place temporary files due to sandboxing.  Instead,
+	* the confstr function should be called to get the proper per-user,
+	* per-process TMPDIR value.
+	*/
+
+        char		tmppath[1024];	/* Temporary directory */
+
+	if ((tmpdir = getenv("TMPDIR")) != NULL && access(tmpdir, W_OK))
+	  tmpdir = NULL;
+
+	if (!tmpdir)
+	{
+	  if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmppath, sizeof(tmppath)))
+	    tmpdir = tmppath;
+	  else
+	    tmpdir = "/private/tmp";		/* This should never happen */
+	}
+#else
        /*
 	* Previously we put root temporary files in the default CUPS temporary
 	* directory under /var/spool/cups.  However, since the scheduler cleans
@@ -224,11 +245,8 @@
 	*/
 
 	if ((tmpdir = getenv("TMPDIR")) == NULL)
-#  ifdef __APPLE__
-	  tmpdir = "/private/tmp";	/* /tmp is a symlink to /private/tmp */
-#  else
-          tmpdir = "/tmp";
-#  endif /* __APPLE__ */
+	  tmpdir = "/tmp";
+#endif /* __APPLE__ */
 
        /*
 	* Make the temporary name using the specified directory...
@@ -280,7 +298,7 @@
       }
     }
   }
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
  /*
   * Try finding a printer URI for this printer...
@@ -511,23 +529,16 @@
     int        depth)			/* I - Depth of query */
 {
   int		i;			/* Looping var */
-  int		http_port;		/* Port number */
-  http_t	*http2;			/* Alternate HTTP connection */
   ipp_t		*request,		/* IPP request */
 		*response;		/* IPP response */
   ipp_attribute_t *attr;		/* Current attribute */
   char		uri[HTTP_MAX_URI],	/* printer-uri attribute */
 		scheme[HTTP_MAX_URI],	/* Scheme name */
-		username[HTTP_MAX_URI],	/* Username:password */
-		classname[255],		/* Temporary class name */
-		http_hostname[HTTP_MAX_HOST];
-					/* Hostname associated with connection */
+		username[HTTP_MAX_URI];	/* Username:password */
   static const char * const requested_attrs[] =
 		{			/* Requested attributes */
-		  "device-uri",
 		  "member-uris",
-		  "printer-uri-supported",
-		  "printer-type"
+		  "printer-uri-supported"
 		};
 
 
@@ -550,15 +561,6 @@
   DEBUG_printf(("5cups_get_printer_uri: printer-uri=\"%s\"", uri));
 
  /*
-  * Get the hostname and port number we are connected to...
-  */
-
-  httpGetHostname(http, http_hostname, sizeof(http_hostname));
-  http_port = httpAddrPort(http->hostaddr);
-
-  DEBUG_printf(("5cups_get_printer_uri: http_hostname=\"%s\"", http_hostname));
-
- /*
   * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
   * attributes:
   *
@@ -582,31 +584,7 @@
 
   if ((response = cupsDoRequest(http, request, resource)) != NULL)
   {
-    const char *device_uri = NULL;	/* device-uri value */
-
-    if ((attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI)) != NULL)
-    {
-      device_uri = attr->values[0].string.text;
-      DEBUG_printf(("5cups_get_printer_uri: device-uri=\"%s\"", device_uri));
-    }
-
-    if (device_uri &&
-        (((!strncmp(device_uri, "ipp://", 6) || !strncmp(device_uri, "ipps://", 7)) &&
-	  (strstr(device_uri, "/printers/") != NULL || strstr(device_uri, "/classes/") != NULL)) ||
-         ((strstr(device_uri, "._ipp.") != NULL || strstr(device_uri, "._ipps.") != NULL) &&
-          !strcmp(device_uri + strlen(device_uri) - 5, "/cups"))))
-    {
-     /*
-      * Statically-configured shared printer.
-      */
-
-      httpSeparateURI(HTTP_URI_CODING_ALL, _httpResolveURI(device_uri, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL), scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
-      ippDelete(response);
-
-      DEBUG_printf(("5cups_get_printer_uri: Resolved to host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
-      return (1);
-    }
-    else if ((attr = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL)
+    if ((attr = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL)
     {
      /*
       * Get the first actual printer name in the class...
@@ -631,55 +609,6 @@
 	  return (1);
 	}
       }
-
-     /*
-      * No printers in this class - try recursively looking for a printer,
-      * but not more than 3 levels deep...
-      */
-
-      if (depth < 3)
-      {
-	for (i = 0; i < attr->num_values; i ++)
-	{
-	  httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
-	                  scheme, sizeof(scheme), username, sizeof(username),
-			  host, hostsize, port, resource, resourcesize);
-	  if (!strncmp(resource, "/classes/", 9))
-	  {
-	   /*
-	    * Found a class!  Connect to the right server...
-	    */
-
-	    if (!_cups_strcasecmp(http_hostname, host) && *port == http_port)
-	      http2 = http;
-	    else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
-	    {
-	      DEBUG_puts("8cups_get_printer_uri: Unable to connect to server");
-
-	      continue;
-	    }
-
-           /*
-	    * Look up printers on that server...
-	    */
-
-            strlcpy(classname, resource + 9, sizeof(classname));
-
-            cups_get_printer_uri(http2, classname, host, hostsize, port,
-	                         resource, resourcesize, depth + 1);
-
-           /*
-	    * Close the connection as needed...
-	    */
-
-	    if (http2 != http)
-	      httpClose(http2);
-
-            if (*host)
-	      return (1);
-	  }
-	}
-      }
     }
     else if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
     {
diff --git a/cups/ppd.c b/cups/ppd.c
index 5bd839d..5f27484 100644
--- a/cups/ppd.c
+++ b/cups/ppd.c
@@ -1,7 +1,7 @@
 /*
  * PPD file routines for CUPS.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -94,9 +94,9 @@
 				       cups_encoding_t encoding);
 static ppd_option_t	*ppd_get_option(ppd_group_t *group, const char *name);
 static _ppd_globals_t	*ppd_globals_alloc(void);
-#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
 static void		ppd_globals_free(_ppd_globals_t *g);
-#endif /* HAVE_PTHREAD_H || WIN32 */
+#endif /* HAVE_PTHREAD_H || _WIN32 */
 #ifdef HAVE_PTHREAD_H
 static void		ppd_globals_init(void);
 #endif /* HAVE_PTHREAD_H */
@@ -333,7 +333,9 @@
 		  _("Bad custom parameter"),
 		  _("Missing option keyword"),
 		  _("Bad value string"),
-		  _("Missing CloseGroup")
+		  _("Missing CloseGroup"),
+		  _("Bad CloseUI/JCLCloseUI"),
+		  _("Missing CloseUI/JCLCloseUI")
 		};
 
 
@@ -1542,8 +1544,29 @@
         choice->code = _cupsStrRetain(custom_attr->value);
       }
     }
-    else if (!strcmp(keyword, "CloseUI") || !strcmp(keyword, "JCLCloseUI"))
+    else if (!strcmp(keyword, "CloseUI"))
     {
+      if ((!option || option->section == PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
+      {
+        pg->ppd_status = PPD_BAD_CLOSE_UI;
+
+	goto error;
+      }
+
+      option = NULL;
+
+      _cupsStrFree(string);
+      string = NULL;
+    }
+    else if (!strcmp(keyword, "JCLCloseUI"))
+    {
+      if ((!option || option->section != PPD_ORDER_JCL) && pg->ppd_conform == PPD_CONFORM_STRICT)
+      {
+        pg->ppd_status = PPD_BAD_CLOSE_UI;
+
+	goto error;
+      }
+
       option = NULL;
 
       _cupsStrFree(string);
@@ -2007,6 +2030,16 @@
   }
 
  /*
+  * Check for a missing CloseUI/JCLCloseUI...
+  */
+
+  if (option && pg->ppd_conform == PPD_CONFORM_STRICT)
+  {
+    pg->ppd_status = PPD_MISSING_CLOSE_UI;
+    goto error;
+  }
+
+ /*
   * Check for a missing CloseGroup...
   */
 
@@ -2804,13 +2837,13 @@
  * 'ppd_globals_free()' - Free global data.
  */
 
-#if defined(HAVE_PTHREAD_H) || defined(WIN32)
+#if defined(HAVE_PTHREAD_H) || defined(_WIN32)
 static void
 ppd_globals_free(_ppd_globals_t *pg)	/* I - Pointer to global data */
 {
   free(pg);
 }
-#endif /* HAVE_PTHREAD_H || WIN32 */
+#endif /* HAVE_PTHREAD_H || _WIN32 */
 
 
 #ifdef HAVE_PTHREAD_H
diff --git a/cups/ppd.h b/cups/ppd.h
index fb33c08..6e628cb 100644
--- a/cups/ppd.h
+++ b/cups/ppd.h
@@ -132,6 +132,8 @@
   PPD_MISSING_OPTION_KEYWORD,		/* Missing option keyword */
   PPD_BAD_VALUE,			/* Bad value string */
   PPD_MISSING_CLOSE_GROUP,		/* Missing CloseGroup */
+  PPD_BAD_CLOSE_UI,			/* Bad CloseUI/JCLCloseUI */
+  PPD_MISSING_CLOSE_UI,			/* Missing CloseUI/JCLCloseUI */
   PPD_MAX_STATUS			/* @private@ */
 } ppd_status_t;
 
diff --git a/cups/pwg-media.c b/cups/pwg-media.c
index 73356fd..62455ea 100644
--- a/cups/pwg-media.c
+++ b/cups/pwg-media.c
@@ -189,6 +189,7 @@
   _PWG_MEDIA_MM("jis_b1_728x1030mm", "jis-b1", "B1", 728, 1030),
   _PWG_MEDIA_MM("jis_b0_1030x1456mm", "jis-b0", "B0", 1030, 1456),
   _PWG_MEDIA_MM("jis_exec_216x330mm", NULL, "216x330mm", 216, 330),
+  _PWG_MEDIA_MM("jpn_kaku1_270x382mm", NULL, "EnvKaku1", 270, 382),
   _PWG_MEDIA_MM("jpn_kaku2_240x332mm", NULL, "EnvKaku2", 240, 332),
   _PWG_MEDIA_MM("jpn_kaku3_216x277mm", NULL, "EnvKaku3", 216, 277),
   _PWG_MEDIA_MM("jpn_kaku4_197x267mm", NULL, "EnvKaku4", 197, 267),
diff --git a/cups/pwg.h b/cups/pwg.h
index 2269dbe..4204050 100644
--- a/cups/pwg.h
+++ b/cups/pwg.h
@@ -1,7 +1,7 @@
 /*
  * PWG media API definitions for CUPS.
  *
- * Copyright 2009-2013 by Apple Inc.
+ * Copyright 2009-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -39,7 +39,7 @@
  * Types and structures...
  */
 
-typedef struct pwg_map_s		/**** Map element - PPD to/from PWG */
+typedef struct pwg_map_s		/**** Map element - PPD to/from PWG @exclude all@ */
 {
   char		*pwg,			/* PWG media keyword */
 		*ppd;			/* PPD option keyword */
@@ -54,7 +54,7 @@
 		length;			/* Length in 2540ths */
 } pwg_media_t;
 
-typedef struct pwg_size_s		/**** Size element - PPD to/from PWG */
+typedef struct pwg_size_s		/**** Size element - PPD to/from PWG @exclude all@ */
 {
   pwg_map_t	map;			/* Map element */
   int		width,			/* Width in 2540ths */
diff --git a/cups/raster-private.h b/cups/raster-private.h
index 3813581..94dd951 100644
--- a/cups/raster-private.h
+++ b/cups/raster-private.h
@@ -1,7 +1,7 @@
 /*
  * Private image library definitions for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1993-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -24,13 +24,13 @@
 #  include <cups/cups.h>
 #  include <cups/debug-private.h>
 #  include <cups/string-private.h>
-#  ifdef WIN32
+#  ifdef _WIN32
 #    include <io.h>
 #    include <winsock2.h>		/* for htonl() definition */
 #  else
 #    include <unistd.h>
 #    include <fcntl.h>
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 
 
 /*
@@ -49,12 +49,8 @@
  * Prototypes...
  */
 
-extern int		_cupsRasterExecPS(cups_page_header2_t *h,
-			                  int *preferred_bits,
-			                  const char *code)
-			                  __attribute__((nonnull(3)));
-extern void		_cupsRasterAddError(const char *f, ...)
-			__attribute__((__format__(__printf__, 1, 2)));
+extern int		_cupsRasterExecPS(cups_page_header2_t *h, int *preferred_bits, const char *code) _CUPS_NONNULL((3));
+extern void		_cupsRasterAddError(const char *f, ...) _CUPS_FORMAT(1,2);
 extern void		_cupsRasterClearError(void);
 
 #endif /* !_CUPS_RASTER_PRIVATE_H_ */
diff --git a/cups/request.c b/cups/request.c
index 39cbe6c..a4e4b90 100644
--- a/cups/request.c
+++ b/cups/request.c
@@ -1,7 +1,7 @@
 /*
  * IPP utilities for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -20,11 +20,11 @@
 #include "cups-private.h"
 #include <fcntl.h>
 #include <sys/stat.h>
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 #ifndef O_BINARY
 #  define O_BINARY 0
 #endif /* O_BINARY */
@@ -131,13 +131,12 @@
   * Get the default connection as needed...
   */
 
-  if (!http)
-    if ((http = _cupsConnect()) == NULL)
-    {
-      ippDelete(request);
+  if (!http && (http = _cupsConnect()) == NULL)
+  {
+    ippDelete(request);
 
-      return (NULL);
-    }
+    return (NULL);
+  }
 
  /*
   * See if we have a file to send...
@@ -151,43 +150,39 @@
       * Can't get file information!
       */
 
-      _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED,
-                    NULL, 0);
-
+      _cupsSetError(errno == EBADF ? IPP_STATUS_ERROR_NOT_FOUND : IPP_STATUS_ERROR_NOT_AUTHORIZED, NULL, 0);
       ippDelete(request);
 
       return (NULL);
     }
 
-#ifdef WIN32
+#ifdef _WIN32
     if (fileinfo.st_mode & _S_IFDIR)
 #else
     if (S_ISDIR(fileinfo.st_mode))
-#endif /* WIN32 */
+#endif /* _WIN32 */
     {
      /*
       * Can't send a directory...
       */
 
-      ippDelete(request);
-
       _cupsSetError(IPP_STATUS_ERROR_NOT_POSSIBLE, strerror(EISDIR), 0);
+      ippDelete(request);
 
       return (NULL);
     }
 
-#ifndef WIN32
+#ifndef _WIN32
     if (!S_ISREG(fileinfo.st_mode))
       length = 0;			/* Chunk when piping */
     else
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
     length = ippLength(request) + (size_t)fileinfo.st_size;
   }
   else
     length = ippLength(request);
 
-  DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld",
-                (long)ippLength(request), (long)length));
+  DEBUG_printf(("2cupsDoIORequest: Request length=%ld, total length=%ld", (long)ippLength(request), (long)length));
 
  /*
   * Clear any "Local" authentication data since it is probably stale...
@@ -220,9 +215,9 @@
       * Send the file with the request...
       */
 
-#ifndef WIN32
+#ifndef _WIN32
       if (S_ISREG(fileinfo.st_mode))
-#endif /* WIN32 */
+#endif /* _WIN32 */
       lseek(infile, 0, SEEK_SET);
 
       while ((bytes = read(infile, buffer, sizeof(buffer))) > 0)
@@ -596,6 +591,8 @@
   int			got_status;	/* Did we get the status? */
   ipp_state_t		state;		/* State of IPP processing */
   http_status_t		expect;		/* Expect: header to use */
+  char			date[256];	/* Date: header value */
+  int			digest;		/* Are we using Digest authentication? */
 
 
   DEBUG_printf(("cupsSendRequest(http=%p, request=%p(%s), resource=\"%s\", length=" CUPS_LLFMT ")", (void *)http, (void *)request, request ? ippOpString(request->request.op.operation_id) : "?", resource, CUPS_LLCAST length));
@@ -615,9 +612,8 @@
   * Get the default connection as needed...
   */
 
-  if (!http)
-    if ((http = _cupsConnect()) == NULL)
-      return (HTTP_STATUS_SERVICE_UNAVAILABLE);
+  if (!http && (http = _cupsConnect()) == NULL)
+    return (HTTP_STATUS_SERVICE_UNAVAILABLE);
 
  /*
   * If the prior request was not flushed out, do so now...
@@ -685,8 +681,20 @@
     httpClearFields(http);
     httpSetExpect(http, expect);
     httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+    httpSetField(http, HTTP_FIELD_DATE, httpGetDateString2(time(NULL), date, (int)sizeof(date)));
     httpSetLength(http, length);
 
+    digest = http->authstring && !strncmp(http->authstring, "Digest ", 7);
+
+    if (digest)
+    {
+     /*
+      * Update the Digest authentication string...
+      */
+
+      _httpSetDigestAuthString(http, http->nextnonce, "POST", resource);
+    }
+
 #ifdef HAVE_GSSAPI
     if (http->authstring && !strncmp(http->authstring, "Negotiate", 9))
     {
@@ -771,9 +779,9 @@
     * Wait up to 1 second to get the 100-continue response as needed...
     */
 
-    if (!got_status)
+    if (!got_status || (digest && status == HTTP_STATUS_CONTINUE))
     {
-      if (expect == HTTP_STATUS_CONTINUE)
+      if (expect == HTTP_STATUS_CONTINUE || digest)
       {
 	DEBUG_puts("2cupsSendRequest: Waiting for 100-continue...");
 
@@ -996,7 +1004,11 @@
     */
 
     if (strcmp(cg->http->hostname, cg->server) ||
+#ifdef AF_LOCAL
+        (httpAddrFamily(cg->http->hostaddr) != AF_LOCAL && cg->ipp_port != httpAddrPort(cg->http->hostaddr)) ||
+#else
         cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
+#endif /* AF_LOCAL */
         (cg->http->encryption != cg->encryption &&
 	 cg->http->encryption == HTTP_ENCRYPTION_NEVER))
     {
@@ -1016,13 +1028,13 @@
       char	ch;			/* Connection check byte */
       ssize_t	n;			/* Number of bytes */
 
-#ifdef WIN32
+#ifdef _WIN32
       if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK)) == 0 ||
           (n < 0 && WSAGetLastError() != WSAEWOULDBLOCK))
 #else
       if ((n = recv(cg->http->fd, &ch, 1, MSG_PEEK | MSG_DONTWAIT)) == 0 ||
           (n < 0 && errno != EWOULDBLOCK))
-#endif /* WIN32 */
+#endif /* _WIN32 */
       {
        /*
         * Nope, close the connection...
diff --git a/cups/sidechannel.c b/cups/sidechannel.c
index a4cd960..f2dbabd 100644
--- a/cups/sidechannel.c
+++ b/cups/sidechannel.c
@@ -19,15 +19,15 @@
 
 #include "sidechannel.h"
 #include "cups-private.h"
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 */
-#ifndef WIN32
+#endif /* _WIN32 */
+#ifndef _WIN32
 #  include <sys/select.h>
 #  include <sys/time.h>
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 #ifdef HAVE_POLL
 #  include <poll.h>
 #endif /* HAVE_POLL */
diff --git a/cups/snmp.c b/cups/snmp.c
index fffa218..8437528 100644
--- a/cups/snmp.c
+++ b/cups/snmp.c
@@ -395,11 +395,11 @@
 
       ready = select(fd + 1, &input_set, NULL, NULL, &stimeout);
     }
-#  ifdef WIN32
+#  ifdef _WIN32
     while (ready < 0 && WSAGetLastError() == WSAEINTR);
 #  else
     while (ready < 0 && (errno == EINTR || errno == EAGAIN));
-#  endif /* WIN32 */
+#  endif /* _WIN32 */
 #endif /* HAVE_POLL */
 
    /*
@@ -739,6 +739,10 @@
   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
 
 
+#ifdef __clang_analyzer__ /* Suppress bogus clang error */
+  memset(string, 0, sizeof(string));
+#endif /* __clang_analyzer__ */
+
   if (cg->snmp_debug <= 0)
     return;
 
diff --git a/cups/string-private.h b/cups/string-private.h
index e8448d1..ced2492 100644
--- a/cups/string-private.h
+++ b/cups/string-private.h
@@ -1,7 +1,7 @@
 /*
  * Private string definitions for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -42,12 +42,12 @@
 #    include <bstring.h>
 #  endif /* HAVE_BSTRING_H */
 
-#  if defined(WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
+#  if defined(_WIN32) && !defined(__CUPS_SSIZE_T_DEFINED)
 #    define __CUPS_SSIZE_T_DEFINED
 #    include <stddef.h>
 /* Windows does not support the ssize_t type, so map it to long... */
 typedef long ssize_t;			/* @private@ */
-#  endif /* WIN32 && !__CUPS_SSIZE_T_DEFINED */
+#  endif /* _WIN32 && !__CUPS_SSIZE_T_DEFINED */
 
 
 /*
@@ -175,8 +175,7 @@
 #  endif /* !HAVE_STRLCPY */
 
 #  ifndef HAVE_SNPRINTF
-extern int	_cups_snprintf(char *, size_t, const char *, ...)
-		__attribute__ ((__format__ (__printf__, 3, 4)));
+extern int	_cups_snprintf(char *, size_t, const char *, ...) _CUPS_FORMAT(3, 4);
 #    define snprintf _cups_snprintf
 #  endif /* !HAVE_SNPRINTF */
 
diff --git a/cups/string.c b/cups/string.c
index 39a787f..0d4ed0f 100644
--- a/cups/string.c
+++ b/cups/string.c
@@ -695,10 +695,11 @@
   */
 
   dstlen = strlen(dst);
-  size   -= dstlen + 1;
 
-  if (!size)
-    return (dstlen);		/* No room, return immediately... */
+  if (size < (dstlen + 1))
+    return (dstlen);		        /* No room, return immediately... */
+
+  size -= dstlen + 1;
 
  /*
   * Figure out how much room is needed...
diff --git a/cups/tempfile.c b/cups/tempfile.c
index da705a9..7b341e1 100644
--- a/cups/tempfile.c
+++ b/cups/tempfile.c
@@ -1,8 +1,8 @@
 /*
  * Temp file utilities for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
- * Copyright 1997-2006 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -21,11 +21,11 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 
 
 /*
@@ -42,24 +42,46 @@
   int		fd;			/* File descriptor for temp file */
   int		tries;			/* Number of tries */
   const char	*tmpdir;		/* TMPDIR environment var */
-#ifdef WIN32
-  char		tmppath[1024];		/* Windows temporary directory */
+#if defined(__APPLE__) || defined(_WIN32)
+  char		tmppath[1024];		/* Temporary directory */
+#endif /* __APPLE__ || _WIN32 */
+#ifdef _WIN32
   DWORD		curtime;		/* Current time */
 #else
   struct timeval curtime;		/* Current time */
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
  /*
   * See if TMPDIR is defined...
   */
 
-#ifdef WIN32
+#ifdef _WIN32
   if ((tmpdir = getenv("TEMP")) == NULL)
   {
     GetTempPath(sizeof(tmppath), tmppath);
     tmpdir = tmppath;
   }
+
+#elif defined(__APPLE__)
+ /*
+  * On macOS and iOS, the TMPDIR environment variable is not always the best
+  * location to place temporary files due to sandboxing.  Instead, the confstr
+  * function should be called to get the proper per-user, per-process TMPDIR
+  * value.
+  */
+
+  if ((tmpdir = getenv("TMPDIR")) != NULL && access(tmpdir, W_OK))
+    tmpdir = NULL;
+
+  if (!tmpdir)
+  {
+    if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmppath, sizeof(tmppath)))
+      tmpdir = tmppath;
+    else
+      tmpdir = "/private/tmp";		/* This should never happen */
+  }
+
 #else
  /*
   * Previously we put root temporary files in the default CUPS temporary
@@ -69,12 +91,8 @@
   */
 
   if ((tmpdir = getenv("TMPDIR")) == NULL)
-#  if defined(__APPLE__) && !TARGET_OS_IOS
-    tmpdir = "/private/tmp";		/* /tmp is a symlink to /private/tmp */
-#  else
     tmpdir = "/tmp";
-#  endif /* __APPLE__  && !TARGET_OS_IOS */
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
  /*
   * Make the temporary name using the specified directory...
@@ -84,7 +102,7 @@
 
   do
   {
-#ifdef WIN32
+#ifdef _WIN32
    /*
     * Get the current time of day...
     */
@@ -108,21 +126,21 @@
     */
 
     snprintf(filename, (size_t)len - 1, "%s/%05x%08x", tmpdir, (unsigned)getpid(), (unsigned)(curtime.tv_sec + curtime.tv_usec + tries));
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
    /*
     * Open the file in "exclusive" mode, making sure that we don't
     * stomp on an existing file or someone's symlink crack...
     */
 
-#ifdef WIN32
+#ifdef _WIN32
     fd = open(filename, _O_CREAT | _O_RDWR | _O_TRUNC | _O_BINARY,
               _S_IREAD | _S_IWRITE);
 #elif defined(O_NOFOLLOW)
     fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
 #else
     fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
     if (fd < 0 && errno != EEXIST)
       break;
diff --git a/cups/testarray.c b/cups/testarray.c
index 6164ffc..892e325 100644
--- a/cups/testarray.c
+++ b/cups/testarray.c
@@ -494,7 +494,7 @@
  * 'get_seconds()' - Get the current time in seconds...
  */
 
-#ifdef WIN32
+#ifdef _WIN32
 #  include <windows.h>
 
 
@@ -515,7 +515,7 @@
   gettimeofday(&curtime, NULL);
   return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
 }
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
diff --git a/cups/testcups.c b/cups/testcups.c
index aa58766..f4931c5 100644
--- a/cups/testcups.c
+++ b/cups/testcups.c
@@ -1,7 +1,7 @@
 /*
  * CUPS API test program for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -47,7 +47,9 @@
   cups_dest_t	*dests,			/* Destinations */
 		*dest,			/* Current destination */
 		*named_dest;		/* Current named destination */
-  const char	*ppdfile;		/* PPD file */
+  const char	*dest_name,             /* Destination name */
+                *dval,                  /* Destination value */
+                *ppdfile;		/* PPD file */
   ppd_file_t	*ppd;			/* PPD file data */
   int		num_jobs;		/* Number of jobs for queue */
   cups_job_t	*jobs;			/* Jobs for queue */
@@ -360,11 +362,19 @@
   * cupsGetDest(printer)
   */
 
-  printf("cupsGetDest(\"%s\"): ", dests[num_dests / 2].name);
+  for (i = 0, dest_name = NULL; i < num_dests; i ++)
+  {
+    if ((dval = cupsGetOption("printer-is-temporary", dests[i].num_options, dest[i].options)) != NULL && !strcmp(dval, "false"))
+    {
+      dest_name = dests[i].name;
+      break;
+    }
+  }
+
+  printf("cupsGetDest(\"%s\"): ", dest_name ? dest_name : "(null)");
   fflush(stdout);
 
-  if ((dest = cupsGetDest(dests[num_dests / 2].name, NULL, num_dests,
-                          dests)) == NULL)
+  if ((dest = cupsGetDest(dest_name, NULL, num_dests, dests)) == NULL)
   {
     puts("FAIL");
     return (1);
@@ -380,8 +390,7 @@
          dest->instance ? dest->instance : "(null)");
   fflush(stdout);
 
-  if ((named_dest = cupsGetNamedDest(NULL, dest->name,
-                                     dest->instance)) == NULL ||
+  if ((named_dest = cupsGetNamedDest(NULL, dest->name, dest->instance)) == NULL ||
       !dests_equal(dest, named_dest))
   {
     if (named_dest)
@@ -408,7 +417,7 @@
   fputs("cupsPrintFile: ", stdout);
   fflush(stdout);
 
-  if (cupsPrintFile(dest->name, "../data/testprint", "Test Page",
+  if (cupsPrintFile(dest->name, "../test/testfile.pdf", "Test Page",
                     dest->num_options, dest->options) <= 0)
   {
     printf("FAIL (%s)\n", cupsLastErrorString());
@@ -421,7 +430,7 @@
   * cupsGetPPD(printer)
   */
 
-  fputs("cupsGetPPD(): ", stdout);
+  fputs("cupsGetPPD: ", stdout);
   fflush(stdout);
 
   if ((ppdfile = cupsGetPPD(dest->name)) == NULL)
@@ -436,7 +445,7 @@
     * ppdOpenFile()
     */
 
-    fputs("ppdOpenFile(): ", stdout);
+    fputs("ppdOpenFile: ", stdout);
     fflush(stdout);
 
     if ((ppd = ppdOpenFile(ppdfile)) == NULL)
@@ -550,33 +559,39 @@
 {
   int		i;			/* Looping var */
   cups_option_t	*aoption;		/* Current option */
+  cups_option_t	*boption;		/* Current option */
   const char	*bval;			/* Option value */
 
 
   if (!a || !b)
     return;
 
-  puts("    Item                  cupsGetDest           cupsGetNamedDest");
-  puts("    --------------------  --------------------  --------------------");
+  puts("    Item                  cupsGetDest               cupsGetNamedDest");
+  puts("    --------------------  ------------------------  ------------------------");
 
   if (_cups_strcasecmp(a->name, b->name))
-    printf("    name                  %-20.20s  %-20.20s\n", a->name, b->name);
+    printf("    name                  %-24.24s  %-24.24s\n", a->name, b->name);
 
   if ((a->instance && !b->instance) ||
       (!a->instance && b->instance) ||
       (a->instance && _cups_strcasecmp(a->instance, b->instance)))
-    printf("    instance              %-20.20s  %-20.20s\n",
+    printf("    instance              %-24.24s  %-24.24s\n",
            a->instance ? a->instance : "(null)",
 	   b->instance ? b->instance : "(null)");
 
   if (a->num_options != b->num_options)
-    printf("    num_options           %-20d  %-20d\n", a->num_options,
+    printf("    num_options           %-24d  %-24d\n", a->num_options,
            b->num_options);
 
   for (i = a->num_options, aoption = a->options; i > 0; i --, aoption ++)
     if ((bval = cupsGetOption(aoption->name, b->num_options,
                               b->options)) == NULL ||
         strcmp(aoption->value, bval))
-      printf("    %-20.20s  %-20.20s  %-20.20s\n", aoption->name,
+      printf("    %-20.20s  %-24.24s  %-24.24s\n", aoption->name,
              aoption->value, bval ? bval : "(null)");
+
+  for (i = b->num_options, boption = b->options; i > 0; i --, boption ++)
+    if (!cupsGetOption(boption->name, a->num_options, a->options))
+      printf("    %-20.20s  %-24.24s  %-24.24s\n", boption->name,
+             boption->value, "(null)");
 }
diff --git a/cups/testdest.c b/cups/testdest.c
index 945e3e1..9eab8d2 100644
--- a/cups/testdest.c
+++ b/cups/testdest.c
@@ -1,7 +1,7 @@
 /*
  * CUPS destination API test program for CUPS.
  *
- * Copyright 2012-2016 by Apple Inc.
+ * Copyright 2012-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -32,7 +32,7 @@
 static void	show_default(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option);
 static void	show_media(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, unsigned flags, const char *name);
 static void	show_supported(http_t *http, cups_dest_t *dest, cups_dinfo_t *dinfo, const char *option, const char *value);
-static void	usage(const char *arg) __attribute__((noreturn));
+static void	usage(const char *arg) _CUPS_NORETURN;
 
 
 /*
@@ -105,6 +105,14 @@
   }
   else if (!strncmp(argv[1], "ipp://", 6) || !strncmp(argv[1], "ipps://", 7))
     dest = cupsGetDestWithURI(NULL, argv[1]);
+  else if (!strcmp(argv[1], "default"))
+  {
+    dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
+    if (dest && dest->instance)
+      printf("default is \"%s/%s\".\n", dest->name, dest->instance);
+    else
+      printf("default is \"%s\".\n", dest->name);
+  }
   else
     dest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, argv[1], NULL);
 
@@ -218,9 +226,9 @@
   (void)flags;
 
   if (dest->instance)
-    printf("%s/%s:\n", dest->name, dest->instance);
+    printf("%s%s/%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name, dest->instance);
   else
-    printf("%s:\n", dest->name);
+    printf("%s%s:\n", (flags & CUPS_DEST_FLAGS_REMOVED) ? "REMOVE " : "", dest->name);
 
   for (i = 0; i < dest->num_options; i ++)
     printf("    %s=\"%s\"\n", dest->options[i].name, dest->options[i].value);
@@ -463,10 +471,37 @@
 	     cups_dinfo_t *dinfo,	/* I - Destination information */
 	     const char  *option)	/* I - Option */
 {
-  (void)http;
-  (void)dest;
-  (void)dinfo;
-  (void)option;
+  if (!strcmp(option, "media"))
+  {
+   /*
+    * Show default media option...
+    */
+
+    cups_size_t size;                   /* Media size information */
+
+    if (cupsGetDestMediaDefault(http, dest, dinfo, CUPS_MEDIA_FLAGS_DEFAULT, &size))
+      printf("%s (%.2fx%.2fmm, margins=[%.2f %.2f %.2f %.2f])\n", size.media, size.width * 0.01, size.length * 0.01, size.left * 0.01, size.bottom * 0.01, size.right * 0.01, size.top * 0.01);
+     else
+       puts("FAILED");
+  }
+  else
+  {
+   /*
+    * Show default other option...
+    */
+
+    ipp_attribute_t *defattr;           /* Default attribute */
+
+    if ((defattr = cupsFindDestDefault(http, dest, dinfo, option)) != NULL)
+    {
+      char value[1024];                 /* Value of default attribute */
+
+      ippAttributeString(defattr, value, sizeof(value));
+      puts(value);
+    }
+    else
+      puts("FAILED");
+  }
 }
 
 
@@ -594,7 +629,8 @@
   }
   else if (!value)
   {
-    puts(option);
+    printf("%s (%s - %s)\n", option, cupsLocalizeDestOption(http, dest, dinfo, option), cupsCheckDestSupported(http, dest, dinfo, option, NULL) ? "supported" : "not-supported");
+
     if ((attr = cupsFindDestSupported(http, dest, dinfo, option)) != NULL)
     {
       count = ippGetCount(attr);
@@ -608,7 +644,13 @@
 
         case IPP_TAG_ENUM :
 	    for (i = 0; i < count; i ++)
-              printf("  %s\n", ippEnumString(option, ippGetInteger(attr, i)));
+	    {
+	      int val = ippGetInteger(attr, i);
+	      char valstr[256];
+
+              snprintf(valstr, sizeof(valstr), "%d", val);
+              printf("  %s (%s)\n", ippEnumString(option, ippGetInteger(attr, i)), cupsLocalizeDestValue(http, dest, dinfo, option, valstr));
+            }
 	    break;
 
         case IPP_TAG_RANGE :
@@ -634,11 +676,15 @@
 	    }
 	    break;
 
+	case IPP_TAG_KEYWORD :
+	    for (i = 0; i < count; i ++)
+              printf("  %s (%s)\n", ippGetString(attr, i, NULL), cupsLocalizeDestValue(http, dest, dinfo, option, ippGetString(attr, i, NULL)));
+	    break;
+
 	case IPP_TAG_TEXTLANG :
 	case IPP_TAG_NAMELANG :
 	case IPP_TAG_TEXT :
 	case IPP_TAG_NAME :
-	case IPP_TAG_KEYWORD :
 	case IPP_TAG_URI :
 	case IPP_TAG_URISCHEME :
 	case IPP_TAG_CHARSET :
diff --git a/cups/testfile.c b/cups/testfile.c
index b2ec8cd..1473a4a 100644
--- a/cups/testfile.c
+++ b/cups/testfile.c
@@ -1,7 +1,7 @@
 /*
  * File test program for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -22,14 +22,11 @@
 #include "file.h"
 #include <stdlib.h>
 #include <time.h>
-#ifdef HAVE_LIBZ
-#  include <zlib.h>
-#endif /* HAVE_LIBZ */
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 #include <fcntl.h>
 
 
@@ -53,10 +50,10 @@
   int		status;			/* Exit status */
   char		filename[1024];		/* Filename buffer */
   cups_file_t	*fp;			/* File pointer */
-#ifndef WIN32
+#ifndef _WIN32
   int		fds[2];			/* Open file descriptors */
   cups_file_t	*fdfile;		/* File opened with cupsFileOpenFd() */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
   int		count;			/* Number of lines in file */
 
 
@@ -84,7 +81,7 @@
 
     status += random_tests();
 
-#ifndef WIN32
+#ifndef _WIN32
    /*
     * Test fdopen and close without reading...
     */
@@ -118,7 +115,7 @@
 
       puts("PASS");
     }
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
    /*
     * Count lines in psglyphs, rewind, then count again.
@@ -174,13 +171,13 @@
     */
 
     fputs("\ncupsFileFind: ", stdout);
-#ifdef WIN32
+#ifdef _WIN32
     if (cupsFileFind("notepad.exe", "C:/WINDOWS", 1, filename, sizeof(filename)) &&
 	cupsFileFind("notepad.exe", "C:/WINDOWS;C:/WINDOWS/SYSTEM32", 1, filename, sizeof(filename)))
 #else
     if (cupsFileFind("cat", "/bin", 1, filename, sizeof(filename)) &&
 	cupsFileFind("cat", "/bin:/usr/bin", 1, filename, sizeof(filename)))
-#endif /* WIN32 */
+#endif /* _WIN32 */
       printf("PASS (%s)\n", filename);
     else
     {
diff --git a/cups/testgetdests.c b/cups/testgetdests.c
new file mode 100644
index 0000000..459998f
--- /dev/null
+++ b/cups/testgetdests.c
@@ -0,0 +1,51 @@
+/*
+ * CUPS cupsGetDests API test program for CUPS.
+ *
+ * Copyright 2017 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
+ * missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include "cups.h"
+#include <sys/time.h>
+
+
+/*
+ * 'main()' - Loop calling cupsGetDests.
+ */
+
+int                                     /* O - Exit status */
+main(void)
+{
+  int           num_dests;              /* Number of destinations */
+  cups_dest_t   *dests;                 /* Destinations */
+  struct timeval start, end;            /* Start and stop time */
+  double        secs;                   /* Total seconds to run cupsGetDests */
+
+
+  for (;;)
+  {
+    gettimeofday(&start, NULL);
+    num_dests = cupsGetDests(&dests);
+    gettimeofday(&end, NULL);
+    secs = end.tv_sec - start.tv_sec + 0.000001 * (end.tv_usec - start.tv_usec);
+
+    printf("Found %d printers in %.3f seconds...\n", num_dests, secs);
+
+    cupsFreeDests(num_dests, dests);
+    sleep(1);
+  }
+
+  return (0);
+}
diff --git a/cups/testhttp.c b/cups/testhttp.c
index 376d71f..7d8ec06 100644
--- a/cups/testhttp.c
+++ b/cups/testhttp.c
@@ -1,8 +1,8 @@
 /*
  * HTTP test program for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
- * Copyright 1997-2006 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -120,6 +120,9 @@
 			  { HTTP_URI_STATUS_OK, "ipp://%22%23%2F%3A%3C%3E%3F%40%5B%5C%5D%5E%60%7B%7C%7D/",
 			    "ipp", "", "\"#/:<>?@[\\]^`{|}", "/", 631, 0,
 			    HTTP_URI_CODING_MOST  },
+			  { HTTP_URI_STATUS_UNKNOWN_SCHEME, "smb://server/Some%20Printer",
+			    "smb", "", "server", "/Some Printer", 0, 0,
+			    HTTP_URI_CODING_ALL },
 
 			  /* Missing scheme */
 			  { HTTP_URI_STATUS_MISSING_SCHEME, "/path/to/file/index.html",
@@ -148,6 +151,9 @@
 			    HTTP_URI_CODING_MOST  },
 
 			  /* Bad scheme */
+			  { HTTP_URI_STATUS_BAD_SCHEME, "://server/ipp",
+			    "", "", "", "", 0, 0,
+			    HTTP_URI_CODING_MOST  },
 			  { HTTP_URI_STATUS_BAD_SCHEME, "bad_scheme://server/resource",
 			    "", "", "", "", 0, 0,
 			    HTTP_URI_CODING_MOST  },
@@ -180,6 +186,9 @@
 			    HTTP_URI_CODING_MOST  },
 
 			  /* Bad resource */
+			  { HTTP_URI_STATUS_BAD_RESOURCE, "mailto:\r\nbla",
+			    "mailto", "", "", "", 0, 0,
+			    HTTP_URI_CODING_MOST  },
 			  { HTTP_URI_STATUS_BAD_RESOURCE, "http://server/index.html%",
 			    "http", "", "server", "", 80, 0,
 			    HTTP_URI_CODING_MOST  },
@@ -332,6 +341,40 @@
     if (!j)
       puts("PASS");
 
+#if 0
+   /*
+    * _httpDigest()
+    */
+
+    fputs("_httpDigest(MD5): ", stdout);
+    if (!_httpDigest(buffer, sizeof(buffer), "MD5", "Mufasa", "http-auth@example.org", "Circle of Life", "7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", 1, "f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", "auth", "GET", "/dir/index.html"))
+    {
+      failures ++;
+      puts("FAIL (unable to calculate hash)");
+    }
+    else if (strcmp(buffer, "8ca523f5e9506fed4657c9700eebdbec"))
+    {
+      failures ++;
+      printf("FAIL (got \"%s\", expected \"8ca523f5e9506fed4657c9700eebdbec\")\n", buffer);
+    }
+    else
+      puts("PASS");
+
+    fputs("_httpDigest(SHA-256): ", stdout);
+    if (!_httpDigest(buffer, sizeof(buffer), "SHA-256", "Mufasa", "http-auth@example.org", "Circle of Life", "7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v", 1, "f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ", "auth", "GET", "/dir/index.html"))
+    {
+      failures ++;
+      puts("FAIL (unable to calculate hash)");
+    }
+    else if (strcmp(buffer, "753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1"))
+    {
+      failures ++;
+      printf("FAIL (got \"%s\", expected \"753927fa0e85d155564e2e272a28d1802ca10daf4496794697cf8db5856cb6c1\")\n", buffer);
+    }
+    else
+      puts("PASS");
+#endif /* 0 */
+
    /*
     * httpGetHostname()
     */
@@ -590,6 +633,8 @@
 
   for (i = 1; i < argc; i ++)
   {
+    int new_auth;
+
     if (!strcmp(argv[i], "-o"))
     {
       i ++;
@@ -673,6 +718,8 @@
 
     printf("Checking file \"%s\"...\n", resource);
 
+    new_auth = 0;
+
     do
     {
       if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
@@ -685,9 +732,13 @@
 	}
       }
 
+      if (http->authstring && !strncmp(http->authstring, "Digest ", 7) && !new_auth)
+        _httpSetDigestAuthString(http, http->nextnonce, "HEAD", resource);
+
       httpClearFields(http);
       httpSetField(http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString(http));
       httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
+
       if (httpHead(http, resource))
       {
         if (httpReconnect2(http, 30000, NULL))
@@ -704,6 +755,8 @@
 
       while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
 
+      new_auth = 0;
+
       if (status == HTTP_STATUS_UNAUTHORIZED)
       {
        /*
@@ -716,7 +769,9 @@
 	* See if we can do authentication...
 	*/
 
-	if (cupsDoAuthentication(http, "GET", resource))
+        new_auth = 1;
+
+	if (cupsDoAuthentication(http, "HEAD", resource))
 	{
 	  status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
 	  break;
@@ -764,6 +819,8 @@
     printf("Requesting file \"%s\" (Accept-Encoding: %s)...\n", resource,
            encoding ? encoding : "identity");
 
+    new_auth = 0;
+
     do
     {
       if (!_cups_strcasecmp(httpGetField(http, HTTP_FIELD_CONNECTION), "close"))
@@ -776,6 +833,9 @@
 	}
       }
 
+      if (http->authstring && !strncmp(http->authstring, "Digest ", 7) && !new_auth)
+        _httpSetDigestAuthString(http, http->nextnonce, "GET", resource);
+
       httpClearFields(http);
       httpSetField(http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString(http));
       httpSetField(http, HTTP_FIELD_ACCEPT_LANGUAGE, "en");
@@ -797,6 +857,8 @@
 
       while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE);
 
+      new_auth = 0;
+
       if (status == HTTP_STATUS_UNAUTHORIZED)
       {
        /*
@@ -809,6 +871,8 @@
 	* See if we can do authentication...
 	*/
 
+        new_auth = 1;
+
 	if (cupsDoAuthentication(http, "GET", resource))
 	{
 	  status = HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED;
diff --git a/cups/testipp.c b/cups/testipp.c
index 017ee9d..2ef2496 100644
--- a/cups/testipp.c
+++ b/cups/testipp.c
@@ -1,7 +1,7 @@
 /*
  * IPP test program for CUPS.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2005 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -20,12 +20,12 @@
 #include "file.h"
 #include "string-private.h"
 #include "ipp-private.h"
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
 #  include <fcntl.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -810,88 +810,9 @@
 print_attributes(ipp_t *ipp,		/* I - IPP request */
                  int   indent)		/* I - Indentation */
 {
-  int			i;		/* Looping var */
   ipp_tag_t		group;		/* Current group */
   ipp_attribute_t	*attr;		/* Current attribute */
-  _ipp_value_t		*val;		/* Current value */
-  static const char * const tags[] =	/* Value/group tag strings */
-			{
-			  "reserved-00",
-			  "operation-attributes-tag",
-			  "job-attributes-tag",
-			  "end-of-attributes-tag",
-			  "printer-attributes-tag",
-			  "unsupported-attributes-tag",
-			  "subscription-attributes-tag",
-			  "event-attributes-tag",
-			  "reserved-08",
-			  "reserved-09",
-			  "reserved-0A",
-			  "reserved-0B",
-			  "reserved-0C",
-			  "reserved-0D",
-			  "reserved-0E",
-			  "reserved-0F",
-			  "unsupported",
-			  "default",
-			  "unknown",
-			  "no-value",
-			  "reserved-14",
-			  "not-settable",
-			  "delete-attr",
-			  "admin-define",
-			  "reserved-18",
-			  "reserved-19",
-			  "reserved-1A",
-			  "reserved-1B",
-			  "reserved-1C",
-			  "reserved-1D",
-			  "reserved-1E",
-			  "reserved-1F",
-			  "reserved-20",
-			  "integer",
-			  "boolean",
-			  "enum",
-			  "reserved-24",
-			  "reserved-25",
-			  "reserved-26",
-			  "reserved-27",
-			  "reserved-28",
-			  "reserved-29",
-			  "reserved-2a",
-			  "reserved-2b",
-			  "reserved-2c",
-			  "reserved-2d",
-			  "reserved-2e",
-			  "reserved-2f",
-			  "octetString",
-			  "dateTime",
-			  "resolution",
-			  "rangeOfInteger",
-			  "begCollection",
-			  "textWithLanguage",
-			  "nameWithLanguage",
-			  "endCollection",
-			  "reserved-38",
-			  "reserved-39",
-			  "reserved-3a",
-			  "reserved-3b",
-			  "reserved-3c",
-			  "reserved-3d",
-			  "reserved-3e",
-			  "reserved-3f",
-			  "reserved-40",
-			  "textWithoutLanguage",
-			  "nameWithoutLanguage",
-			  "reserved-43",
-			  "keyword",
-			  "uri",
-			  "uriScheme",
-			  "charset",
-			  "naturalLanguage",
-			  "mimeMediaType",
-			  "memberName"
-			};
+  char                  buffer[2048];   /* Value string */
 
 
   for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
@@ -907,83 +828,12 @@
     {
       group = attr->group_tag;
 
-      printf("\n%*s%s:\n\n", indent - 4, "", tags[group]);
+      printf("\n%*s%s:\n\n", indent - 4, "", ippTagString(group));
     }
 
-    printf("%*s%s (", indent, "", attr->name ? attr->name : "(null)");
-    if (attr->num_values > 1)
-      printf("1setOf ");
-    printf("%s):", tags[attr->value_tag]);
+    ippAttributeString(attr, buffer, sizeof(buffer));
 
-    switch (attr->value_tag)
-    {
-      case IPP_TAG_ENUM :
-      case IPP_TAG_INTEGER :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %d", val->integer);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_BOOLEAN :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %s", val->boolean ? "true" : "false");
-          putchar('\n');
-          break;
-
-      case IPP_TAG_RANGE :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %d-%d", val->range.lower, val->range.upper);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_DATE :
-          {
-	    char	vstring[256];	/* Formatted time */
-
-	    for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	      printf(" (%s)", _cupsStrDate(vstring, sizeof(vstring), ippDateToTime(val->date)));
-          }
-          putchar('\n');
-          break;
-
-      case IPP_TAG_RESOLUTION :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" %dx%d%s", val->resolution.xres, val->resolution.yres,
-	           val->resolution.units == IPP_RES_PER_INCH ? "dpi" : "dpcm");
-          putchar('\n');
-          break;
-
-      case IPP_TAG_STRING :
-      case IPP_TAG_TEXTLANG :
-      case IPP_TAG_NAMELANG :
-      case IPP_TAG_TEXT :
-      case IPP_TAG_NAME :
-      case IPP_TAG_KEYWORD :
-      case IPP_TAG_URI :
-      case IPP_TAG_URISCHEME :
-      case IPP_TAG_CHARSET :
-      case IPP_TAG_LANGUAGE :
-      case IPP_TAG_MIMETYPE :
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	    printf(" \"%s\"", val->string.text);
-          putchar('\n');
-          break;
-
-      case IPP_TAG_BEGIN_COLLECTION :
-          putchar('\n');
-
-          for (i = 0, val = attr->values; i < attr->num_values; i ++, val ++)
-	  {
-	    if (i)
-	      putchar('\n');
-	    print_attributes(val->collection, indent + 4);
-	  }
-          break;
-
-      default :
-          printf("UNKNOWN (%d values)\n", attr->num_values);
-          break;
-    }
+    printf("%*s%s (%s%s): %s\n", indent, "", attr->name ? attr->name : "(null)", attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), buffer);
   }
 }
 
diff --git a/cups/testlang.c b/cups/testlang.c
index 6aa49ab..3f140d9 100644
--- a/cups/testlang.c
+++ b/cups/testlang.c
@@ -1,7 +1,7 @@
 /*
  * Localization test program for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -19,6 +19,16 @@
 
 #include "cups-private.h"
 #include "ppd-private.h"
+#ifdef __APPLE__
+#  include <CoreFoundation/CoreFoundation.h>
+#endif /* __APPLE__ */
+
+
+/*
+ * Local functions...
+ */
+
+static int  test_string(cups_lang_t *language, const char *msgid);
 
 
 /*
@@ -71,8 +81,9 @@
 
   printf("Language = \"%s\"\n", language->language);
   printf("Encoding = \"%s\"\n", _cupsEncodingName(language->encoding));
-  printf("No       = \"%s\"\n", _cupsLangString(language, "No"));
-  printf("Yes      = \"%s\"\n", _cupsLangString(language, "Yes"));
+
+  errors += test_string(language, "No");
+  errors += test_string(language, "Yes");
 
   if (language != language2)
   {
@@ -138,9 +149,141 @@
         }
       }
 
+      printf("media-empty: %s\n", ppdLocalizeIPPReason(ppd, "media-empty", NULL, buffer, sizeof(buffer)));
+
       ppdClose(ppd);
     }
   }
+#ifdef __APPLE__
+  else
+  {
+   /*
+    * Test all possible language IDs for compatibility with _cupsAppleLocale...
+    */
+
+    CFIndex     j,                      /* Looping var */
+                num_locales;            /* Number of locales */
+    CFArrayRef  locales;                /* Locales */
+    CFStringRef locale_id,              /* Current locale ID */
+                language_id;            /* Current language ID */
+    char        locale_str[256],        /* Locale ID C string */
+                language_str[256],      /* Language ID C string */
+                *bufptr;                /* Pointer to ".UTF-8" in POSIX locale */
+    size_t      buflen;                 /* Length of POSIX locale */
+#  if TEST_COUNTRY_CODES
+    CFIndex     k,                      /* Looping var */
+                num_country_codes;      /* Number of country codes */
+    CFArrayRef  country_codes;          /* Country codes */
+    CFStringRef country_code,           /* Current country code */
+                temp_id;                /* Temporary language ID */
+    char        country_str[256];       /* Country code C string */
+#  endif /* TEST_COUNTRY_CODES */
+
+    locales     = CFLocaleCopyAvailableLocaleIdentifiers();
+    num_locales = CFArrayGetCount(locales);
+
+#  if TEST_COUNTRY_CODES
+    country_codes     = CFLocaleCopyISOCountryCodes();
+    num_country_codes = CFArrayGetCount(country_codes);
+#  endif /* TEST_COUNTRY_CODES */
+
+    printf("%d locales are available:\n", (int)num_locales);
+
+    for (j = 0; j < num_locales; j ++)
+    {
+      locale_id   = CFArrayGetValueAtIndex(locales, j);
+      language_id = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, locale_id);
+
+      if (!locale_id || !CFStringGetCString(locale_id, locale_str, (CFIndex)sizeof(locale_str), kCFStringEncodingASCII))
+      {
+        printf("%d: FAIL (unable to get locale ID string)\n", (int)j + 1);
+        errors ++;
+        continue;
+      }
+
+      if (!language_id || !CFStringGetCString(language_id, language_str, (CFIndex)sizeof(language_str), kCFStringEncodingASCII))
+      {
+        printf("%d %s: FAIL (unable to get language ID string)\n", (int)j + 1, locale_str);
+        errors ++;
+        continue;
+      }
+
+      if (!_cupsAppleLocale(language_id, buffer, sizeof(buffer)))
+      {
+        printf("%d %s(%s): FAIL (unable to convert language ID string to POSIX locale)\n", (int)j + 1, locale_str, language_str);
+        errors ++;
+        continue;
+      }
+
+      if ((bufptr = strstr(buffer, ".UTF-8")) != NULL)
+        buflen = (size_t)(bufptr - buffer);
+      else
+        buflen = strlen(buffer);
+
+      if ((language = cupsLangGet(buffer)) == NULL)
+      {
+        printf("%d %s(%s): FAIL (unable to load POSIX locale \"%s\")\n", (int)j + 1, locale_str, language_str, buffer);
+        errors ++;
+        continue;
+      }
+
+      if (strncasecmp(language->language, buffer, buflen))
+      {
+        printf("%d %s(%s): FAIL (unable to load POSIX locale \"%s\", got \"%s\")\n", (int)j + 1, locale_str, language_str, buffer, language->language);
+        errors ++;
+        continue;
+      }
+
+      printf("%d %s(%s): PASS (POSIX locale is \"%s\")\n", (int)j + 1, locale_str, language_str, buffer);
+    }
+
+    CFRelease(locales);
+
+#  if TEST_COUNTRY_CODES
+    CFRelease(country_codes);
+#  endif /* TEST_COUNTRY_CODES */
+  }
+#endif /* __APPLE__ */
+
+  if (errors == 0)
+    puts("ALL TESTS PASSED");
 
   return (errors > 0);
 }
+
+
+/*
+ * 'test_string()' - Test the localization of a string.
+ */
+
+static int                            /* O - 1 on failure, 0 on success */
+test_string(cups_lang_t *language,    /* I - Language */
+            const char  *msgid)       /* I - Message */
+{
+  const char  *msgstr;                /* Localized string */
+
+
+ /*
+  * Get the localized string and then see if we got what we expected.
+  *
+  * For the POSIX locale, the string pointers should be the same.
+  * For any other locale, the string pointers should be different.
+  */
+
+  msgstr = _cupsLangString(language, msgid);
+  if (strcmp(language->language, "C") && msgid == msgstr)
+  {
+    printf("%-8s = \"%s\" (FAIL - no message catalog loaded)\n", msgid, msgstr);
+    return (1);
+  }
+  else if (!strcmp(language->language, "C") && msgid != msgstr)
+  {
+    printf("%-8s = \"%s\" (FAIL - POSIX locale is localized)\n", msgid, msgstr);
+    return (1);
+  }
+
+  printf("%-8s = \"%s\" (PASS)\n", msgid, msgstr);
+
+  return (0);
+}
+
diff --git a/cups/testppd.c b/cups/testppd.c
index e1b5156..a127a79 100644
--- a/cups/testppd.c
+++ b/cups/testppd.c
@@ -21,12 +21,12 @@
 #include "cups-private.h"
 #include "ppd-private.h"
 #include <sys/stat.h>
-#ifdef WIN32
+#ifdef _WIN32
 #  include <io.h>
 #else
 #  include <unistd.h>
 #  include <fcntl.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 #include <math.h>
 
 
@@ -876,6 +876,13 @@
 		host[256],		/* Hostname */
 		resource[256];		/* Resource path */
     int		port;			/* Port number */
+    static const char * const pattrs[] =/* Requested printer attributes */
+    {
+      "job-template",
+      "printer-defaults",
+      "printer-description",
+      "media-col-database"
+    };
 
     if (httpSeparateURI(HTTP_URI_CODING_ALL, argv[1], scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
     {
@@ -892,6 +899,7 @@
 
     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, argv[1]);
+    ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]), NULL, pattrs);
     response = cupsDoRequest(http, request, resource);
 
     if (_ppdCreateFromIPP(buffer, sizeof(buffer), response))
diff --git a/cups/testsnmp.c b/cups/testsnmp.c
index 4026a28..9e168ab 100644
--- a/cups/testsnmp.c
+++ b/cups/testsnmp.c
@@ -1,7 +1,7 @@
 /*
  * SNMP test program for CUPS.
  *
- * Copyright 2008-2014 by Apple Inc.
+ * Copyright 2008-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -27,7 +27,7 @@
 static void	print_packet(cups_snmp_t *packet, void *data);
 static int	show_oid(int fd, const char *community,
 		         http_addr_t *addr, const char *s, int walk);
-static void	usage(void) __attribute__((noreturn));
+static void	usage(void) _CUPS_NORETURN;
 
 
 /*
diff --git a/cups/thread-private.h b/cups/thread-private.h
index ca4ef4c..ae8163d 100644
--- a/cups/thread-private.h
+++ b/cups/thread-private.h
@@ -1,7 +1,7 @@
 /*
  * Private threading definitions for CUPS.
  *
- * Copyright 2009-2016 by Apple Inc.
+ * Copyright 2009-2017 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -46,7 +46,7 @@
 #    define _cupsThreadGetData(k) pthread_getspecific(k)
 #    define _cupsThreadSetData(k,p) pthread_setspecific(k,p)
 
-#  elif defined(WIN32)			/* Windows threading */
+#  elif defined(_WIN32)			/* Windows threading */
 #    include <winsock2.h>
 #    include <windows.h>
 typedef void *(__stdcall *_cups_thread_func_t)(void *arg);
@@ -99,6 +99,7 @@
 extern void	_cupsRWUnlock(_cups_rwlock_t *rwlock);
 extern void	_cupsThreadCancel(_cups_thread_t thread);
 extern _cups_thread_t _cupsThreadCreate(_cups_thread_func_t func, void *arg);
+extern void     _cupsThreadDetach(_cups_thread_t thread);
 extern void	*_cupsThreadWait(_cups_thread_t thread);
 
 #  ifdef __cplusplus
diff --git a/cups/thread.c b/cups/thread.c
index 77b4442..7ffc2ec 100644
--- a/cups/thread.c
+++ b/cups/thread.c
@@ -1,7 +1,7 @@
 /*
  * Threading primitives for CUPS.
  *
- * Copyright 2009-2016 by Apple Inc.
+ * Copyright © 2009-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -56,8 +56,16 @@
   {
     struct timespec abstime;		/* Timeout */
 
-    abstime.tv_sec  = (long)timeout;
-    abstime.tv_nsec = (long)(1000000000 * (timeout - (long)timeout));
+    clock_gettime(CLOCK_REALTIME, &abstime);
+
+    abstime.tv_sec  += (long)timeout;
+    abstime.tv_nsec += (long)(1000000000 * (timeout - (long)timeout));
+
+    while (abstime.tv_nsec >= 1000000000)
+    {
+      abstime.tv_nsec -= 1000000000;
+      abstime.tv_sec ++;
+    };
 
     pthread_cond_timedwait(cond, mutex, &abstime);
   }
@@ -173,6 +181,17 @@
 
 
 /*
+ * '_cupsThreadDetach()' - Tell the OS that the thread is running independently.
+ */
+
+void
+_cupsThreadDetach(_cups_thread_t thread)/* I - Thread ID */
+{
+  pthread_detach(thread);
+}
+
+
+/*
  * '_cupsThreadWait()' - Wait for a thread to exit.
  */
 
@@ -189,7 +208,7 @@
 }
 
 
-#elif defined(WIN32)
+#elif defined(_WIN32)
 #  include <process.h>
 
 
@@ -344,6 +363,18 @@
 
 
 /*
+ * '_cupsThreadDetach()' - Tell the OS that the thread is running independently.
+ */
+
+void
+_cupsThreadDetach(_cups_thread_t thread)/* I - Thread ID */
+{
+  // TODO: Implement me
+  (void)thread;
+}
+
+
+/*
  * '_cupsThreadWait()' - Wait for a thread to exit.
  */
 
@@ -490,8 +521,7 @@
     _cups_thread_func_t func,		/* I - Entry point */
     void                *arg)		/* I - Entry point context */
 {
-  fputs("DEBUG: CUPS was compiled without threading support, no thread "
-        "created.\n", stderr);
+  fputs("DEBUG: CUPS was compiled without threading support, no thread created.\n", stderr);
 
   (void)func;
   (void)arg;
@@ -501,6 +531,17 @@
 
 
 /*
+ * '_cupsThreadDetach()' - Tell the OS that the thread is running independently.
+ */
+
+void
+_cupsThreadDetach(_cups_thread_t thread)/* I - Thread ID */
+{
+  (void)thread;
+}
+
+
+/*
  * '_cupsThreadWait()' - Wait for a thread to exit.
  */
 
diff --git a/cups/tls-boringssl.c b/cups/tls-boringssl.c
index 6aec314..c32e683 100644
--- a/cups/tls-boringssl.c
+++ b/cups/tls-boringssl.c
@@ -30,7 +30,9 @@
 
 static char		*tls_keypath = NULL;
 					/* Server cert keychain path */
-static int		tls_options = -1;/* Options for TLS connections */
+static int		tls_options = -1,/* Options for TLS connections */
+  tls_min_version = _HTTP_TLS_1_0,
+  tls_max_version = _HTTP_TLS_MAX;
 
 
 /*
@@ -414,9 +416,11 @@
  */
 
 void
-_httpTLSSetOptions(int options)		/* I - Options */
+_httpTLSSetOptions(int options, int min_version, int max_version)		/* I - Options */
 {
   tls_options = options;
+  tls_min_version = min_version;
+  tls_max_version = max_version;
 }
 
 
@@ -453,12 +457,9 @@
     return (-1);
   }
 
-  if (tls_options & _HTTP_TLS_DENY_TLS10)
-    context = SSL_CTX_new(http->mode == _HTTP_MODE_CLIENT ? TLSv1_1_client_method() : TLSv1_1_server_method());
-  else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
-    context = SSL_CTX_new(http->mode == _HTTP_MODE_CLIENT ? SSLv3_client_method() : SSLv3_server_method());
-  else
-    context = SSL_CTX_new(http->mode == _HTTP_MODE_CLIENT ? TLSv1_client_method() : TLSv1_server_method());
+  context = SSL_CTX_new(TLS_method());
+  SSL_CTX_set_min_proto_version(context, tls_min_version);
+  SSL_CTX_set_max_proto_version(context, tls_max_version);
 
   bio = BIO_new(_httpBIOMethods());
   BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)http);
diff --git a/cups/tls-darwin.c b/cups/tls-darwin.c
index b6e88b0..c4f0ccb 100644
--- a/cups/tls-darwin.c
+++ b/cups/tls-darwin.c
@@ -1,8 +1,8 @@
 /*
  * TLS support code for CUPS on macOS.
  *
- * Copyright 2007-2016 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright © 2007-2018 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
@@ -21,7 +21,7 @@
 
 #include <spawn.h>
 
-extern char **environ;
+extern char **environ; /* @private@ */
 
 
 /*
@@ -53,7 +53,9 @@
 					/* Server cert keychain path */
 static _cups_mutex_t	tls_mutex = _CUPS_MUTEX_INITIALIZER;
 					/* Mutex for keychain/certs */
-static int		tls_options = -1;/* Options for TLS connections */
+static int		tls_options = -1,/* Options for TLS connections */
+			tls_min_version = _HTTP_TLS_1_0,
+			tls_max_version = _HTTP_TLS_MAX;
 
 
 /*
@@ -807,7 +809,6 @@
     CFStringRef		cf_name;	/* CF common name string */
     char		name[256];	/* Common name associated with cert */
     time_t		expiration;	/* Expiration date of cert */
-    _cups_md5_state_t	md5_state;	/* MD5 state */
     unsigned char	md5_digest[16];	/* MD5 result */
 
     if ((cf_name = SecCertificateCopySubjectSummary(secCert)) != NULL)
@@ -820,9 +821,7 @@
 
     expiration = (time_t)(SecCertificateNotValidAfter(secCert) + kCFAbsoluteTimeIntervalSince1970);
 
-    _cupsMD5Init(&md5_state);
-    _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
-    _cupsMD5Finish(&md5_state, md5_digest);
+    cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
 
     snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
 
@@ -1139,9 +1138,16 @@
  */
 
 void
-_httpTLSSetOptions(int options)		/* I - Options */
+_httpTLSSetOptions(int options,		/* I - Options */
+                   int min_version,	/* I - Minimum TLS version */
+                   int max_version)	/* I - Maximum TLS version */
 {
-  tls_options = options;
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+  {
+    tls_options     = options;
+    tls_min_version = min_version;
+    tls_max_version = max_version;
+  }
 }
 
 
@@ -1173,7 +1179,7 @@
   {
     DEBUG_puts("4_httpTLSStart: Setting defaults.");
     _cupsSetDefaults();
-    DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
+    DEBUG_printf(("4_httpTLSStart: tls_options=%x, tls_min_version=%d, tls_max_version=%d", tls_options, tls_min_version, tls_max_version));
   }
 
 #ifdef HAVE_SECKEYCHAINOPEN
@@ -1216,17 +1222,26 @@
 
   if (!error)
   {
-    SSLProtocol minProtocol;
+    static const SSLProtocol protocols[] =	/* Min/max protocol versions */
+    {
+      kSSLProtocol3,
+      kTLSProtocol1,
+      kTLSProtocol11,
+      kTLSProtocol12,
+      kTLSProtocol13
+    };
 
-    if (tls_options & _HTTP_TLS_DENY_TLS10)
-      minProtocol = kTLSProtocol11;
-    else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
-      minProtocol = kSSLProtocol3;
-    else
-      minProtocol = kTLSProtocol1;
+    if (tls_min_version < _HTTP_TLS_MAX)
+    {
+      error = SSLSetProtocolVersionMin(http->tls, protocols[tls_min_version]);
+      DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", protocols[tls_min_version], (int)error));
+    }
 
-    error = SSLSetProtocolVersionMin(http->tls, minProtocol);
-    DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMin(%d), error=%d", minProtocol, (int)error));
+    if (!error && tls_max_version < _HTTP_TLS_MAX)
+    {
+      error = SSLSetProtocolVersionMax(http->tls, protocols[tls_max_version]);
+      DEBUG_printf(("4_httpTLSStart: SSLSetProtocolVersionMax(%d), error=%d", protocols[tls_max_version], (int)error));
+    }
   }
 
 #  if HAVE_SSLSETENABLEDCIPHERS
@@ -1324,7 +1339,6 @@
           case TLS_DHE_RSA_WITH_AES_256_CBC_SHA :
           case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA :
           case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA :
-//          case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
           case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA :
           case TLS_DH_DSS_WITH_AES_128_CBC_SHA256 :
           case TLS_DH_RSA_WITH_AES_128_CBC_SHA256 :
@@ -1337,6 +1351,14 @@
           case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA :
           case TLS_DHE_PSK_WITH_AES_128_CBC_SHA :
           case TLS_DHE_PSK_WITH_AES_256_CBC_SHA :
+          case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
+          case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
+	      if (tls_options & _HTTP_TLS_DENY_CBC)
+	      {
+	        DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
+	        break;
+	      }
+
 //          case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
 //          case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
           case TLS_DH_RSA_WITH_AES_128_GCM_SHA256 :
@@ -1347,15 +1369,31 @@
           case TLS_DH_DSS_WITH_AES_256_GCM_SHA384 :
           case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 :
           case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 :
-          case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
-          case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
               if (tls_options & _HTTP_TLS_ALLOW_DH)
 	        enabled[num_enabled ++] = supported[i];
 	      else
 		DEBUG_printf(("4_httpTLSStart: Excluding DH/DHE cipher suite %d", supported[i]));
               break;
 
-          /* Anything else we'll assume is secure */
+          case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA :
+          case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 :
+          case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 :
+          case TLS_RSA_WITH_3DES_EDE_CBC_SHA :
+          case TLS_RSA_WITH_AES_128_CBC_SHA :
+          case TLS_RSA_WITH_AES_256_CBC_SHA :
+              if (tls_options & _HTTP_TLS_DENY_CBC)
+	      {
+	        DEBUG_printf(("4_httpTLSStart: Excluding CBC cipher suite %d", supported[i]));
+	        break;
+	      }
+
+          /* Anything else we'll assume is "secure" */
           default :
 	      enabled[num_enabled ++] = supported[i];
 	      break;
@@ -1502,7 +1540,28 @@
 
   if (!error)
   {
-    int done = 0;			/* Are we done yet? */
+    int			done = 0;	/* Are we done yet? */
+    double		old_timeout;	/* Old timeout value */
+    http_timeout_cb_t	old_cb;		/* Old timeout callback */
+    void		*old_data;	/* Old timeout data */
+
+   /*
+    * Enforce a minimum timeout of 10 seconds for the TLS handshake...
+    */
+
+    old_timeout  = http->timeout_value;
+    old_cb       = http->timeout_cb;
+    old_data     = http->timeout_data;
+
+    if (!old_cb || old_timeout < 10.0)
+    {
+      DEBUG_puts("4_httpTLSStart: Setting timeout to 10 seconds.");
+      httpSetTimeout(http, 10.0, NULL, NULL);
+    }
+
+   /*
+    * Do the TLS handshake...
+    */
 
     while (!error && !done)
     {
@@ -1623,6 +1682,12 @@
 	    break;
       }
     }
+
+   /*
+    * Restore the previous timeout settings...
+    */
+
+    httpSetTimeout(http, old_timeout, old_cb, old_data);
   }
 
   if (error)
@@ -1834,7 +1899,9 @@
   DEBUG_printf(("4http_cdsa_copy_server: Returning %p.", (void *)certificates));
 
   return (certificates);
+
 #else
+  (void)common_name;
 
   if (!tls_selfsigned)
     return (NULL);
@@ -2055,7 +2122,7 @@
 
   http = (http_t *)connection;
 
-  if (!http->blocking)
+  if (!http->blocking || http->timeout_value > 0.0)
   {
    /*
     * Make sure we have data before we read...
diff --git a/cups/tls-gnutls.c b/cups/tls-gnutls.c
index 7193d79..606b45a 100644
--- a/cups/tls-gnutls.c
+++ b/cups/tls-gnutls.c
@@ -1,8 +1,8 @@
 /*
  * TLS support code for CUPS using GNU TLS.
  *
- * Copyright 2007-2016 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright © 2007-2018 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
@@ -35,7 +35,9 @@
 					/* Server cert keychain path */
 static _cups_mutex_t	tls_mutex = _CUPS_MUTEX_INITIALIZER;
 					/* Mutex for keychain/certs */
-static int		tls_options = -1;/* Options for TLS connections */
+static int		tls_options = -1,/* Options for TLS connections */
+			tls_min_version = _HTTP_TLS_1_0,
+			tls_max_version = _HTTP_TLS_MAX;
 
 
 /*
@@ -397,7 +399,7 @@
         for (i = 0; i < count; i ++)
 	{
 	  rserial_size = sizeof(rserial);
-          if (!gnutls_x509_crl_get_crt_serial(tls_crl, i, rserial, &rserial_size, NULL) && cserial_size == rserial_size && !memcmp(cserial, rserial, rserial_size))
+          if (!gnutls_x509_crl_get_crt_serial(tls_crl, (unsigned)i, rserial, &rserial_size, NULL) && cserial_size == rserial_size && !memcmp(cserial, rserial, rserial_size))
 	  {
 	    result = 0;
 	    break;
@@ -646,7 +648,6 @@
     char		name[256];	/* Common name associated with cert */
     size_t		namelen;	/* Length of name */
     time_t		expiration;	/* Expiration date of cert */
-    _cups_md5_state_t	md5_state;	/* MD5 state */
     unsigned char	md5_digest[16];	/* MD5 result */
 
     namelen = sizeof(name) - 1;
@@ -657,9 +658,7 @@
 
     expiration = gnutls_x509_crt_get_expiration_time(cert);
 
-    _cupsMD5Init(&md5_state);
-    _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
-    _cupsMD5Finish(&md5_state, md5_digest);
+    cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
 
     snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
 
@@ -1094,7 +1093,7 @@
 
   http = (http_t *)ptr;
 
-  if (!http->blocking)
+  if (!http->blocking || http->timeout_value > 0.0)
   {
    /*
     * Make sure we have data before we read...
@@ -1224,9 +1223,16 @@
  */
 
 void
-_httpTLSSetOptions(int options)		/* I - Options */
+_httpTLSSetOptions(int options,		/* I - Options */
+                   int min_version,	/* I - Minimum TLS version */
+                   int max_version)	/* I - Maximum TLS version */
 {
-  tls_options = options;
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+  {
+    tls_options     = options;
+    tls_min_version = min_version;
+    tls_max_version = max_version;
+  }
 }
 
 
@@ -1242,8 +1248,21 @@
   int			status;		/* Status of handshake */
   gnutls_certificate_credentials_t *credentials;
 					/* TLS credentials */
-  char			priority_string[1024];
+  char			priority_string[2048];
 					/* Priority string */
+  int			version;	/* Current version */
+  double		old_timeout;	/* Old timeout value */
+  http_timeout_cb_t	old_cb;		/* Old timeout callback */
+  void			*old_data;	/* Old timeout data */
+  static const char * const versions[] =/* SSL/TLS versions */
+  {
+    "VERS-SSL3.0",
+    "VERS-TLS1.0",
+    "VERS-TLS1.1",
+    "VERS-TLS1.2",
+    "VERS-TLS1.3",
+    "VERS-TLS-ALL"
+  };
 
 
   DEBUG_printf(("3_httpTLSStart(http=%p)", http));
@@ -1505,18 +1524,50 @@
 
   strlcpy(priority_string, "NORMAL", sizeof(priority_string));
 
-  if (tls_options & _HTTP_TLS_DENY_TLS10)
-    strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-TLS1.0:-VERS-SSL3.0", sizeof(priority_string));
-  else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
-    strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
+  if (tls_max_version < _HTTP_TLS_MAX)
+  {
+   /*
+    * Require specific TLS versions...
+    */
+
+    strlcat(priority_string, ":-VERS-TLS-ALL", sizeof(priority_string));
+    for (version = tls_min_version; version <= tls_max_version; version ++)
+    {
+      strlcat(priority_string, ":+", sizeof(priority_string));
+      strlcat(priority_string, versions[version], sizeof(priority_string));
+    }
+  }
+  else if (tls_min_version == _HTTP_TLS_SSL3)
+  {
+   /*
+    * Allow all versions of TLS and SSL/3.0...
+    */
+
+    strlcat(priority_string, ":+VERS-TLS-ALL:+VERS-SSL3.0", sizeof(priority_string));
+  }
   else
-    strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-SSL3.0", sizeof(priority_string));
+  {
+   /*
+    * Require a minimum version...
+    */
 
-  if (!(tls_options & _HTTP_TLS_ALLOW_RC4))
-    strlcat(priority_string, ":-ARCFOUR-128", sizeof(priority_string));
+    strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
+    for (version = 0; version < tls_min_version; version ++)
+    {
+      strlcat(priority_string, ":-", sizeof(priority_string));
+      strlcat(priority_string, versions[version], sizeof(priority_string));
+    }
+  }
 
-  if (!(tls_options & _HTTP_TLS_ALLOW_DH))
-    strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
+  if (tls_options & _HTTP_TLS_ALLOW_RC4)
+    strlcat(priority_string, ":+ARCFOUR-128", sizeof(priority_string));
+  else
+    strlcat(priority_string, ":!ARCFOUR-128", sizeof(priority_string));
+
+  strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
+
+  if (tls_options & _HTTP_TLS_DENY_CBC)
+    strlcat(priority_string, ":!AES-128-CBC:!AES-256-CBC:!CAMELLIA-128-CBC:!CAMELLIA-256-CBC:!3DES-CBC", sizeof(priority_string));
 
 #ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
   gnutls_priority_set_direct(http->tls, priority_string, NULL);
@@ -1536,6 +1587,24 @@
 #endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */
   gnutls_transport_set_push_function(http->tls, http_gnutls_write);
 
+ /*
+  * Enforce a minimum timeout of 10 seconds for the TLS handshake...
+  */
+
+  old_timeout  = http->timeout_value;
+  old_cb       = http->timeout_cb;
+  old_data     = http->timeout_data;
+
+  if (!old_cb || old_timeout < 10.0)
+  {
+    DEBUG_puts("4_httpTLSStart: Setting timeout to 10 seconds.");
+    httpSetTimeout(http, 10.0, NULL, NULL);
+  }
+
+ /*
+  * Do the TLS handshake...
+  */
+
   while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
   {
     DEBUG_printf(("5_httpStartTLS: gnutls_handshake returned %d (%s)",
@@ -1553,10 +1622,18 @@
       free(credentials);
       http->tls = NULL;
 
+      httpSetTimeout(http, old_timeout, old_cb, old_data);
+
       return (-1);
     }
   }
 
+ /*
+  * Restore the previous timeout settings...
+  */
+
+  httpSetTimeout(http, old_timeout, old_cb, old_data);
+
   http->tls_credentials = credentials;
 
   return (0);
diff --git a/cups/tls-sspi.c b/cups/tls-sspi.c
index 46f6e79..ea3fb00 100644
--- a/cups/tls-sspi.c
+++ b/cups/tls-sspi.c
@@ -2,7 +2,7 @@
  * TLS support for CUPS on Windows using the Security Support Provider
  * Interface (SSPI).
  *
- * Copyright 2010-2015 by Apple Inc.
+ * Copyright 2010-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -52,7 +52,9 @@
  * Local globals...
  */
 
-static int		tls_options = -1;/* Options for TLS connections */
+static int		tls_options = -1,/* Options for TLS connections */
+			tls_min_version = _HTTP_TLS_1_0,
+			tls_max_version = _HTTP_TLS_MAX;
 
 
 /*
@@ -351,7 +353,6 @@
     SYSTEMTIME		systime;	/* System time */
     struct tm		tm;		/* UNIX date/time */
     time_t		expiration;	/* Expiration date of cert */
-    _cups_md5_state_t	md5_state;	/* MD5 state */
     unsigned char	md5_digest[16];	/* MD5 result */
 
     FileTimeToSystemTime(&(cert->pCertInfo->NotAfter), &systime);
@@ -378,9 +379,7 @@
     else
       strlcpy(cert_name, "unknown", sizeof(cert_name));
 
-    _cupsMD5Init(&md5_state);
-    _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
-    _cupsMD5Finish(&md5_state, md5_digest);
+    cupsHashData("md5", first->data, first->datalen, md5_digest, sizeof(md5_digest));
 
     snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", cert_name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
 
@@ -911,9 +910,16 @@
  */
 
 void
-_httpTLSSetOptions(int options)		/* I - Options */
+_httpTLSSetOptions(int options,		/* I - Options */
+                   int min_version,	/* I - Minimum TLS version */
+                   int max_version)	/* I - Maximum TLS version */
 {
-  tls_options = options;
+  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+  {
+    tls_options     = options;
+    tls_min_version = min_version;
+    tls_max_version = max_version;
+  }
 }
 
 
@@ -1761,41 +1767,45 @@
 #ifdef SP_PROT_TLS1_2_SERVER
   if (http->mode == _HTTP_MODE_SERVER)
   {
-    if (tls_options & _HTTP_TLS_DENY_TLS10)
-      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
-    else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
+    if (tls_min_version == _HTTP_TLS_SSL3)
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER | SP_PROT_SSL3_SERVER;
-    else
+    else if (tls_min_version == _HTTP_TLS_1_0)
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_0_SERVER;
+    else if (tls_min_version == _HTTP_TLS_1_1)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER | SP_PROT_TLS1_1_SERVER;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER;
   }
   else
   {
-    if (tls_options & _HTTP_TLS_DENY_TLS10)
-      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
-    else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
+    if (tls_min_version == _HTTP_TLS_SSL3)
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_SSL3_CLIENT;
-    else
+    else if (tls_min_version == _HTTP_TLS_1_0)
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT | SP_PROT_TLS1_0_CLIENT;
+    else if (tls_min_version == _HTTP_TLS_1_1)
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_1_CLIENT;
+    else
+      SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
   }
 
 #else
   if (http->mode == _HTTP_MODE_SERVER)
   {
-    if (tls_options & _HTTP_TLS_ALLOW_SSL3)
+    if (tls_min_version == _HTTP_TLS_SSL3)
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER | SP_PROT_SSL3_SERVER;
     else
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
   }
   else
   {
-    if (tls_options & _HTTP_TLS_ALLOW_SSL3)
+    if (tls_min_version == _HTTP_TLS_SSL3)
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT | SP_PROT_SSL3_CLIENT;
     else
       SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_CLIENT;
   }
 #endif /* SP_PROT_TLS1_2_SERVER */
 
-  /* TODO: Support _HTTP_TLS_ALLOW_RC4 and _HTTP_TLS_ALLOW_DH options; right now we'll rely on Windows registry to enable/disable RC4/DH... */
+  /* TODO: Support _HTTP_TLS_ALLOW_RC4, _HTTP_TLS_ALLOW_DH, and _HTTP_TLS_DENY_CBC options; right now we'll rely on Windows registry to enable/disable RC4/DH/CBC... */
 
  /*
   * Create an SSPI credential.
diff --git a/cups/tls.c b/cups/tls.c
index 36d484c..2539a09 100644
--- a/cups/tls.c
+++ b/cups/tls.c
@@ -23,20 +23,20 @@
 #include "cups-private.h"
 #include <fcntl.h>
 #include <math.h>
-#ifdef WIN32
+#ifdef _WIN32
 #  include <tchar.h>
 #else
 #  include <signal.h>
 #  include <sys/time.h>
 #  include <sys/resource.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 #ifdef HAVE_POLL
 #  include <poll.h>
 #endif /* HAVE_POLL */
 
 
 /*
- * Local functions...
+ * Include platform-specific TLS code...
  */
 
 #ifdef HAVE_SSL
diff --git a/cups/tlscheck.c b/cups/tlscheck.c
index 32cbcca..d16bf1e 100644
--- a/cups/tlscheck.c
+++ b/cups/tlscheck.c
@@ -1,7 +1,7 @@
 /*
  * TLS check program for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -54,6 +54,8 @@
   int		af = AF_UNSPEC,		/* Address family */
 		tls_options = _HTTP_TLS_NONE,
 					/* TLS options */
+		tls_min_version = _HTTP_TLS_1_0,
+		tls_max_version = _HTTP_TLS_MAX,
 		verbose = 0;		/* Verbosity */
   ipp_t		*request,		/* IPP Get-Printer-Attributes request */
 		*response;		/* IPP Get-Printer-Attributes response */
@@ -82,9 +84,33 @@
     {
       tls_options |= _HTTP_TLS_ALLOW_DH;
     }
+    else if (!strcmp(argv[i], "--no-cbc"))
+    {
+      tls_options |= _HTTP_TLS_DENY_CBC;
+    }
     else if (!strcmp(argv[i], "--no-tls10"))
     {
-      tls_options |= _HTTP_TLS_DENY_TLS10;
+      tls_min_version = _HTTP_TLS_1_1;
+    }
+    else if (!strcmp(argv[i], "--tls10"))
+    {
+      tls_min_version = _HTTP_TLS_1_0;
+      tls_max_version = _HTTP_TLS_1_0;
+    }
+    else if (!strcmp(argv[i], "--tls11"))
+    {
+      tls_min_version = _HTTP_TLS_1_1;
+      tls_max_version = _HTTP_TLS_1_1;
+    }
+    else if (!strcmp(argv[i], "--tls12"))
+    {
+      tls_min_version = _HTTP_TLS_1_2;
+      tls_max_version = _HTTP_TLS_1_2;
+    }
+    else if (!strcmp(argv[i], "--tls13"))
+    {
+      tls_min_version = _HTTP_TLS_1_3;
+      tls_max_version = _HTTP_TLS_1_3;
     }
     else if (!strcmp(argv[i], "--rc4"))
     {
@@ -140,7 +166,7 @@
   if (!port)
     port = 631;
 
-  _httpTLSSetOptions(tls_options);
+  _httpTLSSetOptions(tls_options, tls_min_version, tls_max_version);
 
   http = httpConnect2(server, port, NULL, af, HTTP_ENCRYPTION_ALWAYS, 1, 30000, NULL);
   if (!http)
@@ -729,8 +755,13 @@
   puts("");
   puts("Options:");
   puts("  --dh        Allow DH/DHE key exchange");
+  puts("  --no-cbc    Disable CBC cipher suites");
   puts("  --no-tls10  Disable TLS/1.0");
   puts("  --rc4       Allow RC4 encryption");
+  puts("  --tls10     Only use TLS/1.0");
+  puts("  --tls11     Only use TLS/1.1");
+  puts("  --tls12     Only use TLS/1.2");
+  puts("  --tls13     Only use TLS/1.3");
   puts("  --verbose   Be verbose");
   puts("  -4          Connect using IPv4 addresses only");
   puts("  -6          Connect using IPv6 addresses only");
diff --git a/cups/usersys.c b/cups/usersys.c
index 333d21e..781f27b 100644
--- a/cups/usersys.c
+++ b/cups/usersys.c
@@ -1,7 +1,7 @@
 /*
  * User, system, and password routines for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2017 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -20,13 +20,13 @@
 #include "cups-private.h"
 #include <stdlib.h>
 #include <sys/stat.h>
-#ifdef WIN32
+#ifdef _WIN32
 #  include <windows.h>
 #else
 #  include <pwd.h>
 #  include <termios.h>
 #  include <sys/utsname.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
 /*
@@ -54,7 +54,9 @@
 typedef struct _cups_client_conf_s	/**** client.conf config data ****/
 {
 #ifdef HAVE_SSL
-  int			ssl_options;	/* SSLOptions values */
+  int			ssl_options,	/* SSLOptions values */
+			ssl_min_version,/* Minimum SSL/TLS version */
+			ssl_max_version;/* Maximum SSL/TLS version */
 #endif /* HAVE_SSL */
   int			trust_first,	/* Trust on first use? */
 			any_root,	/* Allow any (e.g., self-signed) root */
@@ -132,6 +134,8 @@
  * thread in a program. Multi-threaded programs that override the setting via
  * the @link cupsSetPasswordCB@ or @link cupsSetPasswordCB2@ functions need to
  * do so in each thread for the same function to be used.
+ *
+ * @exclude all@
  */
 
 const char *				/* O - Password */
@@ -145,7 +149,7 @@
 
 
 /*
- * 'cupsGetPassword2()' - Get a password from the user using the advanced
+ * 'cupsGetPassword2()' - Get a password from the user using the current
  *                        password callback.
  *
  * Uses the current password callback function. Returns @code NULL@ if the
@@ -153,8 +157,8 @@
  *
  * Note: The current password callback function is tracked separately for each
  * thread in a program. Multi-threaded programs that override the setting via
- * the @link cupsSetPasswordCB@ or @link cupsSetPasswordCB2@ functions need to
- * do so in each thread for the same function to be used.
+ * the @link cupsSetPasswordCB2@ function need to do so in each thread for the
+ * same function to be used.
  *
  * @since CUPS 1.4/macOS 10.6@
  */
@@ -297,6 +301,8 @@
  * Note: The current password callback is tracked separately for each thread
  * in a program. Multi-threaded programs that override the callback need to do
  * so in each thread for the same callback to be used.
+ *
+ * @exclude all@
  */
 
 void
@@ -484,12 +490,12 @@
 {
   _cups_globals_t	*cg = _cupsGlobals();
 					/* Thread globals */
-#ifdef WIN32
+#ifdef _WIN32
   SYSTEM_INFO		sysinfo;	/* System information */
   OSVERSIONINFO		version;	/* OS version info */
 #else
   struct utsname	name;		/* uname info */
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 
   if (user_agent)
@@ -498,7 +504,7 @@
     return;
   }
 
-#ifdef WIN32
+#ifdef _WIN32
   version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
   GetVersionEx(&version);
   GetNativeSystemInfo(&sysinfo);
@@ -522,7 +528,7 @@
   snprintf(cg->user_agent, sizeof(cg->user_agent),
            CUPS_MINIMAL " (%s %s; %s) IPP/2.0",
 	   name.sysname, name.release, name.machine);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -574,7 +580,7 @@
 const char *				/* O - Password or @code NULL@ if none */
 _cupsGetPassword(const char *prompt)	/* I - Prompt string */
 {
-#ifdef WIN32
+#ifdef _WIN32
   HANDLE		tty;		/* Console handle */
   DWORD			mode;		/* Console mode */
   char			passch,		/* Current key press */
@@ -840,7 +846,7 @@
     memset(cg->password, 0, sizeof(cg->password));
     return (NULL);
   }
-#endif /* WIN32 */
+#endif /* _WIN32 */
 }
 
 
@@ -899,7 +905,7 @@
 
 #  ifdef HAVE_GETEUID
   if ((geteuid() == getuid() || !getuid()) && getegid() == getgid() && (home = getenv("HOME")) != NULL)
-#  elif !defined(WIN32)
+#  elif !defined(_WIN32)
   if (getuid() && (home = getenv("HOME")) != NULL)
 #  else
   if ((home = getenv("HOME")) != NULL)
@@ -953,7 +959,7 @@
     cg->validate_certs = cc.validate_certs;
 
 #ifdef HAVE_SSL
-  _httpTLSSetOptions(cc.ssl_options);
+  _httpTLSSetOptions(cc.ssl_options | _HTTP_TLS_SET_DEFAULT, cc.ssl_min_version, cc.ssl_max_version);
 #endif /* HAVE_SSL */
 }
 
@@ -1095,7 +1101,7 @@
 
   if (!cc->user[0])
   {
-#ifdef WIN32
+#ifdef _WIN32
    /*
     * Get the current user name from the OS...
     */
@@ -1131,7 +1137,7 @@
     if (pw)
       strlcpy(cc->user, pw->pw_name, sizeof(cc->user));
     else
-#endif /* WIN32 */
+#endif /* _WIN32 */
     {
      /*
       * Use the default "unknown" user name...
@@ -1160,18 +1166,22 @@
 
   memset(cc, 0, sizeof(_cups_client_conf_t));
 
-  cc->encryption     = (http_encryption_t)-1;
-  cc->trust_first    = -1;
-  cc->any_root       = -1;
-  cc->expired_certs  = -1;
-  cc->validate_certs = -1;
+#ifdef HAVE_SSL
+  cc->ssl_min_version = _HTTP_TLS_1_0;
+  cc->ssl_max_version = _HTTP_TLS_MAX;
+#endif /* HAVE_SSL */
+  cc->encryption      = (http_encryption_t)-1;
+  cc->trust_first     = -1;
+  cc->any_root        = -1;
+  cc->expired_certs   = -1;
+  cc->validate_certs  = -1;
 
  /*
   * Load settings from the org.cups.PrintingPrefs plist (which trump
   * everything...)
   */
 
-#ifdef __APPLE__
+#if defined(__APPLE__) && defined(HAVE_SSL)
   char	sval[1024];			/* String value */
   int	bval;				/* Boolean value */
 
@@ -1192,7 +1202,7 @@
 
   if (cups_apple_get_boolean(kValidateCertsKey, &bval))
     cc->validate_certs = bval;
-#endif /* __APPLE__ */
+#endif /* __APPLE__ && HAVE_SSL */
 }
 
 
@@ -1332,7 +1342,9 @@
   * SSLOptions [AllowRC4] [AllowSSL3] [AllowDH] [DenyTLS1.0] [None]
   */
 
-  int	options = _HTTP_TLS_NONE;	/* SSL/TLS options */
+  int	options = _HTTP_TLS_NONE,	/* SSL/TLS options */
+	min_version = _HTTP_TLS_1_0,	/* Minimum SSL/TLS version */
+	max_version = _HTTP_TLS_MAX;	/* Maximum SSL/TLS version */
   char	temp[256],			/* Copy of value */
 	*start,				/* Start of option */
 	*end;				/* End of option */
@@ -1360,18 +1372,38 @@
     if (!_cups_strcasecmp(start, "AllowRC4"))
       options |= _HTTP_TLS_ALLOW_RC4;
     else if (!_cups_strcasecmp(start, "AllowSSL3"))
-      options |= _HTTP_TLS_ALLOW_SSL3;
+      min_version = _HTTP_TLS_SSL3;
     else if (!_cups_strcasecmp(start, "AllowDH"))
       options |= _HTTP_TLS_ALLOW_DH;
+    else if (!_cups_strcasecmp(start, "DenyCBC"))
+      options |= _HTTP_TLS_DENY_CBC;
     else if (!_cups_strcasecmp(start, "DenyTLS1.0"))
-      options |= _HTTP_TLS_DENY_TLS10;
+      min_version = _HTTP_TLS_1_1;
+    else if (!_cups_strcasecmp(start, "MaxTLS1.0"))
+      max_version = _HTTP_TLS_1_0;
+    else if (!_cups_strcasecmp(start, "MaxTLS1.1"))
+      max_version = _HTTP_TLS_1_1;
+    else if (!_cups_strcasecmp(start, "MaxTLS1.2"))
+      max_version = _HTTP_TLS_1_2;
+    else if (!_cups_strcasecmp(start, "MaxTLS1.3"))
+      max_version = _HTTP_TLS_1_3;
+    else if (!_cups_strcasecmp(start, "MinTLS1.0"))
+      min_version = _HTTP_TLS_1_0;
+    else if (!_cups_strcasecmp(start, "MinTLS1.1"))
+      min_version = _HTTP_TLS_1_1;
+    else if (!_cups_strcasecmp(start, "MinTLS1.2"))
+      min_version = _HTTP_TLS_1_2;
+    else if (!_cups_strcasecmp(start, "MinTLS1.3"))
+      min_version = _HTTP_TLS_1_3;
     else if (!_cups_strcasecmp(start, "None"))
       options = _HTTP_TLS_NONE;
   }
 
-  cc->ssl_options = options;
+  cc->ssl_options     = options;
+  cc->ssl_max_version = max_version;
+  cc->ssl_min_version = min_version;
 
-  DEBUG_printf(("4cups_set_ssl_options(cc=%p, value=\"%s\") options=%x", (void *)cc, value, options));
+  DEBUG_printf(("4cups_set_ssl_options(cc=%p, value=\"%s\") options=%x, min_version=%d, max_version=%d", (void *)cc, value, options, min_version, max_version));
 }
 #endif /* HAVE_SSL */
 
diff --git a/cups/util.c b/cups/util.c
index 5db2fc6..ef1709c 100644
--- a/cups/util.c
+++ b/cups/util.c
@@ -1,8 +1,8 @@
 /*
  * Printing utilities for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
- * Copyright 1997-2006 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1997-2006 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -20,11 +20,11 @@
 #include "cups-private.h"
 #include <fcntl.h>
 #include <sys/stat.h>
-#if defined(WIN32) || defined(__EMX__)
+#if defined(_WIN32) || defined(__EMX__)
 #  include <io.h>
 #else
 #  include <unistd.h>
-#endif /* WIN32 || __EMX__ */
+#endif /* _WIN32 || __EMX__ */
 
 
 /*
@@ -35,6 +35,8 @@
  *
  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
  * the cause of any failure.
+ *
+ * @exclude all@
  */
 
 int					/* O - 1 on success, 0 on failure */
@@ -58,7 +60,7 @@
  * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
  * the cause of any failure.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 ipp_status_t				/* O - IPP status */
@@ -146,7 +148,7 @@
  * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
  * instead.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -157,12 +159,10 @@
     int           num_options,		/* I - Number of options */
     cups_option_t *options)		/* I - Options */
 {
-  char		printer_uri[1024],	/* Printer URI */
-		resource[1024];		/* Printer resource */
-  ipp_t		*request,		/* Create-Job request */
-		*response;		/* Create-Job response */
-  ipp_attribute_t *attr;		/* job-id attribute */
   int		job_id = 0;		/* job-id value */
+  ipp_status_t  status;                 /* Create-Job status */
+  cups_dest_t	*dest;			/* Destination */
+  cups_dinfo_t  *info;                  /* Destination information */
 
 
   DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", num_options=%d, options=%p)", (void *)http, name, title, num_options, (void *)options));
@@ -178,46 +178,42 @@
   }
 
  /*
-  * Build a Create-Job request...
+  * Lookup the destination...
   */
 
-  if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
+  if ((dest = cupsGetNamedDest(http, name, NULL)) == NULL)
   {
-    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
+    DEBUG_puts("1cupsCreateJob: Destination not found.");
+    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOENT), 0);
     return (0);
   }
 
-  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
-                   NULL, "localhost", ippPort(), "/printers/%s", name);
-  snprintf(resource, sizeof(resource), "/printers/%s", name);
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, printer_uri);
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-               NULL, cupsUser());
-  if (title)
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
-                 title);
-  cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
-  cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
-  cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
-
  /*
-  * Send the request and get the job-id...
+  * Query dest information and create the job...
   */
 
-  response = cupsDoRequest(http, request, resource);
+  DEBUG_puts("1cupsCreateJob: Querying destination info.");
+  if ((info = cupsCopyDestInfo(http, dest)) == NULL)
+  {
+    DEBUG_puts("1cupsCreateJob: Query failed.");
+    cupsFreeDests(1, dest);
+    return (0);
+  }
 
-  if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
-    job_id = attr->values[0].integer;
+  status = cupsCreateDestJob(http, dest, info, &job_id, title, num_options, options);
+  DEBUG_printf(("1cupsCreateJob: cupsCreateDestJob returned %04x (%s)", status, ippErrorString(status)));
 
-  ippDelete(response);
+  cupsFreeDestInfo(info);
+  cupsFreeDests(1, dest);
 
  /*
-  * Return it...
+  * Return the job...
   */
 
-  return (job_id);
+  if (status >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+    return (0);
+  else
+    return (job_id);
 }
 
 
@@ -226,7 +222,7 @@
  *
  * The document must have been started using @link cupsStartDocument@.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 ipp_status_t				/* O - Status of document submission */
@@ -277,7 +273,7 @@
  * This function is deprecated and no longer returns a list of printer
  * classes - use @link cupsGetDests@ instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of classes */
@@ -299,6 +295,8 @@
  * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
  * functions to get the user-defined default printer, as this function does
  * not support the lpoptions-defined default printer.
+ *
+ * @exclude all@
  */
 
 const char *				/* O - Default printer or @code NULL@ */
@@ -322,7 +320,7 @@
  * functions to get the user-defined default printer, as this function does
  * not support the lpoptions-defined default printer.
  *
- * @since CUPS 1.1.21/macOS 10.4@
+ * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
  */
 
 const char *				/* O - Default printer or @code NULL@ */
@@ -388,6 +386,8 @@
  * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
  * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
  * jobs that are stopped, canceled, aborted, or completed.
+ *
+ * @exclude all@
  */
 
 int					/* O - Number of jobs */
@@ -683,7 +683,7 @@
  * This function is deprecated and no longer returns a list of printers - use
  * @link cupsGetDests@ instead.
  *
- * @deprecated@
+ * @deprecated@ @exclude all@
  */
 
 int					/* O - Number of printers */
@@ -698,6 +698,8 @@
 
 /*
  * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
+ *
+ * @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -718,7 +720,7 @@
  * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
  *                      server.
  *
- * @since CUPS 1.1.21/macOS 10.4@
+ * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -740,6 +742,8 @@
 /*
  * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
  *                      default server.
+ *
+ * @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -766,7 +770,7 @@
  * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
  *                       specified server.
  *
- * @since CUPS 1.1.21/macOS 10.4@
+ * @since CUPS 1.1.21/macOS 10.4@ @exclude all@
  */
 
 int					/* O - Job ID or 0 on error */
@@ -896,7 +900,7 @@
  * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
  * any supported MIME type string can be supplied.
  *
- * @since CUPS 1.4/macOS 10.6@
+ * @since CUPS 1.4/macOS 10.6@ @exclude all@
  */
 
 http_status_t				/* O - HTTP status of request */
@@ -951,3 +955,4 @@
 
   return (status);
 }
+
diff --git a/cups/versioning.h b/cups/versioning.h
index 2e92e6b..620396f 100644
--- a/cups/versioning.h
+++ b/cups/versioning.h
@@ -1,7 +1,7 @@
 /*
  * API versioning definitions for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright © 2007-2018 by Apple Inc.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -16,79 +16,41 @@
 #  define _CUPS_VERSIONING_H_
 
 /*
- * This header defines several constants - _CUPS_DEPRECATED,
- * _CUPS_DEPRECATED_MSG, _CUPS_INTERNAL_MSG, _CUPS_API_1_1, _CUPS_API_1_1_19,
- * _CUPS_API_1_1_20, _CUPS_API_1_1_21, _CUPS_API_1_2, _CUPS_API_1_3,
- * _CUPS_API_1_4, _CUPS_API_1_5, _CUPS_API_1_6, _CUPS_API_1_7, and
- * _CUPS_API_2_0 - which add compiler-specific attributes that flag functions
- * that are deprecated, added in particular releases, or internal to CUPS.
+ * This header defines several macros that add compiler-specific attributes for
+ * functions:
  *
- * On macOS, the _CUPS_API_* constants are defined based on the values of
- * the MAC_OS_X_VERSION_MIN_ALLOWED and MAC_OS_X_VERSION_MAX_ALLOWED constants
- * provided by the compiler.
+ *   - _CUPS_API_major_minor[_patch]: Specifies when an API became available by
+ *     CUPS version.
+ *   - _CUPS_DEPRECATED: Function is deprecated with no replacement.
+ *   - _CUPS_DEPRECATED_MSG("message"): Function is deprecated and has a
+ *     replacement.
+ *   - _CUPS_FORMAT(format-index, additional-args-index): Function has a
+ *     printf-style format argument followed by zero or more additional
+ *     arguments.  Indices start at 1.
+ *   - _CUPS_INTERNAL: Function is internal with no replacement API.
+ *   - _CUPS_INTERNAL_MSG("msg"): Function is internal - use specified API
+ *     instead.
+ *   - _CUPS_NONNULL((arg list)): Specifies the comma-separated argument indices
+ *     are assumed non-NULL.  Indices start at 1.
+ *   - _CUPS_NORETURN: Specifies the function does not return.
+ *   - _CUPS_PRIVATE: Specifies the function is private to CUPS.
+ *   - _CUPS_PUBLIC: Specifies the function is public API.
  */
 
-#  if defined(__APPLE__) && !defined(_CUPS_SOURCE) && !TARGET_OS_IOS
-#    include <AvailabilityMacros.h>
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_10_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_10_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_10_AND_LATER */
-#    ifndef AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
-#      define AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER __attribute__((unavailable))
-#    endif /* !AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER */
-#    define _CUPS_API_1_1_19 AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
-#    define _CUPS_API_1_1_20 AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER
-#    define _CUPS_API_1_1_21 AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER
-#    define _CUPS_API_1_2 AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
-#    define _CUPS_API_1_3 AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER
-#    define _CUPS_API_1_4 AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER
-#    define _CUPS_API_1_5 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER
-#    define _CUPS_API_1_6 AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER
-#    define _CUPS_API_1_7 AVAILABLE_MAC_OS_X_VERSION_10_9_AND_LATER
-#    define _CUPS_API_2_0 AVAILABLE_MAC_OS_X_VERSION_10_10_AND_LATER
-#    define _CUPS_API_2_2 AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER
-#  else
-#    define _CUPS_API_1_1_19
-#    define _CUPS_API_1_1_20
-#    define _CUPS_API_1_1_21
-#    define _CUPS_API_1_2
-#    define _CUPS_API_1_3
-#    define _CUPS_API_1_4
-#    define _CUPS_API_1_5
-#    define _CUPS_API_1_6
-#    define _CUPS_API_1_7
-#    define _CUPS_API_2_0
-#    define _CUPS_API_2_2
-#  endif /* __APPLE__ && !_CUPS_SOURCE */
-
 /*
- * With GCC and Clang we can mark old APIs as "deprecated" or "unavailable" with
- * messages so you get warnings/errors are compile-time...
+ * Determine which compiler is being used and what annotation features are
+ * available...
  */
 
+#  ifdef __APPLE__
+#    include <os/availability.h>
+#  endif /* __APPLE__ */
+
 #  ifdef __has_extension		/* Clang */
 #    define _CUPS_HAS_DEPRECATED
+#    define _CUPS_HAS_FORMAT
+#    define _CUPS_HAS_NORETURN
+#    define _CUPS_HAS_VISIBILITY
 #    if __has_extension(attribute_deprecated_with_message)
 #      define _CUPS_HAS_DEPRECATED_WITH_MESSAGE
 #    endif
@@ -98,6 +60,9 @@
 #  elif defined(__GNUC__)		/* GCC and compatible */
 #    if __GNUC__ >= 3			/* GCC 3.0 or higher */
 #      define _CUPS_HAS_DEPRECATED
+#      define _CUPS_HAS_FORMAT
+#      define _CUPS_HAS_NORETURN
+#      define _CUPS_HAS_VISIBILITY
 #    endif /* __GNUC__ >= 3 */
 #    if __GNUC__ >= 5			/* GCC 5.x */
 #      define _CUPS_HAS_DEPRECATED_WITH_MESSAGE
@@ -105,61 +70,197 @@
 					/* GCC 4.5 or higher */
 #      define _CUPS_HAS_DEPRECATED_WITH_MESSAGE
 #    endif /* __GNUC__ >= 5 */
+#  elif defined(_WIN32)
+#    define __attribute__(...)
 #  endif /* __has_extension */
 
+
+/*
+ * Define _CUPS_INTERNAL, _CUPS_PRIVATE, and _CUPS_PUBLIC visibilty macros for
+ * internal/private/public functions...
+ */
+
+#  ifdef _CUPS_HAS_VISIBILITY
+#    define _CUPS_INTERNAL	__attribute__ ((visibility("hidden")))
+#    define _CUPS_PRIVATE	__attribute__ ((visibility("default")))
+#    define _CUPS_PUBLIC	__attribute__ ((visibility("default")))
+#  else
+#    define _CUPS_INTERNAL
+#    define _CUPS_PRIVATE
+#    define _CUPS_PUBLIC
+#  endif /* _CUPS_HAS_VISIBILITY */
+
+
+/*
+ * Define _CUPS_API_major_minor[_patch] availability macros for CUPS.
+ *
+ * Note: Using any of the _CUPS_API macros automatically adds _CUPS_PUBLIC.
+ */
+
+#  if defined(__APPLE__) && !defined(_CUPS_SOURCE) && !TARGET_OS_IOS
+/*
+ * On Apple operating systems, the _CUPS_API_* constants are defined using the
+ * API_ macros in <os/availability.h>.
+ *
+ * On iOS, we don't actually have libcups available directly, but the supplied
+ * libcups_static target in the Xcode project supports building on iOS 11.0 and
+ * later.
+ */
+#    define _CUPS_API_1_1_19 API_AVAILABLE(macos(10.3), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_1_20 API_AVAILABLE(macos(10.4), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_1_21 API_AVAILABLE(macos(10.4), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_2 API_AVAILABLE(macos(10.5), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_3 API_AVAILABLE(macos(10.5), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_4 API_AVAILABLE(macos(10.6), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_5 API_AVAILABLE(macos(10.7), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_6 API_AVAILABLE(macos(10.8), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_1_7 API_AVAILABLE(macos(10.9), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_2_0 API_AVAILABLE(macos(10.10), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_2_2 API_AVAILABLE(macos(10.12), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_2_2_4 API_AVAILABLE(macos(10.13), ios(11.0)) _CUPS_PUBLIC
+#    define _CUPS_API_2_2_7 API_AVAILABLE(macos(10.14), ios(11.0)) _CUPS_PUBLIC
+#  else
+#    define _CUPS_API_1_1_19 _CUPS_PUBLIC
+#    define _CUPS_API_1_1_20 _CUPS_PUBLIC
+#    define _CUPS_API_1_1_21 _CUPS_PUBLIC
+#    define _CUPS_API_1_2 _CUPS_PUBLIC
+#    define _CUPS_API_1_3 _CUPS_PUBLIC
+#    define _CUPS_API_1_4 _CUPS_PUBLIC
+#    define _CUPS_API_1_5 _CUPS_PUBLIC
+#    define _CUPS_API_1_6 _CUPS_PUBLIC
+#    define _CUPS_API_1_7 _CUPS_PUBLIC
+#    define _CUPS_API_2_0 _CUPS_PUBLIC
+#    define _CUPS_API_2_2 _CUPS_PUBLIC
+#    define _CUPS_API_2_2_4 _CUPS_PUBLIC
+#    define _CUPS_API_2_2_7 _CUPS_PUBLIC
+#  endif /* __APPLE__ && !_CUPS_SOURCE */
+
+
+/*
+ * Define _CUPS_DEPRECATED and _CUPS_INTERNAL macros to mark old APIs as
+ * "deprecated" or "unavailable" with messages so you get warnings/errors are
+ * compile-time...
+ *
+ * Note: Using any of the _CUPS_DEPRECATED macros automatically adds
+ * _CUPS_PUBLIC.
+ */
+
 #  if !defined(_CUPS_HAS_DEPRECATED) || (defined(_CUPS_SOURCE) && !defined(_CUPS_NO_DEPRECATED))
     /*
      * Don't mark functions deprecated if the compiler doesn't support it
      * or we are building CUPS source that doesn't care.
      */
-#    define _CUPS_DEPRECATED
-#    define _CUPS_DEPRECATED_MSG(m)
-#    define _CUPS_DEPRECATED_1_6_MSG(m)
-#    define _CUPS_DEPRECATED_1_7_MSG(m)
-#    define _CUPS_INTERNAL_MSG(m)
+#    define _CUPS_DEPRECATED _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_MSG(m) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_2_MSG(m) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_6_MSG(m) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_7_MSG(m) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_2_2_MSG(m) _CUPS_PUBLIC
+#  elif defined(__APPLE__) && defined(_CUPS_NO_DEPRECATED)
+    /*
+     * Compiler supports the unavailable attribute, so use it when the code
+     * wants to exclude the use of deprecated API.
+     */
+#    define _CUPS_DEPRECATED __attribute__ ((unavailable)) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_2_MSG(m) API_DEPRECATED(m, macos(10.2,10.5)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_6_MSG(m) API_DEPRECATED(m, macos(10.2,10.8)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_7_MSG(m) API_DEPRECATED(m, macos(10.2,10.9)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_2_2_MSG(m) API_DEPRECATED(m, macos(10.2,10.12)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+
+#  elif defined(__APPLE__)
+    /*
+     * Just mark things as deprecated...
+     */
+#    define _CUPS_DEPRECATED __attribute__ ((deprecated)) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_2_MSG(m) API_DEPRECATED(m, macos(10.2,10.5)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_6_MSG(m) API_DEPRECATED(m, macos(10.2,10.8)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_7_MSG(m) API_DEPRECATED(m, macos(10.2,10.9)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_2_2_MSG(m) API_DEPRECATED(m, macos(10.2,10.12)) API_UNAVAILABLE(ios) _CUPS_PUBLIC
+
 #  elif defined(_CUPS_HAS_UNAVAILABLE_WITH_MESSAGE) && defined(_CUPS_NO_DEPRECATED)
     /*
      * Compiler supports the unavailable attribute, so use it when the code
      * wants to exclude the use of deprecated API.
      */
-#    define _CUPS_DEPRECATED __attribute__ ((unavailable))
-#    define _CUPS_DEPRECATED_MSG(m) __attribute__ ((unavailable(m)))
-#    define _CUPS_DEPRECATED_1_6_MSG(m) __attribute__ ((unavailable(m)))
-#    define _CUPS_DEPRECATED_1_7_MSG(m) __attribute__ ((unavailable(m)))
-#    define _CUPS_INTERNAL_MSG(m) __attribute__ ((unavailable(m)))
+#    define _CUPS_DEPRECATED __attribute__ ((unavailable)) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_2_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_6_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_1_7_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
+#    define _CUPS_DEPRECATED_2_2_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
 #  else
     /*
      * Compiler supports the deprecated attribute, so use it.
      */
-#    define _CUPS_DEPRECATED __attribute__ ((deprecated))
+#    define _CUPS_DEPRECATED __attribute__ ((deprecated)) _CUPS_PUBLIC
 #    ifdef _CUPS_HAS_DEPRECATED_WITH_MESSAGE
-#      define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated(m)))
+#      define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_1_2_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_1_6_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_1_7_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_2_2_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
 #    else
-#      define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated))
+#      define _CUPS_DEPRECATED_MSG(m) __attribute__ ((deprecated)) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_1_2_MSG(m) __attribute__ ((deprecated)) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_1_6_MSG(m) __attribute__ ((deprecated)) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_1_7_MSG(m) __attribute__ ((deprecated)) _CUPS_PUBLIC
+#      define _CUPS_DEPRECATED_2_2_MSG(m) __attribute__ ((deprecated)) _CUPS_PUBLIC
 #    endif /* _CUPS_HAS_DEPRECATED_WITH_MESSAGE */
-#    if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
-#      define _CUPS_DEPRECATED_1_6_MSG(m) _CUPS_DEPRECATED_MSG(m)
-#    else
-#      define _CUPS_DEPRECATED_1_6_MSG(m)
-#    endif /* MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8 */
-#    if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
-#      define _CUPS_DEPRECATED_1_7_MSG(m) _CUPS_DEPRECATED_MSG(m)
-#    else
-#      define _CUPS_DEPRECATED_1_7_MSG(m)
-#    endif /* MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9 */
-#    ifdef _CUPS_SOURCE
-#      define _CUPS_INTERNAL_MSG(m)
-#    elif defined(_CUPS_HAS_UNAVAILABLE_WITH_MESSAGE)
-#      define _CUPS_INTERNAL_MSG(m) __attribute__ ((unavailable(m)))
-#    elif defined(_CUPS_HAS_DEPRECATED_WITH_MESSAGE)
-#      define _CUPS_INTERNAL_MSG(m) __attribute__ ((deprecated(m)))
-#    else
-#      define _CUPS_INTERNAL_MSG(m) __attribute__ ((deprecated))
-#    endif /* _CUPS_SOURCE */
 #  endif /* !_CUPS_HAS_DEPRECATED || (_CUPS_SOURCE && !_CUPS_NO_DEPRECATED) */
 
-#  ifndef __GNUC__
-#    define __attribute__(x)
-#  endif /* !__GNUC__ */
+
+/*
+ * Define _CUPS_FORMAT macro for printf-style functions...
+ */
+
+#  ifdef _CUPS_HAS_FORMAT
+#    define _CUPS_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
+#  else
+#    define _CUPS_FORMAT(a,b)
+#  endif /* _CUPS_HAS_FORMAT */
+
+
+/*
+ * Define _CUPS_INTERNAL_MSG macro for private APIs that have (historical)
+ * public visibility.
+ *
+ * Note: Using the _CUPS_INTERNAL_MSG macro automatically adds _CUPS_PUBLIC.
+ */
+
+#  ifdef _CUPS_SOURCE
+#    define _CUPS_INTERNAL_MSG(m) _CUPS_PUBLIC
+#  elif defined(_CUPS_HAS_UNAVAILABLE_WITH_MESSAGE)
+#    define _CUPS_INTERNAL_MSG(m) __attribute__ ((unavailable(m))) _CUPS_PUBLIC
+#  elif defined(_CUPS_HAS_DEPRECATED_WITH_MESSAGE)
+#    define _CUPS_INTERNAL_MSG(m) __attribute__ ((deprecated(m))) _CUPS_PUBLIC
+#  else
+#    define _CUPS_INTERNAL_MSG(m) __attribute__ ((deprecated)) _CUPS_PUBLIC
+#  endif /* _CUPS_SOURCE */
+
+
+/*
+ * Define _CUPS_NONNULL macro for functions that don't expect non-null
+ * arguments...
+ */
+
+#  ifdef _CUPS_HAS_NONNULL
+#    define _CUPS_NONNULL(...) __attribute__ ((nonnull(__VA_ARGS__)))
+#  else
+#    define _CUPS_NONNULL(...)
+#  endif /* _CUPS_HAS_FORMAT */
+
+
+/*
+ * Define _CUPS_NORETURN macro for functions that don't return.
+ */
+
+#  ifdef _CUPS_HAS_NORETURN
+#    define _CUPS_NORETURN	__attribute__ ((noreturn))
+#  else
+#    define _CUPS_NORETURN
+#  endif /* _CUPS_HAS_NORETURN */
+
 
 #endif /* !_CUPS_VERSIONING_H_ */
diff --git a/filter/Makefile b/filter/Makefile
index 7da764d..74fd46f 100644
--- a/filter/Makefile
+++ b/filter/Makefile
@@ -1,7 +1,7 @@
 #
 # Filter makefile for CUPS.
 #
-# Copyright 2007-2012 by Apple Inc.
+# Copyright 2007-2017 by Apple Inc.
 # Copyright 1997-2006 by Easy Software Products.
 #
 # These coded instructions, statements, and computer programs are the
@@ -29,6 +29,7 @@
 		libcupsimage.a
 UNITTARGETS =	\
 		rasterbench \
+		testclient \
 		testraster
 TARGETS	=	\
 		$(LIBTARGETS) \
@@ -38,7 +39,7 @@
 OBJS	=	$(IMAGEOBJS) \
 		commandtops.o gziptoany.o common.o pstops.o \
 		rasterbench.o rastertoepson.o rastertohp.o rastertolabel.o \
-		rastertopwg.o testraster.o
+		rastertopwg.o testclient.o testraster.o
 
 
 #
@@ -216,7 +217,8 @@
 
 commandtops:	commandtops.o ../cups/$(LIBCUPS)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ commandtops.o $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ commandtops.o $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -225,7 +227,8 @@
 
 gziptoany:	gziptoany.o ../Makedefs ../cups/$(LIBCUPS)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ gziptoany.o $(LIBZ) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ gziptoany.o $(LIBZ) $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -234,8 +237,9 @@
 
 libcupsimage.so.2 libcupsimage.sl.2:	$(IMAGEOBJS)
 	echo Linking $@...
-	$(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(IMAGEOBJS) $(DSOLIBS) \
+	$(DSO) $(ARCHFLAGS) $(LDFLAGS) $(DSOFLAGS) -o $@ $(IMAGEOBJS) $(DSOLIBS) \
 		-L../cups $(LINKCUPS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	$(RM) `basename $@ .2`
 	$(LN) $@ `basename $@ .2`
 
@@ -246,34 +250,23 @@
 
 libcupsimage.2.dylib:	$(IMAGEOBJS) $(LIBCUPSIMAGEORDER)
 	echo Linking $@...
-	$(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ \
+	$(DSO) $(ARCHFLAGS) $(LDFLAGS) $(DSOFLAGS) -o $@ \
 		-install_name $(libdir)/$@ \
 		-current_version 2.3.0 \
 		-compatibility_version 2.0.0 \
 		$(IMAGEOBJS) $(DSOLIBS) -L../cups $(LINKCUPS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	$(RM) libcupsimage.dylib
 	$(LN) $@ libcupsimage.dylib
 
 
 #
-# libcupsimage_s.a
-#
-
-libcupsimage_s.a:	$(IMAGEOBJS) libcupsimage_s.exp
-	echo Linking $@...
-	$(DSO) $(DSOFLAGS) -Wl,-berok,-bexport:libcupsimage_s.exp \
-		-o libcupsimage_s.o $(IMAGEOBJS) $(DSOLIBS)
-	$(RM) $@
-	$(AR) $(ARFLAGS) $@ libcupsimage_s.o
-
-
-#
 # libcupsimage.la
 #
 
 libcupsimage.la:       $(IMAGEOBJS)
 	echo Linking $@...
-	$(DSO) $(ARCHFLAGS) $(DSOFLAGS) -o $@ $(IMAGEOBJS:.o=.lo) $(DSOLIBS) \
+	$(DSO) $(ARCHFLAGS) $(LDFLAGS) $(DSOFLAGS) -o $@ $(IMAGEOBJS:.o=.lo) $(DSOLIBS) \
 		-L../cups $(LINKCUPS) \
 		-rpath $(LIBDIR) -version-info 2:3
 
@@ -295,7 +288,8 @@
 
 pstops:	pstops.o common.o ../cups/$(LIBCUPS)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ pstops.o common.o $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -304,7 +298,8 @@
 
 rastertoepson:	rastertoepson.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertoepson.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertoepson.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -313,7 +308,8 @@
 
 rastertohp:	rastertohp.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertohp.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertohp.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -322,7 +318,8 @@
 
 rastertolabel:	rastertolabel.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertolabel.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertolabel.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -331,13 +328,27 @@
 
 rastertopwg:	rastertopwg.o ../cups/$(LIBCUPS) $(LIBCUPSIMAGE)
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertopwg.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rastertopwg.o $(LINKCUPSIMAGE) $(IMGLIBS) $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 rastertopwg-static:	rastertopwg.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rastertopwg.o libcupsimage.a \
+	$(LD_CC) $(LDFLAGS) -o $@ rastertopwg.o libcupsimage.a \
 		../cups/$(LIBCUPSSTATIC) $(IMGLIBS) $(DSOLIBS) $(COMMONLIBS) \
 		$(SSLLIBS) $(DNSSDLIBS) $(LIBGSSAPI)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
+
+
+#
+# testclient (dependency on static libraries is intentional)
+#
+
+testclient:	testclient.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
+	echo Linking $@...
+	$(LD_CC) $(LDFLAGS) -o $@ testclient.o \
+		libcupsimage.a ../cups/$(LIBCUPSSTATIC) \
+		$(LIBGSSAPI) $(SSLLIBS) $(DNSSDLIBS) $(COMMONLIBS) $(LIBZ)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
@@ -346,9 +357,10 @@
 
 testraster:	testraster.o ../cups/$(LIBCUPSSTATIC) libcupsimage.a
 	echo Linking $@...
-	$(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testraster.o libcupsimage.a \
+	$(LD_CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testraster.o libcupsimage.a \
 		../cups/$(LIBCUPSSTATIC) $(IMGLIBS) $(DSOLIBS) $(COMMONLIBS) \
 		$(SSLLIBS) $(DNSSDLIBS) $(LIBGSSAPI)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 	echo Running raster API tests...
 	./testraster
 
@@ -359,7 +371,8 @@
 
 rasterbench:	rasterbench.o libcupsimage.a
 	echo Linking $@...
-	$(CC) $(LDFLAGS) -o $@ rasterbench.o libcupsimage.a $(LIBS)
+	$(LD_CC) $(LDFLAGS) -o $@ rasterbench.o libcupsimage.a $(LIBS)
+	$(CODE_SIGN) -s "$(CODE_SIGN_IDENTITY)" $@
 
 
 #
diff --git a/filter/pstops.c b/filter/pstops.c
index 072356e..fab60d9 100644
--- a/filter/pstops.c
+++ b/filter/pstops.c
@@ -1,8 +1,8 @@
 /*
  * PostScript filter for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
- * Copyright 1993-2007 by Easy Software Products.
+ * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 1993-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
@@ -162,8 +162,7 @@
 				     ssize_t linelen, size_t linesize);
 static void		do_prolog(pstops_doc_t *doc, ppd_file_t *ppd);
 static void 		do_setup(pstops_doc_t *doc, ppd_file_t *ppd);
-static void		doc_printf(pstops_doc_t *doc, const char *format, ...)
-			__attribute__ ((__format__ (__printf__, 2, 3)));
+static void		doc_printf(pstops_doc_t *doc, const char *format, ...) _CUPS_FORMAT(2, 3);
 static void		doc_puts(pstops_doc_t *doc, const char *s);
 static void		doc_write(pstops_doc_t *doc, const char *s, size_t len);
 static void		end_nup(pstops_doc_t *doc, int number);
@@ -2235,7 +2234,7 @@
   bufptr = buffer;
   bufend = buffer + bufsize - 1;
 
-  while (bufptr < bufend)
+  while (*start && bufptr < bufend)
   {
     if (isspace(*start & 255) && !level)
       break;
diff --git a/filter/raster.c b/filter/raster.c
index bacf5ba..3ff0300 100644
--- a/filter/raster.c
+++ b/filter/raster.c
@@ -1,7 +1,7 @@
 /*
  * Raster file routines for CUPS.
  *
- * Copyright 2007-2016 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products.
  *
  * This file is part of the CUPS Imaging library.
@@ -50,11 +50,96 @@
 			*bufend;	/* End of current (read) buffer */
   size_t		bufsize;	/* Buffer size */
 #ifdef DEBUG
-  size_t		iocount;	/* Number of bytes read/written */
+  size_t		iostart,	/* Start of read/write buffer */
+			iocount;	/* Number of bytes read/written */
 #endif /* DEBUG */
   unsigned		apple_page_count;/* Apple raster page count */
 };
 
+typedef void (*_cups_copyfunc_t)(void *dst, const void *src, size_t bytes);
+
+
+/*
+ * Local globals...
+ */
+
+#ifdef DEBUG
+  static const char * const cups_color_spaces[] =
+  {					/* Color spaces */
+    "CUPS_CSPACE_W",
+    "CUPS_CSPACE_RGB",
+    "CUPS_CSPACE_RGBA",
+    "CUPS_CSPACE_K",
+    "CUPS_CSPACE_CMY",
+    "CUPS_CSPACE_YMC",
+    "CUPS_CSPACE_CMYK",
+    "CUPS_CSPACE_YMCK",
+    "CUPS_CSPACE_KCMY",
+    "CUPS_CSPACE_KCMYcm",
+    "CUPS_CSPACE_GMCK",
+    "CUPS_CSPACE_GMCS",
+    "CUPS_CSPACE_WHITE",
+    "CUPS_CSPACE_GOLD",
+    "CUPS_CSPACE_SILVER",
+    "CUPS_CSPACE_CIEXYZ",
+    "CUPS_CSPACE_CIELab",
+    "CUPS_CSPACE_RGBW",
+    "CUPS_CSPACE_SW",
+    "CUPS_CSPACE_SRGB",
+    "CUPS_CSPACE_ADOBERGB",
+    "21",
+    "22",
+    "23",
+    "24",
+    "25",
+    "26",
+    "27",
+    "28",
+    "29",
+    "30",
+    "31",
+    "CUPS_CSPACE_ICC1",
+    "CUPS_CSPACE_ICC2",
+    "CUPS_CSPACE_ICC3",
+    "CUPS_CSPACE_ICC4",
+    "CUPS_CSPACE_ICC5",
+    "CUPS_CSPACE_ICC6",
+    "CUPS_CSPACE_ICC7",
+    "CUPS_CSPACE_ICC8",
+    "CUPS_CSPACE_ICC9",
+    "CUPS_CSPACE_ICCA",
+    "CUPS_CSPACE_ICCB",
+    "CUPS_CSPACE_ICCC",
+    "CUPS_CSPACE_ICCD",
+    "CUPS_CSPACE_ICCE",
+    "CUPS_CSPACE_ICCF",
+    "47",
+    "CUPS_CSPACE_DEVICE1",
+    "CUPS_CSPACE_DEVICE2",
+    "CUPS_CSPACE_DEVICE3",
+    "CUPS_CSPACE_DEVICE4",
+    "CUPS_CSPACE_DEVICE5",
+    "CUPS_CSPACE_DEVICE6",
+    "CUPS_CSPACE_DEVICE7",
+    "CUPS_CSPACE_DEVICE8",
+    "CUPS_CSPACE_DEVICE9",
+    "CUPS_CSPACE_DEVICEA",
+    "CUPS_CSPACE_DEVICEB",
+    "CUPS_CSPACE_DEVICEC",
+    "CUPS_CSPACE_DEVICED",
+    "CUPS_CSPACE_DEVICEE",
+    "CUPS_CSPACE_DEVICEF"
+  };
+  static const char * const cups_modes[] =
+  {					/* Open modes */
+    "CUPS_RASTER_READ",
+    "CUPS_RASTER_WRITE",
+    "CUPS_RASTER_WRITE_COMPRESSED",
+    "CUPS_RASTER_WRITE_PWG",
+    "CUPS_RASTER_WRITE_APPLE"
+  };
+#endif /* DEBUG */
+
 
 /*
  * Local functions...
@@ -62,13 +147,12 @@
 
 static ssize_t	cups_raster_io(cups_raster_t *r, unsigned char *buf, size_t bytes);
 static unsigned	cups_raster_read_header(cups_raster_t *r);
-static ssize_t	cups_raster_read(cups_raster_t *r, unsigned char *buf,
-		                 size_t bytes);
+static ssize_t	cups_raster_read(cups_raster_t *r, unsigned char *buf, size_t bytes);
 static int	cups_raster_update(cups_raster_t *r);
-static ssize_t	cups_raster_write(cups_raster_t *r,
-		                  const unsigned char *pixels);
+static ssize_t	cups_raster_write(cups_raster_t *r, const unsigned char *pixels);
 static ssize_t	cups_read_fd(void *ctx, unsigned char *buf, size_t bytes);
 static void	cups_swap(unsigned char *buf, size_t bytes);
+static void	cups_swap_copy(unsigned char *dst, const unsigned char *src, size_t bytes);
 static ssize_t	cups_write_fd(void *ctx, unsigned char *buf, size_t bytes);
 
 
@@ -356,6 +440,8 @@
 					       @code CUPS_RASTER_WRITE_COMPRESSED@,
 					       or @code CUPS_RASTER_WRITE_PWG@ */
 {
+  DEBUG_printf(("cupsRasterOpen(fd=%d, mode=%s)", fd, cups_modes[mode]));
+
   if (mode == CUPS_RASTER_READ)
     return (cupsRasterOpenIO(cups_read_fd, (void *)((intptr_t)fd), mode));
   else
@@ -387,12 +473,15 @@
   cups_raster_t	*r;			/* New stream */
 
 
+  DEBUG_printf(("cupsRasterOpenIO(iocb=%p, ctx=%p, mode=%s)", (void *)iocb, ctx, cups_modes[mode]));
+
   _cupsRasterClearError();
 
   if ((r = calloc(sizeof(cups_raster_t), 1)) == NULL)
   {
     _cupsRasterAddError("Unable to allocate memory for raster stream: %s\n",
                         strerror(errno));
+    DEBUG_puts("1cupsRasterOpenIO: Returning NULL.");
     return (NULL);
   }
 
@@ -412,6 +501,7 @@
       _cupsRasterAddError("Unable to read header from raster stream: %s\n",
                           strerror(errno));
       free(r);
+      DEBUG_puts("1cupsRasterOpenIO: Unable to read header, returning NULL.");
       return (NULL);
     }
 
@@ -426,6 +516,7 @@
     {
       _cupsRasterAddError("Unknown raster format %08x!\n", r->sync);
       free(r);
+      DEBUG_puts("1cupsRasterOpenIO: Unknown format, returning NULL.");
       return (NULL);
     }
 
@@ -435,6 +526,8 @@
         r->sync == CUPS_RASTER_REVSYNCapple)
       r->compressed = 1;
 
+    DEBUG_printf(("1cupsRasterOpenIO: sync=%08x", r->sync));
+
     if (r->sync == CUPS_RASTER_REVSYNC ||
         r->sync == CUPS_RASTER_REVSYNCv1 ||
         r->sync == CUPS_RASTER_REVSYNCv2 ||
@@ -452,12 +545,14 @@
 	_cupsRasterAddError("Unable to read header from raster stream: %s\n",
 			    strerror(errno));
 	free(r);
+	DEBUG_puts("1cupsRasterOpenIO: Unable to read header, returning NULL.");
 	return (NULL);
       }
-
     }
 
-    DEBUG_printf(("1cupsRasterOpenIO: r->swapped=%d, r->sync=%08x\n", r->swapped, r->sync));
+#ifdef DEBUG
+    r->iostart = r->iocount;
+#endif /* DEBUG */
   }
   else
   {
@@ -496,10 +591,13 @@
       _cupsRasterAddError("Unable to write raster stream header: %s\n",
                           strerror(errno));
       free(r);
+      DEBUG_puts("1cupsRasterOpenIO: Unable to write header, returning NULL.");
       return (NULL);
     }
   }
 
+  DEBUG_printf(("1cupsRasterOpenIO: compressed=%d, swapped=%d, returning %p", r->compressed, r->swapped, (void *)r));
+
   return (r);
 }
 
@@ -522,6 +620,8 @@
     cups_raster_t      *r,		/* I - Raster stream */
     cups_page_header_t *h)		/* I - Pointer to header data */
 {
+  DEBUG_printf(("cupsRasterReadHeader(r=%p, h=%p)", (void *)r, (void *)h));
+
  /*
   * Get the raster header...
   */
@@ -529,6 +629,7 @@
   if (!cups_raster_read_header(r))
   {
     memset(h, 0, sizeof(cups_page_header_t));
+    DEBUG_puts("1cupsRasterReadHeader: Unable to read page header, returning 0.");
     return (0);
   }
 
@@ -538,6 +639,14 @@
 
   memcpy(h, &(r->header), sizeof(cups_page_header_t));
 
+  DEBUG_printf(("1cupsRasterReadHeader: cupsColorSpace=%s", cups_color_spaces[h->cupsColorSpace]));
+  DEBUG_printf(("1cupsRasterReadHeader: cupsBitsPerColor=%u", h->cupsBitsPerColor));
+  DEBUG_printf(("1cupsRasterReadHeader: cupsBitsPerPixel=%u", h->cupsBitsPerPixel));
+  DEBUG_printf(("1cupsRasterReadHeader: cupsBytesPerLine=%u", h->cupsBytesPerLine));
+  DEBUG_printf(("1cupsRasterReadHeader: cupsWidth=%u", h->cupsWidth));
+  DEBUG_printf(("1cupsRasterReadHeader: cupsHeight=%u", h->cupsHeight));
+
+  DEBUG_puts("1cupsRasterReadHeader: Returning 1.");
   return (1);
 }
 
@@ -563,6 +672,7 @@
   if (!cups_raster_read_header(r))
   {
     memset(h, 0, sizeof(cups_page_header2_t));
+    DEBUG_puts("1cupsRasterReadHeader2: Unable to read header, returning 0.");
     return (0);
   }
 
@@ -572,6 +682,14 @@
 
   memcpy(h, &(r->header), sizeof(cups_page_header2_t));
 
+  DEBUG_printf(("1cupsRasterReadHeader2: cupsColorSpace=%s", cups_color_spaces[h->cupsColorSpace]));
+  DEBUG_printf(("1cupsRasterReadHeader2: cupsBitsPerColor=%u", h->cupsBitsPerColor));
+  DEBUG_printf(("1cupsRasterReadHeader2: cupsBitsPerPixel=%u", h->cupsBitsPerPixel));
+  DEBUG_printf(("1cupsRasterReadHeader2: cupsBytesPerLine=%u", h->cupsBytesPerLine));
+  DEBUG_printf(("1cupsRasterReadHeader2: cupsWidth=%u", h->cupsWidth));
+  DEBUG_printf(("1cupsRasterReadHeader2: cupsHeight=%u", h->cupsHeight));
+
+  DEBUG_puts("1cupsRasterReadHeader2: Returning 1.");
   return (1);
 }
 
@@ -777,7 +895,10 @@
            r->header.cupsBitsPerPixel == 12 ||
            r->header.cupsBitsPerPixel == 16) &&
           r->swapped)
-        cups_swap(ptr, (size_t)bytes);
+      {
+        DEBUG_puts("1cupsRasterReadPixels: Swapping bytes.");
+        cups_swap(ptr, (size_t)cupsBytesPerLine);
+      }
 
      /*
       * Update pointers...
@@ -847,8 +968,20 @@
     cups_raster_t      *r,		/* I - Raster stream */
     cups_page_header_t *h)		/* I - Raster page header */
 {
+  DEBUG_printf(("cupsRasterWriteHeader(r=%p, h=%p)", (void *)r, (void *)h));
+
   if (r == NULL || r->mode == CUPS_RASTER_READ)
+  {
+    DEBUG_puts("1cupsRasterWriteHeader: Returning 0.");
     return (0);
+  }
+
+  DEBUG_printf(("1cupsRasterWriteHeader: cupsColorSpace=%s", cups_color_spaces[h->cupsColorSpace]));
+  DEBUG_printf(("1cupsRasterWriteHeader: cupsBitsPerColor=%u", h->cupsBitsPerColor));
+  DEBUG_printf(("1cupsRasterWriteHeader: cupsBitsPerPixel=%u", h->cupsBitsPerPixel));
+  DEBUG_printf(("1cupsRasterWriteHeader: cupsBytesPerLine=%u", h->cupsBytesPerLine));
+  DEBUG_printf(("1cupsRasterWriteHeader: cupsWidth=%u", h->cupsWidth));
+  DEBUG_printf(("1cupsRasterWriteHeader: cupsHeight=%u", h->cupsHeight));
 
  /*
   * Make a copy of the header, and compute the number of raster
@@ -859,7 +992,20 @@
   memcpy(&(r->header), h, sizeof(cups_page_header_t));
 
   if (!cups_raster_update(r))
+  {
+    DEBUG_puts("1cupsRasterWriteHeader: Unable to update parameters, returning 0.");
     return (0);
+  }
+
+  if (r->mode == CUPS_RASTER_WRITE_APPLE)
+  {
+    r->rowheight = h->HWResolution[0] / h->HWResolution[1];
+
+    if (h->HWResolution[0] != (r->rowheight * h->HWResolution[1]))
+      return (0);
+  }
+  else
+    r->rowheight = 1;
 
  /*
   * Write the raster header...
@@ -1019,8 +1165,20 @@
     cups_raster_t       *r,		/* I - Raster stream */
     cups_page_header2_t *h)		/* I - Raster page header */
 {
+  DEBUG_printf(("cupsRasterWriteHeader(r=%p, h=%p)", (void *)r, (void *)h));
+
   if (r == NULL || r->mode == CUPS_RASTER_READ)
+  {
+    DEBUG_puts("1cupsRasterWriteHeader2: Returning 0.");
     return (0);
+  }
+
+  DEBUG_printf(("1cupsRasterWriteHeader2: cupsColorSpace=%s", cups_color_spaces[h->cupsColorSpace]));
+  DEBUG_printf(("1cupsRasterWriteHeader2: cupsBitsPerColor=%u", h->cupsBitsPerColor));
+  DEBUG_printf(("1cupsRasterWriteHeader2: cupsBitsPerPixel=%u", h->cupsBitsPerPixel));
+  DEBUG_printf(("1cupsRasterWriteHeader2: cupsBytesPerLine=%u", h->cupsBytesPerLine));
+  DEBUG_printf(("1cupsRasterWriteHeader2: cupsWidth=%u", h->cupsWidth));
+  DEBUG_printf(("1cupsRasterWriteHeader2: cupsHeight=%u", h->cupsHeight));
 
  /*
   * Make a copy of the header, and compute the number of raster
@@ -1030,7 +1188,10 @@
   memcpy(&(r->header), h, sizeof(cups_page_header2_t));
 
   if (!cups_raster_update(r))
+  {
+    DEBUG_puts("1cupsRasterWriteHeader: Unable to update parameters, returning 0.");
     return (0);
+  }
 
   if (r->mode == CUPS_RASTER_WRITE_APPLE)
   {
@@ -1202,7 +1363,6 @@
          r->header.cupsBitsPerPixel == 16))
     {
       unsigned char	*bufptr;	/* Pointer into write buffer */
-      unsigned		count;		/* Remaining count */
 
      /*
       * Allocate a write buffer as needed...
@@ -1223,21 +1383,10 @@
       }
 
      /*
-      * Byte swap the pixels...
+      * Byte swap the pixels and write them...
       */
 
-      for (bufptr = r->buffer, count = len; count > 1; count -= 2, bufptr += 2)
-      {
-        bufptr[1] = *p++;
-        bufptr[0] = *p++;
-      }
-
-      if (count)			/* This should never happen... */
-        *bufptr = *p;
-
-     /*
-      * Write the byte-swapped buffer...
-      */
+      cups_swap_copy(r->buffer, p, len);
 
       bytes = cups_raster_io(r, r->buffer, len);
     }
@@ -1368,7 +1517,7 @@
   size_t	len;			/* Length for read/swap */
 
 
-  DEBUG_printf(("3cups_raster_read_header(r=%p), r->mode=%d", (void *)r, r ? r->mode : 0));
+  DEBUG_printf(("3cups_raster_read_header(r=%p), r->mode=%s", (void *)r, r ? cups_modes[r->mode] : ""));
 
   if (r == NULL || r->mode != CUPS_RASTER_READ)
     return (0);
@@ -1523,10 +1672,7 @@
 
     DEBUG_printf(("6cups_raster_io: count=%d, total=%d", (int)count, (int)total));
     if (count == 0)
-    {
-      DEBUG_puts("6cups_raster_io: Returning 0.");
-      return (0);
-    }
+      break;
     else if (count < 0)
     {
       DEBUG_puts("6cups_raster_io: Returning -1 on error.");
@@ -1538,6 +1684,7 @@
 #endif /* DEBUG */
   }
 
+  DEBUG_printf(("6cups_raster_io: iocount=" CUPS_LLFMT, CUPS_LLCAST r->iocount));
   DEBUG_printf(("6cups_raster_io: Returning " CUPS_LLFMT ".", CUPS_LLCAST total));
 
   return (total);
@@ -1558,7 +1705,7 @@
 		total;			/* Total bytes read */
 
 
-  DEBUG_printf(("5cups_raster_read(r=%p, buf=%p, bytes=" CUPS_LLFMT ")", (void *)r, (void *)buf, CUPS_LLCAST bytes));
+  DEBUG_printf(("4cups_raster_read(r=%p, buf=%p, bytes=" CUPS_LLFMT "), offset=" CUPS_LLFMT, (void *)r, (void *)buf, CUPS_LLCAST bytes, CUPS_LLCAST (r->iostart + r->bufptr - r->buffer)));
 
   if (!r->compressed)
     return (cups_raster_io(r, buf, bytes));
@@ -1602,7 +1749,7 @@
   {
     count = (ssize_t)bytes - total;
 
-    DEBUG_printf(("6cups_raster_read: count=" CUPS_LLFMT ", remaining=" CUPS_LLFMT ", buf=%p, bufptr=%p, bufend=%p", CUPS_LLCAST count, CUPS_LLCAST remaining, (void *)buf, (void *)r->bufptr, (void *)r->bufend));
+    DEBUG_printf(("5cups_raster_read: count=" CUPS_LLFMT ", remaining=" CUPS_LLFMT ", buf=%p, bufptr=%p, bufend=%p", CUPS_LLCAST count, CUPS_LLCAST remaining, (void *)buf, (void *)r->bufptr, (void *)r->bufend));
 
     if (remaining == 0)
     {
@@ -1612,6 +1759,10 @@
         * Read into the raster buffer and then copy...
 	*/
 
+#ifdef DEBUG
+        r->iostart += (size_t)(r->bufend - r->buffer);
+#endif /* DEBUG */
+
         remaining = (*r->iocb)(r->ctx, r->buffer, r->bufsize);
 	if (remaining <= 0)
 	  return (0);
@@ -1635,6 +1786,7 @@
 	  return (0);
 
 #ifdef DEBUG
+	r->iostart += (size_t)count;
         r->iocount += (size_t)count;
 #endif /* DEBUG */
 
@@ -1687,7 +1839,7 @@
     }
   }
 
-  DEBUG_printf(("6cups_raster_read: Returning %ld", (long)total));
+  DEBUG_printf(("5cups_raster_read: Returning %ld", (long)total));
 
   return (total);
 }
@@ -1850,11 +2002,24 @@
   unsigned char		*wptr;		/* Pointer into write buffer */
   unsigned		bpp,		/* Bytes per pixel */
 			count;		/* Count */
+  _cups_copyfunc_t	cf;		/* Copy function */
 
 
   DEBUG_printf(("3cups_raster_write(r=%p, pixels=%p)", (void *)r, (void *)pixels));
 
  /*
+  * Determine whether we need to swap bytes...
+  */
+
+  if (r->swapped && (r->header.cupsBitsPerColor == 16 || r->header.cupsBitsPerPixel == 12 || r->header.cupsBitsPerPixel == 16))
+  {
+    DEBUG_puts("4cups_raster_write: Swapping bytes when writing.");
+    cf = (_cups_copyfunc_t)cups_swap_copy;
+  }
+  else
+    cf = (_cups_copyfunc_t)memcpy;
+
+  /*
   * Allocate a write buffer as needed...
   */
 
@@ -1905,8 +2070,8 @@
       */
 
       *wptr++ = 0;
-      for (count = bpp; count > 0; count --)
-        *wptr++ = *start++;
+      (*cf)(wptr, start, bpp);
+      wptr += bpp;
     }
     else if (!memcmp(start, ptr, bpp))
     {
@@ -1919,8 +2084,9 @@
 	  break;
 
       *wptr++ = (unsigned char)(count - 1);
-      for (count = bpp; count > 0; count --)
-        *wptr++ = *ptr++;
+      (*cf)(wptr, ptr, bpp);
+      wptr += bpp;
+      ptr  += bpp;
     }
     else
     {
@@ -1941,7 +2107,7 @@
       *wptr++ = (unsigned char)(257 - count);
 
       count *= bpp;
-      memcpy(wptr, start, count);
+      (*cf)(wptr, start, count);
       wptr += count;
     }
   }
@@ -1966,18 +2132,18 @@
   ssize_t	count;			/* Number of bytes read */
 
 
-#ifdef WIN32 /* Sigh */
+#ifdef _WIN32 /* Sigh */
   while ((count = read(fd, buf, (unsigned)bytes)) < 0)
 #else
   while ((count = read(fd, buf, bytes)) < 0)
-#endif /* WIN32 */
+#endif /* _WIN32 */
     if (errno != EINTR && errno != EAGAIN)
     {
-      DEBUG_printf(("4cups_read_fd: %s", strerror(errno)));
+      DEBUG_printf(("8cups_read_fd: %s", strerror(errno)));
       return (-1);
     }
 
-  DEBUG_printf(("4cups_read_fd: Returning %d bytes.", (int)count));
+  DEBUG_printf(("8cups_read_fd: Returning %d bytes.", (int)count));
 
   return (count);
 }
@@ -2010,6 +2176,30 @@
 
 
 /*
+ * 'cups_swap_copy()' - Copy and swap bytes in raster data...
+ */
+
+static void
+cups_swap_copy(
+    unsigned char       *dst,		/* I - Destination */
+    const unsigned char *src,		/* I - Source */
+    size_t              bytes)		/* I - Number of bytes to swap */
+{
+  bytes /= 2;
+
+  while (bytes > 0)
+  {
+    dst[0] = src[1];
+    dst[1] = src[0];
+
+    dst += 2;
+    src += 2;
+    bytes --;
+  }
+}
+
+
+/*
  * 'cups_write_fd()' - Write bytes to a file.
  */
 
@@ -2023,14 +2213,14 @@
   ssize_t	count;			/* Number of bytes written */
 
 
-#ifdef WIN32 /* Sigh */
+#ifdef _WIN32 /* Sigh */
   while ((count = write(fd, buf, (unsigned)bytes)) < 0)
 #else
   while ((count = write(fd, buf, bytes)) < 0)
-#endif /* WIN32 */
+#endif /* _WIN32 */
     if (errno != EINTR && errno != EAGAIN)
     {
-      DEBUG_printf(("4cups_write_fd: %s", strerror(errno)));
+      DEBUG_printf(("8cups_write_fd: %s", strerror(errno)));
       return (-1);
     }
 
diff --git a/filter/rastertoepson.c b/filter/rastertoepson.c
index 4efe669..3de491f 100644
--- a/filter/rastertoepson.c
+++ b/filter/rastertoepson.c
@@ -1,7 +1,7 @@
 /*
  * EPSON ESC/P and ESC/P2 filter for CUPS.
  *
- * Copyright 2007-2015 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1993-2007 by Easy Software Products.
  *
  * These coded instructions, statements, and computer programs are the
@@ -307,7 +307,7 @@
 
   if (DotBytes)
   {
-    if ((LineBuffers[0] = calloc((size_t)DotBytes, header->cupsWidth * (size_t)(Shingling + 1))) == NULL)
+    if ((LineBuffers[0] = calloc((size_t)DotBytes, (header->cupsWidth + 7) * (size_t)(Shingling + 1))) == NULL)
     {
       fputs("ERROR: Unable to allocate memory\n", stderr);
       exit(1);
diff --git a/filter/rastertolabel.c b/filter/rastertolabel.c
index 4e49181..6ad6c62 100644
--- a/filter/rastertolabel.c
+++ b/filter/rastertolabel.c
@@ -305,7 +305,7 @@
 	       header->HWResolution[1], header->cupsHeight,
 	       header->NumCopies);
 	printf("PAGE-WIDTH %u\r\n", header->cupsWidth);
-	printf("PAGE-HEIGHT %u\r\n", header->cupsWidth);
+	printf("PAGE-HEIGHT %u\r\n", header->cupsHeight);
         break;
 
     case INTELLITECH_PCL :
@@ -374,7 +374,7 @@
 
           if (header->cupsCompression != ~0U)
 	  				/* inPrintDensity */
-	    printf("\033&d%uA", 30 * header->cupsCompression / 100 - 15);
+	    printf("\033&d%dA", 30 * header->cupsCompression / 100 - 15);
 
 	  if ((choice = ppdFindMarkedChoice(ppd, "inPrintMode")) != NULL)
 	  {
diff --git a/filter/spec-ppd.shtml b/filter/spec-ppd.shtml
index 3b75430..53494ad 100644
--- a/filter/spec-ppd.shtml
+++ b/filter/spec-ppd.shtml
@@ -1297,7 +1297,20 @@
 
 <p class='summary'>*cupsJobPassword: "format"</p>
 
-<p>This keyword defines the format of the job-password IPP attribute, if supported by the printer. Currently the only supported format is "1111" indicating a 4-digit PIN code.</p>
+<p>This keyword defines the format of the "job-password" IPP attribute, if supported by the printer. The following format characters are supported:</p>
+
+<ul>
+	<li><code>1</code>: US ASCII digits.</li>
+	<li><code>A</code>: US ASCII letters.</li>
+	<li><code>C</code>: US ASCII letters, numbers, and punctuation.</li>
+	<li><code>.</code>: Any US ASCII printable character (0x20 to 0x7e).</li>
+	<li><code>N</code>: Any Unicode digit character.</li>
+	<li><code>U</code>: Any Unicode letter character.</li>
+	<li><code>*</code>: Any Unicode (utf-8) character.</li>
+</ul>
+
+<p>The format characters are repeated to indicate the length of the
+password string.  For example, "1111" indicated a 4-digit US ASCII PIN code.</p>
 
 <p>Example:</p>
 
@@ -1581,7 +1594,7 @@
 
 <h2 class='title'><a name='MACOSX'>macOS Attributes</a></h2>
 
-<h3><span class='info'>macOS 10.3</span><a name='APDialogExtension'>APDialogExtension</a></h3>
+<h3><span class='info'>Deprecated</span><a name='APDialogExtension'>APDialogExtension</a></h3>
 
 <p class='summary'>*APDialogExtension: "/Library/Printers/vendor/filename.plugin"</p>
 
@@ -1593,9 +1606,13 @@
 
 <blockquote><b>Note:</b>
 
-<p>Starting with macOS 10.5, each plug-in must be compiled "4-way fat"
-(32-bit and 64-bit for both PowerPC and Intel) with garbage collection enabled
-in order to be usable with all applications.</p>
+<p>Since 2010, AirPrint has enabled the printing of full quality photos and
+documents from the Mac without requiring driver software. Starting with macOS
+10.12, system level security features prevent print dialog plug-ins from being
+loaded into applications that have enabled the library validation security
+feature. As of macOS 10.14 the <code>APDialogExtension</code> attribute used to
+create macOS print drivers is deprecated. All new printer models should support
+AirPrint moving forward.</p>
 
 </blockquote>
 
diff --git a/filter/testclient.c b/filter/testclient.c
new file mode 100644
index 0000000..3a9c032
--- /dev/null
+++ b/filter/testclient.c
@@ -0,0 +1,960 @@
+/*
+ * Simulated client test program for CUPS.
+ *
+ * Copyright 2017 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
+ * missing or damaged, see the license at "http://www.cups.org/".
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cups/cups.h>
+#include <cups/raster.h>
+#include <cups/string-private.h>
+#include <cups/thread-private.h>
+
+
+/*
+ * Local types...
+ */
+
+typedef struct _client_monitor_s
+{
+  const char		*uri,		/* Printer URI */
+			*hostname,	/* Hostname */
+			*user,		/* Username */
+			*resource;	/* Resource path */
+  int			port;		/* Port number */
+  http_encryption_t	encryption;	/* Use encryption? */
+  ipp_pstate_t		printer_state;	/* Current printer state */
+  char                  printer_state_reasons[1024];
+                                        /* Current printer-state-reasons */
+  int			job_id; 	/* Job ID for submitted job */
+  ipp_jstate_t		job_state;	/* Current job state */
+  char                  job_state_reasons[1024];
+                                        /* Current job-state-reasons */
+} _client_monitor_t;
+
+
+/*
+ * Local functions...
+ */
+
+static const char       *make_raster_file(ipp_t *response, int grayscale, char *tempname, size_t tempsize, const char **format);
+static void	        *monitor_printer(_client_monitor_t *monitor);
+static void             show_attributes(const char *title, int request, ipp_t *ipp);
+static void             show_capabilities(ipp_t *response);
+static void             usage(void);
+
+
+/*
+ * 'main()' - Main entry.
+ */
+
+int					/* O - Exit status */
+main(int  argc,				/* I - Number of command-line arguments */
+     char *argv[])			/* I - Command-line arguments */
+{
+  int                   i;              /* Looping var */
+  const char            *opt,           /* Current option */
+                        *uri = NULL,    /* Printer URI */
+                        *printfile = NULL,
+                                        /* Print file */
+                        *printformat = NULL;
+                                        /* Print format */
+  int                   keepfile = 0,   /* Keep temp file? */
+                        grayscale = 0,  /* Force grayscale? */
+                        verbosity = 0;  /* Verbosity */
+  char                  tempfile[1024] = "",
+                                        /* Temporary file (if any) */
+                        scheme[32],     /* URI scheme */
+                        userpass[256],  /* Username:password */
+                        hostname[256],  /* Hostname */
+                        resource[256];  /* Resource path */
+  int                   port;           /* Port number */
+  http_encryption_t     encryption;     /* Encryption mode */
+  _client_monitor_t     monitor;        /* Monitoring data */
+  http_t                *http;          /* HTTP connection */
+  ipp_t                 *request,       /* IPP request */
+                        *response;      /* IPP response */
+  ipp_attribute_t       *attr;          /* IPP attribute */
+  static const char * const pattrs[] =  /* Printer attributes we are interested in */
+  {
+    "job-template",
+    "printer-defaults",
+    "printer-description",
+    "media-col-database",
+    "media-col-ready"
+  };
+
+
+ /*
+  * Parse command-line options...
+  */
+
+  for (i = 1; i < argc; i ++)
+  {
+    if (argv[i][0] == '-')
+    {
+      for (opt = argv[i] + 1; *opt; opt ++)
+      {
+        switch (*opt)
+        {
+          case 'd' : /* -d document-format */
+              if (printformat)
+              {
+                puts("Document format can only be specified once.");
+                usage();
+                return (1);
+              }
+
+              i ++;
+              if (i >= argc)
+              {
+                puts("Expected document format after '-d'.");
+                usage();
+                return (1);
+              }
+
+              printformat = argv[i];
+              break;
+
+          case 'f' : /* -f print-file */
+              if (printfile)
+              {
+                puts("Print file can only be specified once.");
+                usage();
+                return (1);
+              }
+
+              i ++;
+              if (i >= argc)
+              {
+                puts("Expected print file after '-f'.");
+                usage();
+                return (1);
+              }
+
+              printfile = argv[i];
+              break;
+
+          case 'g' :
+              grayscale = 1;
+              break;
+
+          case 'k' :
+              keepfile = 1;
+              break;
+
+          case 'v' :
+              verbosity ++;
+              break;
+
+          default :
+              printf("Unknown option '-%c'.\n", *opt);
+              usage();
+              return (1);
+        }
+      }
+    }
+    else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7)))
+    {
+      printf("Unknown command-line argument '%s'.\n", argv[i]);
+      usage();
+      return (1);
+    }
+    else
+      uri = argv[i];
+  }
+
+ /*
+  * Make sure we have everything we need.
+  */
+
+  if (!uri)
+  {
+    puts("Expected printer URI.");
+    usage();
+    return (1);
+  }
+
+ /*
+  * Connect to the printer...
+  */
+
+  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+  {
+    printf("Bad printer URI '%s'.\n", uri);
+    return (1);
+  }
+
+  if (!port)
+    port = IPP_PORT;
+
+  if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
+    encryption = HTTP_ENCRYPTION_ALWAYS;
+  else
+    encryption = HTTP_ENCRYPTION_IF_REQUESTED;
+
+  if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL)
+  {
+    printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
+    return (1);
+  }
+
+ /*
+  * Query printer status and capabilities...
+  */
+
+  request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+  response = cupsDoRequest(http, request, resource);
+
+  if (verbosity)
+    show_capabilities(response);
+
+ /*
+  * Now figure out what we will be printing...
+  */
+
+  if (printfile)
+  {
+   /*
+    * User specified a print file, figure out the format...
+    */
+
+    if ((opt = strrchr(printfile, '.')) != NULL)
+    {
+     /*
+      * Guess the format from the extension...
+      */
+
+      if (!strcmp(opt, ".jpg"))
+        printformat = "image/jpeg";
+      else if (!strcmp(opt, ".pdf"))
+        printformat = "application/pdf";
+      else if (!strcmp(opt, ".ps"))
+        printformat = "application/postscript";
+      else if (!strcmp(opt, ".pwg"))
+        printformat = "image/pwg-raster";
+      else if (!strcmp(opt, ".urf"))
+        printformat = "image/urf";
+      else
+        printformat = "application/octet-stream";
+    }
+    else
+    {
+     /*
+      * Tell the printer to auto-detect...
+      */
+
+      printformat = "application/octet-stream";
+    }
+  }
+  else
+  {
+   /*
+    * No file specified, make something to test with...
+    */
+
+    if ((printfile = make_raster_file(response, grayscale, tempfile, sizeof(tempfile), &printformat)) == NULL)
+      return (1);
+  }
+
+  ippDelete(response);
+
+ /*
+  * Start monitoring the printer in the background...
+  */
+
+  memset(&monitor, 0, sizeof(monitor));
+
+  monitor.uri           = uri;
+  monitor.hostname      = hostname;
+  monitor.resource      = resource;
+  monitor.port          = port;
+  monitor.encryption    = encryption;
+
+  _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
+
+ /*
+  * Create the job and wait for completion...
+  */
+
+  request = ippNewRequest(IPP_OP_CREATE_JOB);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+
+  if ((opt = strrchr(printfile, '/')) != NULL)
+    opt ++;
+  else
+    opt = printfile;
+
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, opt);
+
+  if (verbosity)
+    show_attributes("Create-Job request", 1, request);
+
+  response = cupsDoRequest(http, request, resource);
+
+  if (verbosity)
+    show_attributes("Create-Job response", 0, response);
+
+  if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+  {
+    printf("Unable to create print job: %s\n", cupsLastErrorString());
+
+    monitor.job_state = IPP_JSTATE_ABORTED;
+
+    goto cleanup;
+  }
+
+  if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
+  {
+    puts("No job-id returned in Create-Job request.");
+
+    monitor.job_state = IPP_JSTATE_ABORTED;
+
+    goto cleanup;
+  }
+
+  monitor.job_id = ippGetInteger(attr, 0);
+
+  printf("CREATED JOB %d, sending %s of type %s\n", monitor.job_id, printfile, printformat);
+
+  ippDelete(response);
+
+  request = ippNewRequest(IPP_OP_SEND_DOCUMENT);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat);
+  ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+
+  if (verbosity)
+    show_attributes("Send-Document request", 1, request);
+
+  response = cupsDoFileRequest(http, request, resource, printfile);
+
+  if (verbosity)
+    show_attributes("Send-Document response", 0, response);
+
+  if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
+  {
+    printf("Unable to print file: %s\n", cupsLastErrorString());
+
+    monitor.job_state = IPP_JSTATE_ABORTED;
+
+    goto cleanup;
+  }
+
+  puts("WAITING FOR JOB TO COMPLETE");
+
+  while (monitor.job_state < IPP_JSTATE_CANCELED)
+    sleep(1);
+
+ /*
+  * Cleanup after ourselves...
+  */
+
+  cleanup:
+
+  httpClose(http);
+
+  if (tempfile[0] && !keepfile)
+    unlink(tempfile);
+
+  return (monitor.job_state == IPP_JSTATE_COMPLETED);
+}
+
+
+/*
+ * 'make_raster_file()' - Create a temporary raster file.
+ */
+
+static const char *                     /* O - Print filename */
+make_raster_file(ipp_t      *response,  /* I - Printer attributes */
+                 int        grayscale,  /* I - Force grayscale? */
+                 char       *tempname,  /* I - Temporary filename buffer */
+                 size_t     tempsize,   /* I - Size of temp file buffer */
+                 const char **format)   /* O - Print format */
+{
+  int                   i,              /* Looping var */
+                        count;          /* Number of values */
+  ipp_attribute_t       *attr;          /* Printer attribute */
+  const char            *type = NULL;   /* Raster type (colorspace + bits) */
+  pwg_media_t           *media = NULL;  /* Media size */
+  int                   xdpi = 0,       /* Horizontal resolution */
+                        ydpi = 0;       /* Vertical resolution */
+  int                   fd;             /* Temporary file */
+  cups_mode_t           mode;           /* Raster mode */
+  cups_raster_t         *ras;           /* Raster stream */
+  cups_page_header2_t   header;         /* Page header */
+  unsigned char         *line,          /* Line of raster data */
+                        *lineptr;       /* Pointer into line */
+  unsigned              y,              /* Current position on page */
+                        xcount, ycount, /* Current count for X and Y */
+                        xrep, yrep,     /* Repeat count for X and Y */
+                        xoff, yoff,     /* Offsets for X and Y */
+                        yend;           /* End Y value */
+  int                   temprow,        /* Row in template */
+                        tempcolor;      /* Template color */
+  const char            *template;      /* Pointer into template */
+  const unsigned char   *color;         /* Current color */
+  static const unsigned char colors[][3] =
+  {                                     /* Colors for test */
+    { 191, 191, 191 },
+    { 127, 127, 127 },
+    {  63,  63,  63 },
+    {   0,   0,   0 },
+    { 255,   0,   0 },
+    { 255, 127,   0 },
+    { 255, 255,   0 },
+    { 127, 255,   0 },
+    {   0, 255,   0 },
+    {   0, 255, 127 },
+    {   0, 255, 255 },
+    {   0, 127, 255 },
+    {   0,   0, 255 },
+    { 127,   0, 255 },
+    { 255,   0, 255 }
+  };
+  static const char * const templates[] =
+  {                                     /* Raster template */
+    " CCC   U   U  PPPP    SSS          TTTTT  EEEEE   SSS   TTTTT          000     1     222    333      4   55555   66    77777   888    999   ",
+    "C   C  U   U  P   P  S   S           T    E      S   S    T           0   0   11    2   2  3   3  4  4   5      6          7  8   8  9   9  ",
+    "C      U   U  P   P  S               T    E      S        T           0   0    1        2      3  4  4   5      6         7   8   8  9   9  ",
+    "C      U   U  PPPP    SSS   -----    T    EEEE    SSS     T           0 0 0    1      22    333   44444   555   6666      7    888    9999  ",
+    "C      U   U  P          S           T    E          S    T           0   0    1     2         3     4       5  6   6    7    8   8      9  ",
+    "C   C  U   U  P      S   S           T    E      S   S    T           0   0    1    2      3   3     4   5   5  6   6    7    8   8      9  ",
+    " CCC    UUU   P       SSS            T    EEEEE   SSS     T            000    111   22222   333      4    555    666     7     888     99   ",
+    "                                                                                                                                            "
+  };
+
+
+ /*
+  * Figure out the output format...
+  */
+
+  if ((attr = ippFindAttribute(response, "document-format-supported", IPP_TAG_MIMETYPE)) == NULL)
+  {
+    puts("No supported document formats, aborting.");
+    return (NULL);
+  }
+
+  if (*format)
+  {
+    if (!ippContainsString(attr, *format))
+    {
+      printf("Printer does not support document-format '%s'.\n", *format);
+      return (NULL);
+    }
+
+    if (!strcmp(*format, "image/urf"))
+      mode = CUPS_RASTER_WRITE_APPLE;
+    else if (!strcmp(*format, "image/pwg-raster"))
+      mode = CUPS_RASTER_WRITE_PWG;
+    else
+    {
+      printf("Unable to generate document-format '%s'.\n", *format);
+      return (NULL);
+    }
+  }
+  else if (ippContainsString(attr, "image/urf"))
+  {
+   /*
+    * Apple Raster format...
+    */
+
+    *format = "image/urf";
+    mode    = CUPS_RASTER_WRITE_APPLE;
+  }
+  else if (ippContainsString(attr, "image/pwg-raster"))
+  {
+   /*
+    * PWG Raster format...
+    */
+
+    *format = "image/pwg-raster";
+    mode    = CUPS_RASTER_WRITE_PWG;
+  }
+  else
+  {
+   /*
+    * No supported raster format...
+    */
+
+    puts("Printer does not support Apple or PWG raster files, aborting.");
+    return (NULL);
+  }
+
+ /*
+  * Figure out the the media, resolution, and color mode...
+  */
+
+  if ((attr = ippFindAttribute(response, "media-default", IPP_TAG_KEYWORD)) != NULL)
+  {
+   /*
+    * Use default media...
+    */
+
+    media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
+  }
+  else if ((attr = ippFindAttribute(response, "media-ready", IPP_TAG_KEYWORD)) != NULL)
+  {
+   /*
+    * Use ready media...
+    */
+
+    if (ippContainsString(attr, "na_letter_8.5x11in"))
+      media = pwgMediaForPWG("na_letter_8.5x11in");
+    else if (ippContainsString(attr, "iso_a4_210x297mm"))
+      media = pwgMediaForPWG("iso_a4_210x297mm");
+    else
+      media = pwgMediaForPWG(ippGetString(attr, 0, NULL));
+  }
+  else
+  {
+    puts("No default or ready media reported by printer, aborting.");
+    return (NULL);
+  }
+
+  if (mode == CUPS_RASTER_WRITE_APPLE && (attr = ippFindAttribute(response, "urf-supported", IPP_TAG_KEYWORD)) != NULL)
+  {
+    for (i = 0, count = ippGetCount(attr); i < count; i ++)
+    {
+      const char *val = ippGetString(attr, i, NULL);
+
+      if (!strncmp(val, "RS", 2))
+        xdpi = ydpi = atoi(val + 2);
+      else if (!strncmp(val, "W8", 2) && !type)
+        type = "sgray_8";
+      else if (!strncmp(val, "SRGB24", 6) && !grayscale)
+        type = "srgb_8";
+    }
+  }
+  else if (mode == CUPS_RASTER_WRITE_PWG && (attr = ippFindAttribute(response, "pwg-raster-document-resolution-supported", IPP_TAG_RESOLUTION)) != NULL)
+  {
+    for (i = 0, count = ippGetCount(attr); i < count; i ++)
+    {
+      int tempxdpi, tempydpi;
+      ipp_res_t tempunits;
+
+      tempxdpi = ippGetResolution(attr, 0, &tempydpi, &tempunits);
+
+      if (i == 0 || tempxdpi < xdpi || tempydpi < ydpi)
+      {
+        xdpi = tempxdpi;
+        ydpi = tempydpi;
+      }
+    }
+
+    if ((attr = ippFindAttribute(response, "pwg-raster-document-type-supported", IPP_TAG_KEYWORD)) != NULL)
+    {
+      if (!grayscale && ippContainsString(attr, "srgb_8"))
+        type = "srgb_8";
+      else if (ippContainsString(attr, "sgray_8"))
+        type = "sgray_8";
+    }
+  }
+
+  if (xdpi < 72 || ydpi < 72)
+  {
+    puts("No supported raster resolutions, aborting.");
+    return (NULL);
+  }
+
+  if (!type)
+  {
+    puts("No supported color spaces or bit depths, aborting.");
+    return (NULL);
+  }
+
+ /*
+  * Make the raster context and details...
+  */
+
+  if (!cupsRasterInitPWGHeader(&header, media, type, xdpi, ydpi, "one-sided", NULL))
+  {
+    printf("Unable to initialize raster context: %s\n", cupsRasterErrorString());
+    return (NULL);
+  }
+
+  header.cupsInteger[CUPS_RASTER_PWG_TotalPageCount] = 1;
+
+  if (header.cupsWidth > (4 * header.HWResolution[0]))
+  {
+    xoff = header.HWResolution[0] / 2;
+    yoff = header.HWResolution[1] / 2;
+  }
+  else
+  {
+    xoff = 0;
+    yoff = 0;
+  }
+
+  xrep = (header.cupsWidth - 2 * xoff) / 140;
+  yrep = xrep * header.HWResolution[1] / header.HWResolution[0];
+  yend = header.cupsHeight - yoff;
+
+ /*
+  * Prepare the raster file...
+  */
+
+  if ((line = malloc(header.cupsBytesPerLine)) == NULL)
+  {
+    printf("Unable to allocate %u bytes for raster output: %s\n", header.cupsBytesPerLine, strerror(errno));
+    return (NULL);
+  }
+
+  if ((fd = cupsTempFd(tempname, (int)tempsize)) < 0)
+  {
+    printf("Unable to create temporary print file: %s\n", strerror(errno));
+    return (NULL);
+  }
+
+  if ((ras = cupsRasterOpen(fd, mode)) == NULL)
+  {
+    printf("Unable to open raster stream: %s\n", cupsRasterErrorString());
+    close(fd);
+    return (NULL);
+  }
+
+ /*
+  * Write a single page consisting of the template dots repeated over the page.
+  */
+
+  cupsRasterWriteHeader2(ras, &header);
+
+  memset(line, 0xff, header.cupsBytesPerLine);
+
+  for (y = 0; y < yoff; y ++)
+    cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+
+  for (temprow = 0, tempcolor = 0; y < yend;)
+  {
+    template = templates[temprow];
+    color    = colors[tempcolor];
+
+    temprow ++;
+    if (temprow >= (int)(sizeof(templates) / sizeof(templates[0])))
+    {
+      temprow = 0;
+      tempcolor ++;
+      if (tempcolor >= (int)(sizeof(colors) / sizeof(colors[0])))
+        tempcolor = 0;
+      else if (tempcolor > 3 && header.cupsColorSpace == CUPS_CSPACE_SW)
+        tempcolor = 0;
+    }
+
+    memset(line, 0xff, header.cupsBytesPerLine);
+
+    if (header.cupsColorSpace == CUPS_CSPACE_SW)
+    {
+     /*
+      * Do grayscale output...
+      */
+
+      for (lineptr = line + xoff; *template; template ++)
+      {
+        if (*template != ' ')
+        {
+          for (xcount = xrep; xcount > 0; xcount --)
+            *lineptr++ = *color;
+        }
+        else
+        {
+          lineptr += xrep;
+        }
+      }
+    }
+    else
+    {
+     /*
+      * Do color output...
+      */
+
+      for (lineptr = line + 3 * xoff; *template; template ++)
+      {
+        if (*template != ' ')
+        {
+          for (xcount = xrep; xcount > 0; xcount --, lineptr += 3)
+            memcpy(lineptr, color, 3);
+        }
+        else
+        {
+          lineptr += 3 * xrep;
+        }
+      }
+    }
+
+    for (ycount = yrep; ycount > 0 && y < yend; ycount --, y ++)
+      cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+  }
+
+  memset(line, 0xff, header.cupsBytesPerLine);
+
+  for (y = 0; y < header.cupsHeight; y ++)
+    cupsRasterWritePixels(ras, line, header.cupsBytesPerLine);
+
+  cupsRasterClose(ras);
+
+  close(fd);
+
+  printf("PRINT FILE: %s\n", tempname);
+
+  return (tempname);
+}
+
+
+/*
+ * 'monitor_printer()' - Monitor the job and printer states.
+ */
+
+static void *				/* O - Thread exit code */
+monitor_printer(
+    _client_monitor_t *monitor)		/* I - Monitoring data */
+{
+  http_t	*http;			/* Connection to printer */
+  ipp_t		*request,		/* IPP request */
+		*response;		/* IPP response */
+  ipp_attribute_t *attr;		/* Attribute in response */
+  ipp_pstate_t	printer_state;		/* Printer state */
+  char          printer_state_reasons[1024];
+                                        /* Printer state reasons */
+  ipp_jstate_t	job_state;		/* Job state */
+  char          job_state_reasons[1024];/* Printer state reasons */
+  static const char * const jattrs[] =  /* Job attributes we want */
+  {
+    "job-state",
+    "job-state-reasons"
+  };
+  static const char * const pattrs[] =  /* Printer attributes we want */
+  {
+    "printer-state",
+    "printer-state-reasons"
+  };
+
+
+ /*
+  * Open a connection to the printer...
+  */
+
+  http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC, monitor->encryption, 1, 0, NULL);
+
+ /*
+  * Loop until the job is canceled, aborted, or completed.
+  */
+
+  printer_state            = (ipp_pstate_t)0;
+  printer_state_reasons[0] = '\0';
+
+  job_state            = (ipp_jstate_t)0;
+  job_state_reasons[0] = '\0';
+
+  while (monitor->job_state < IPP_JSTATE_CANCELED)
+  {
+   /*
+    * Reconnect to the printer as needed...
+    */
+
+    if (httpGetFd(http) < 0)
+      httpReconnect2(http, 30000, NULL);
+
+    if (httpGetFd(http) >= 0)
+    {
+     /*
+      * Connected, so check on the printer state...
+      */
+
+      request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
+      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
+
+      response = cupsDoRequest(http, request, monitor->resource);
+
+      if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
+        printer_state = (ipp_pstate_t)ippGetInteger(attr, 0);
+
+      if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
+        ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons));
+
+      if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons))
+      {
+        printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons);
+
+        monitor->printer_state = printer_state;
+        strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons));
+      }
+
+      ippDelete(response);
+
+      if (monitor->job_id > 0)
+      {
+       /*
+        * Check the status of the job itself...
+        */
+
+        request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
+        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
+        ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id);
+        ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
+        ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
+
+        response = cupsDoRequest(http, request, monitor->resource);
+
+        if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
+          job_state = (ipp_jstate_t)ippGetInteger(attr, 0);
+
+        if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
+          ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons));
+
+        if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons))
+        {
+          printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons);
+
+          monitor->job_state = job_state;
+          strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons));
+        }
+
+        ippDelete(response);
+      }
+    }
+
+    if (monitor->job_state < IPP_JSTATE_CANCELED)
+    {
+     /*
+      * Sleep for 5 seconds...
+      */
+
+      sleep(5);
+    }
+  }
+
+ /*
+  * Cleanup and return...
+  */
+
+  httpClose(http);
+
+  return (NULL);
+}
+
+
+/*
+ * 'show_attributes()' - Show attributes in a request or response.
+ */
+
+static void
+show_attributes(const char *title,      /* I - Title */
+                int        request,     /* I - 1 for request, 0 for response */
+                ipp_t      *ipp)        /* I - IPP request/response */
+{
+  int                   minor, major = ippGetVersion(ipp, &minor);
+                                        /* IPP version number */
+  ipp_tag_t             group = IPP_TAG_ZERO;
+                                        /* Current group tag */
+  ipp_attribute_t       *attr;          /* Current attribute */
+  const char            *name;          /* Attribute name */
+  char                  buffer[1024];   /* Value */
+
+
+  printf("%s:\n", title);
+  printf("  version=%d.%d\n", major, minor);
+  printf("  request-id=%d\n", ippGetRequestId(ipp));
+  if (!request)
+    printf("  status-code=%s\n", ippErrorString(ippGetStatusCode(ipp)));
+
+  for (attr = ippFirstAttribute(ipp); attr; attr = ippNextAttribute(ipp))
+  {
+    if (group != ippGetGroupTag(attr))
+    {
+      group = ippGetGroupTag(attr);
+      if (group)
+        printf("  %s:\n", ippTagString(group));
+    }
+
+    if ((name = ippGetName(attr)) != NULL)
+    {
+      ippAttributeString(attr, buffer, sizeof(buffer));
+      printf("    %s(%s%s)=%s\n", name, ippGetCount(attr) > 1 ? "1setOf " : "", ippTagString(ippGetValueTag(attr)), buffer);
+    }
+  }
+}
+
+
+/*
+ * 'show_capabilities()' - Show printer capabilities.
+ */
+
+static void
+show_capabilities(ipp_t *response)      /* I - Printer attributes */
+{
+  int                   i;              /* Looping var */
+  ipp_attribute_t       *attr;          /* Attribute */
+  char                  buffer[1024];   /* Attribute value buffer */
+  static const char * const pattrs[] =  /* Attributes we want to show */
+  {
+    "copies-default",
+    "copies-supported",
+    "finishings-default",
+    "finishings-ready",
+    "finishings-supported",
+    "media-default",
+    "media-ready",
+    "media-supported",
+    "output-bin-default",
+    "output-bin-supported",
+    "print-color-mode-default",
+    "print-color-mode-supported",
+    "sides-default",
+    "sides-supported",
+    "document-format-default",
+    "document-format-supported",
+    "pwg-raster-document-resolution-supported",
+    "pwg-raster-document-type-supported",
+    "urf-supported"
+  };
+
+
+  puts("CAPABILITIES:");
+  for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++)
+  {
+     if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL)
+     {
+       ippAttributeString(attr, buffer, sizeof(buffer));
+       printf("  %s=%s\n", pattrs[i], buffer);
+     }
+  }
+}
+
+
+/*
+ * 'usage()' - Show program usage...
+ */
+
+static void
+usage(void)
+{
+  puts("Usage: ./testclient printer-uri [options]");
+  puts("Options:");
+  puts("  -d document-format  Generate the specified format");
+  puts("  -f print-file       Print the named file");
+  puts("  -g                  Force grayscale printing");
+  puts("  -k                  Keep temporary files");
+  puts("  -v                  Be more verbose");
+}
diff --git a/libcups_version b/libcups_version
index 0b1f88b..2a64493 100644
--- a/libcups_version
+++ b/libcups_version
@@ -1 +1 @@
-v2.2.3
+v2.2.9