]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Save work on ipptool, ippserver, and the IPP conformance tests.
authormike <mike@7a7537e8-13f0-0310-91df-b6672ffda945>
Sat, 10 Sep 2011 15:13:23 +0000 (15:13 +0000)
committermike <mike@7a7537e8-13f0-0310-91df-b6672ffda945>
Sat, 10 Sep 2011 15:13:23 +0000 (15:13 +0000)
git-svn-id: svn+ssh://src.apple.com/svn/cups/cups.org/trunk@9994 7a7537e8-13f0-0310-91df-b6672ffda945

test/ipp-1.1.test
test/ipp-2.0.test
test/ipp-2.1.test
test/ipp-2.2.test [new file with mode: 0644]
test/ippserver.c
test/ipptool.c

index 0809aab19cd9215af81df8fd938ba40c9de8e89e..7e7057759836a9fdff4b0b4ba2f197dd1b474d35 100644 (file)
 #
 # Usage:
 #
-#   ./ipptool -f filename -t printer-uri ipp-1.1.test
+#   ./ipptool -f filename [-d document-uri=SOMEURI] -t printer-uri ipp-1.1.test
 #
 
-# Regular expression for IPP URI schemes
-# Matches strings beginning with ipp:// or ipps://
+# Regular expressions for URI schemes:
+#
+#   HTTP_URI_SCHEME - Matches strings beginning with http:// or https://
+#   IPP_URI_SCHEME  - Matches strings beginning with ipp:// or ipps://
+
+DEFINE HTTP_URI_SCHEME "/^https?://.+$$/"
 DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 
 # Test that a request-id value of 0 is not accepted.
 #
 # Required by: RFC 2911 section 3.1.1
 {
-       NAME "3.1.1: Bad request-id value 0"
+       NAME "RFC 2911 section 3.1.1: Bad request-id value 0"
        REQUEST-ID 0
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
@@ -43,7 +47,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.1.4
 {
-       NAME "3.1.4: No Operation Attributes"
+       NAME "RFC 2911 section 3.1.4: No Operation Attributes"
        REQUEST-ID random
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
@@ -52,7 +56,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        EXPECT !printer-uri-supported
 }
 {
-       NAME "3.1.4: attributes-charset"
+       NAME "RFC 2911 section 3.1.4: attributes-charset"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -62,7 +66,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        EXPECT !printer-uri-supported
 }
 {
-       NAME "3.1.4: attributes-natural-language"
+       NAME "RFC 2911 section 3.1.4: attributes-natural-language"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR naturalLanguage attributes-natural-language en
@@ -72,7 +76,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        EXPECT !printer-uri-supported
 }
 {
-       NAME "3.1.4: attributes-natural-language + attributes-charset"
+       NAME "RFC 2911 section 3.1.4: attributes-natural-language + attributes-charset"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR naturalLanguage attributes-natural-language en
@@ -83,7 +87,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        EXPECT !printer-uri-supported
 }
 {
-       NAME "3.1.4: attributes-charset + attributes-natural-language"
+       NAME "RFC 2911 section 3.1.4: attributes-charset + attributes-natural-language"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -100,7 +104,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 # Required by: RFC 2911 section 3.1.8
 {
        # The name of the test...
-       NAME "3.1.8: Unsupported IPP version 0.0"
+       NAME "RFC 2911 section 3.1.8: Unsupported IPP version 0.0"
        VERSION 0.0
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
@@ -117,7 +121,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2
 {
-       NAME "3.2: No printer-uri operation attribute"
+       NAME "RFC 2911 section 3.2: No printer-uri operation attribute"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -132,7 +136,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.1
 {
-       NAME "3.2.1: Print-Job Operation"
+       NAME "RFC 2911 section 3.2.1: Print-Job Operation"
        OPERATION Print-Job
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -143,11 +147,13 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        ATTR boolean ipp-attribute-fidelity false
        ATTR name document-name $filename
        ATTR keyword compression none
-       ATTR mimeMediaType document-format application/octet-stream
+       ATTR mimeMediaType document-format $filetype
        FILE $filename
 
        STATUS successful-ok
        STATUS client-error-document-format-not-supported
+       STATUS server-error-job-canceled
+
        EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
        EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
               WITH-VALUE >0
@@ -159,18 +165,39 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
               IN-GROUP job-attributes-tag WITH-VALUE >-1
 }
 
+# Test Validate-Job operation
+#
+# Required by: RFC 2911 section 3.2.3
+{
+       NAME "RFC 2911 section 3.2.3: Validate-Job Operation"
+       OPERATION Validate-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+
+       STATUS successful-ok
+}
+
+
 # Test Get-Printer-Attributes operation
 #
 # Required by: RFC 2911 section 3.2.5
 {
-       NAME "3.2.5: Get-Printer-Attributes Operation (default)"
+       NAME "RFC 2911 section 3.2.5: Get-Printer-Attributes Operation (default)"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
        ATTR naturalLanguage attributes-natural-language en
        ATTR uri printer-uri $uri
        ATTR name requesting-user-name $user
-       ATTR mimeMediaType document-format application/octet-stream
+       ATTR mimeMediaType document-format $filetype
 
        STATUS successful-ok
 
@@ -217,8 +244,8 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        EXPECT ?printer-location OF-TYPE text IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE "/^.{0,127}$$/"
        EXPECT ?printer-make-and-model OF-TYPE text IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE "/^.{0,127}$$/"
        EXPECT ?printer-message-from-operator OF-TYPE text IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE "/^.{0,127}$$/"
-       EXPECT ?printer-more-info OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1
-       EXPECT ?printer-more-info-manufacturer OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT ?printer-more-info OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE "$HTTP_URI_SCHEME"
+       EXPECT ?printer-more-info-manufacturer OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE "$HTTP_URI_SCHEME"
        EXPECT ?printer-state-message OF-TYPE text IN-GROUP printer-attributes-tag
        EXPECT ?reference-uri-schemes-supported OF-TYPE uriScheme IN-GROUP printer-attributes-tag
        EXPECT charset-configured OF-TYPE charset IN-GROUP printer-attributes-tag COUNT 1
@@ -230,11 +257,21 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        EXPECT ipp-versions-supported OF-TYPE keyword IN-GROUP printer-attributes-tag WITH-VALUE 1.1
        EXPECT natural-language-configured OF-TYPE naturalLanguage IN-GROUP printer-attributes-tag COUNT 1
        EXPECT operations-supported OF-TYPE enum IN-GROUP printer-attributes-tag WITH-VALUE 0x0002 # Print-Job
+       EXPECT operations-supported WITH-VALUE 0x0003 DEFINE-MATCH HAVE_PRINT_URI # Print-URI
        EXPECT operations-supported WITH-VALUE 0x0004 # Validate-Job
+       EXPECT operations-supported WITH-VALUE 0x0005 DEFINE-MATCH HAVE_CREATE_JOB # Create-Job
+       EXPECT operations-supported WITH-VALUE 0x0006 DEFINE-MATCH HAVE_SEND_DOCUMENT # Send-Document
+       EXPECT operations-supported WITH-VALUE 0x0007 DEFINE-MATCH HAVE_SEND_URI # Send-URI
        EXPECT operations-supported WITH-VALUE 0x0008 # Cancel-Job
        EXPECT operations-supported WITH-VALUE 0x0009 # Get-Job-Attributes
        EXPECT operations-supported WITH-VALUE 0x000a # Get-Jobs
        EXPECT operations-supported WITH-VALUE 0x000b # Get-Printer-Attributes
+       EXPECT operations-supported WITH-VALUE 0x000c DEFINE-MATCH HAVE_HOLD_JOB # Hold-Job
+       EXPECT operations-supported WITH-VALUE 0x000d DEFINE-MATCH HAVE_RELEASE_JOB # Release-Job
+       EXPECT operations-supported WITH-VALUE 0x000e DEFINE-MATCH HAVE_RESTART_JOB # Restart-Job
+       EXPECT operations-supported WITH-VALUE 0x0010 DEFINE-MATCH HAVE_PAUSE_PRINTER # Pause-Printer
+       EXPECT operations-supported WITH-VALUE 0x0011 DEFINE-MATCH HAVE_RESUME_PRINTER # Resume-Printer
+       EXPECT operations-supported WITH-VALUE 0x0012 DEFINE-MATCH HAVE_PURGE_JOBS # Purge-Jobs
        EXPECT pdl-override-supported OF-TYPE keyword IN-GROUP printer-attributes-tag COUNT 1
        EXPECT printer-is-accepting-jobs OF-TYPE boolean IN-GROUP printer-attributes-tag COUNT 1
        EXPECT printer-name OF-TYPE name IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE "/^.{1,127}$$/"
@@ -252,14 +289,14 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.5
 {
-       NAME "3.2.5: Get-Printer-Attributes Operation (requested-attributes)"
+       NAME "RFC 2911 section 3.2.5: Get-Printer-Attributes Operation (requested-attributes)"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
        ATTR naturalLanguage attributes-natural-language en
        ATTR uri printer-uri $uri
        ATTR name requesting-user-name $user
-       ATTR mimeMediaType document-format application/octet-stream
+       ATTR mimeMediaType document-format $filetype
        ATTR keyword requested-attributes printer-uri-supported
 
        STATUS successful-ok
@@ -273,7 +310,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.6
 {
-       NAME "3.2.6: Get-Jobs Operation (default)"
+       NAME "RFC 2911 section 3.2.6: Get-Jobs Operation (default)"
        OPERATION Get-Jobs
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -330,7 +367,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.6
 {
-       NAME "3.2.6: Get-Jobs Operation (requested-attributes)"
+       NAME "RFC 2911 section 3.2.6: Get-Jobs Operation (requested-attributes)"
        OPERATION Get-Jobs
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -342,8 +379,8 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        STATUS successful-ok
        EXPECT job-id OF-TYPE integer IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >0
        EXPECT job-uri OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE "$IPP_URI_SCHEME"
-       EXPECT job-printer-uri OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1
-       EXPECT ?job-more-info OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1
+       EXPECT job-printer-uri OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT ?job-more-info OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE "$HTTP_URI_SCHEME"
        EXPECT job-name OF-TYPE name IN-GROUP job-attributes-tag COUNT 1
        EXPECT job-originating-user-name OF-TYPE name IN-GROUP job-attributes-tag COUNT 1
        EXPECT job-state OF-TYPE enum IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >2,<10
@@ -388,7 +425,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.6
 {
-       NAME "3.2.6: Get-Jobs Operation (my-jobs)"
+       NAME "RFC 2911 section 3.2.6: Get-Jobs Operation (my-jobs)"
        OPERATION Get-Jobs
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -450,7 +487,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        # use the authenticated username over the requesting-user-name value.
        SKIP-IF-DEFINED uriuser
 
-       NAME "3.2.6: Get-Jobs Operation (my-jobs different user)"
+       NAME "RFC 2911 section 3.2.6: Get-Jobs Operation (my-jobs different user)"
        OPERATION Get-Jobs
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -508,7 +545,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.6
 {
-       NAME "3.2.6: Get-Jobs Operation (which-jobs=not-completed)"
+       NAME "RFC 2911 section 3.2.6: Get-Jobs Operation (which-jobs=not-completed)"
        OPERATION Get-Jobs
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -567,7 +604,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.6
 {
-       NAME "3.2.6: Get-Jobs Operation (which-jobs=completed)"
+       NAME "RFC 2911 section 3.2.6: Get-Jobs Operation (which-jobs=completed)"
        OPERATION Get-Jobs
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -626,7 +663,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.3.3
 {
-       NAME "3.3.3: Cancel-Job Operation (completed job)"
+       NAME "RFC 2911 section 3.3.3: Cancel-Job Operation (completed job)"
        OPERATION Cancel-Job
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -643,7 +680,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.2.1
 {
-       NAME "3.2.1: Print-Job Operation"
+       NAME "RFC 2911 section 3.2.1: Print-Job Operation"
        OPERATION Print-Job
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -654,11 +691,13 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        ATTR boolean ipp-attribute-fidelity false
        ATTR name document-name $filename
        ATTR keyword compression none
-       ATTR mimeMediaType document-format application/octet-stream
+       ATTR mimeMediaType document-format $filetype
        FILE $filename
 
        STATUS successful-ok
        STATUS client-error-document-format-not-supported
+       STATUS server-error-job-canceled
+
        EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
        EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
               WITH-VALUE >0
@@ -675,7 +714,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.3.3
 {
-       NAME "3.3.3: Cancel-Job Operation (pending/processing job)"
+       NAME "RFC 2911 section 3.3.3: Cancel-Job Operation (pending/processing job)"
        OPERATION Cancel-Job
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -693,7 +732,7 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 #
 # Required by: RFC 2911 section 3.3.4
 {
-       NAME "3.3.4: Get-Job-Attributes Operation"
+       NAME "RFC 2911 section 3.3.4: Get-Job-Attributes Operation"
        OPERATION Get-Job-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
@@ -705,8 +744,8 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
        STATUS successful-ok
        EXPECT job-id OF-TYPE integer IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >0
        EXPECT job-uri OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE "$IPP_URI_SCHEME"
-       EXPECT job-printer-uri OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1
-       EXPECT ?job-more-info OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1
+       EXPECT job-printer-uri OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT ?job-more-info OF-TYPE uri IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE "$HTTP_URI_SCHEME"
        EXPECT job-name OF-TYPE name IN-GROUP job-attributes-tag COUNT 1
        EXPECT job-originating-user-name OF-TYPE name IN-GROUP job-attributes-tag COUNT 1
        EXPECT job-state OF-TYPE enum IN-GROUP job-attributes-tag COUNT 1 WITH-VALUE >2,<10
@@ -747,6 +786,322 @@ DEFINE IPP_URI_SCHEME "/^ipps?://.+$$/"
 }
 
 
+# Test Print-URI operation
+#
+# Defined by: RFC 2911 section 3.2.2
+{
+       SKIP-IF-NOT-DEFINED HAVE_PRINT_URI
+       SKIP-IF-NOT-DEFINED document-uri
+
+       NAME "RFC 2911 section 3.2.2: Print-URI Operation"
+       OPERATION Print-URI
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+       ATTR uri document-uri $document-uri
+
+       STATUS successful-ok
+       STATUS client-error-document-access-error
+       STATUS client-error-document-format-not-supported
+       STATUS client-error-uri-scheme-not-supported
+       STATUS server-error-job-canceled
+
+       EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE >0
+       EXPECT job-state OF-TYPE enum COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE 3,4,5,6,7,8,9
+       EXPECT job-state-reasons OF-TYPE keyword IN-GROUP job-attributes-tag
+       EXPECT ?job-state-message OF-TYPE text IN-GROUP job-attributes-tag
+       EXPECT ?number-of-intervening-jobs OF-TYPE integer
+              IN-GROUP job-attributes-tag WITH-VALUE >-1
+}
+
+
+# Test Print-URI operation with bad document-uri
+#
+# Defined by: RFC 2911 section 3.2.2
+{
+       SKIP-IF-NOT-DEFINED HAVE_PRINT_URI
+
+       NAME "Print-URI with bad URI: Print-URI Operation"
+       OPERATION Print-URI
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+       ATTR uri document-uri "bogus://bogus"
+
+       STATUS client-error-uri-scheme-not-supported
+
+       EXPECT !job-uri
+       EXPECT !job-id
+       EXPECT !job-state
+       EXPECT !job-state-reasons
+       EXPECT !job-state-message
+}
+
+
+# Test Create-Job and Send-Document operations
+#
+# Defined by: RFC 2911 section 3.2.4 and 3.3.1
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "RFC 2911 section 3.2.4: Create-Job Operation"
+       OPERATION Create-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+
+       STATUS successful-ok
+
+       EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE >0
+       EXPECT job-state OF-TYPE enum COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE 3,4,5,6,7,8,9
+       EXPECT job-state-reasons OF-TYPE keyword IN-GROUP job-attributes-tag
+       EXPECT ?job-state-message OF-TYPE text IN-GROUP job-attributes-tag
+       EXPECT ?number-of-intervening-jobs OF-TYPE integer
+              IN-GROUP job-attributes-tag WITH-VALUE >-1
+}
+
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "RFC 2911 section 3.3.1: Send-Document Operation"
+       OPERATION Send-Document
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR integer job-id $job-id
+       ATTR name requesting-user-name $user
+       ATTR boolean last-document true
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+       FILE $filename
+
+       STATUS successful-ok
+       STATUS client-error-document-format-not-supported
+       STATUS server-error-job-canceled
+}
+
+# Test Create-Job and Send-Document operations (no last-document)
+#
+# Defined by: RFC 2911 section 3.2.4 and 3.3.1
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "Send-Document missing last-document: Create-Job Operation"
+       OPERATION Create-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+
+       STATUS successful-ok
+
+       EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE >0
+       EXPECT job-state OF-TYPE enum COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE 3,4,5,6,7,8,9
+       EXPECT job-state-reasons OF-TYPE keyword IN-GROUP job-attributes-tag
+       EXPECT ?job-state-message OF-TYPE text IN-GROUP job-attributes-tag
+       EXPECT ?number-of-intervening-jobs OF-TYPE integer
+              IN-GROUP job-attributes-tag WITH-VALUE >-1
+}
+
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "Send-Document missing last-document: Send-Document Operation"
+       OPERATION Send-Document
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR integer job-id $job-id
+       ATTR name requesting-user-name $user
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+       FILE $filename
+
+       STATUS client-error-bad-request
+}
+
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "RFC 2911 section 3.3.3: Cancel-Job Operation"
+       OPERATION Cancel-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR integer job-id $job-id
+       ATTR name requesting-user-name $user
+
+       STATUS successful-ok
+       STATUS server-error-job-canceled
+}
+
+# Test Create-Job and Send-URI operations
+#
+# Defined by: RFC 2911 section 3.2.4 and 3.3.2
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_URI
+       SKIP-IF-NOT-DEFINED document-uri
+
+       NAME "RFC 2911 section 3.2.4: Create-Job Operation"
+       OPERATION Create-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+
+       STATUS successful-ok
+
+       EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE >0
+       EXPECT job-state OF-TYPE enum COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE 3,4,5,6,7,8,9
+       EXPECT job-state-reasons OF-TYPE keyword IN-GROUP job-attributes-tag
+       EXPECT ?job-state-message OF-TYPE text IN-GROUP job-attributes-tag
+       EXPECT ?number-of-intervening-jobs OF-TYPE integer
+              IN-GROUP job-attributes-tag WITH-VALUE >-1
+}
+
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+       SKIP-IF-NOT-DEFINED document-uri
+
+       NAME "RFC 2911 section 3.3.2: Send-URI Operation"
+       OPERATION Send-URI
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR integer job-id $job-id
+       ATTR name requesting-user-name $user
+       ATTR boolean last-document true
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+       ATTR uri document-uri $document-uri
+
+       STATUS successful-ok
+       STATUS client-error-document-access-error
+       STATUS client-error-document-format-not-supported
+       STATUS client-error-uri-scheme-not-supported
+       STATUS server-error-job-canceled
+}
+
+
+# Test Create-Job and Send-URI operations (bad URI)
+#
+# Defined by: RFC 2911 section 3.2.4 and 3.3.2
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_URI
+
+       NAME "Send-URI with bad URI: Create-Job Operation"
+       OPERATION Create-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR name job-name $filename
+       ATTR boolean ipp-attribute-fidelity false
+
+       STATUS successful-ok
+
+       EXPECT job-uri OF-TYPE uri COUNT 1 IN-GROUP job-attributes-tag WITH-VALUE "$IPP_URI_SCHEME"
+       EXPECT job-id OF-TYPE integer COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE >0
+       EXPECT job-state OF-TYPE enum COUNT 1 IN-GROUP job-attributes-tag
+              WITH-VALUE 3,4,5,6,7,8,9
+       EXPECT job-state-reasons OF-TYPE keyword IN-GROUP job-attributes-tag
+       EXPECT ?job-state-message OF-TYPE text IN-GROUP job-attributes-tag
+       EXPECT ?number-of-intervening-jobs OF-TYPE integer
+              IN-GROUP job-attributes-tag WITH-VALUE >-1
+}
+
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "Send-URI with bad URI: Send-URI Operation (bad URI)"
+       OPERATION Send-URI
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR integer job-id $job-id
+       ATTR name requesting-user-name $user
+       ATTR boolean last-document true
+       ATTR name document-name $filename
+       ATTR keyword compression none
+       ATTR mimeMediaType document-format $filetype
+       ATTR uri document-uri "bogus://bogus"
+
+       STATUS client-error-uri-scheme-not-supported
+}
+
+{
+       SKIP-IF-NOT-DEFINED HAVE_CREATE_JOB
+       SKIP-IF-NOT-DEFINED HAVE_SEND_DOCUMENT
+
+       NAME "Send-URI with bad URI: Cancel-Job Operation"
+       OPERATION Cancel-Job
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR integer job-id $job-id
+       ATTR name requesting-user-name $user
+
+       STATUS successful-ok
+       STATUS server-error-job-canceled
+}
+
+
 #
 # End of "$Id$".
 #
index 6d9dffa6b04eb04fd4ab3bedb9b51fa8738d1161..44c3cf2b4c044e8e77473b1174038887f59701e8 100644 (file)
@@ -19,7 +19,7 @@
 
 # Do all of the IPP/1.1 tests as an IPP/2.0 client
 #
-# Required by: PWG 5100.10 section 4.3
+# Required by: PWG 5100.12 section 4.3
 INCLUDE "ipp-1.1.test"
 
 
@@ -29,9 +29,9 @@ DEFINE MEDIA_REGEX "/^((custom|na|asme|roc|oe)_[a-z0-9][-a-z0-9]*_([1-9][0-9]*(\
 
 # Test required printer description attribute support.
 #
-# Required by: PWG 5100.10 section 6.2
+# Required by: PWG 5100.12 section 6.2
 {
-       NAME "PWG 5100.10 section 6.2 - Required Printer Description Attributes"
+       NAME "PWG 5100.12 section 6.2 - Required Printer Description Attributes"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
index 98126947e1ad2201b5dc27d1361d47e9f9fbe6d4..43f9b9f3ba17e42a957c49b8192c2e3fa11519f6 100644 (file)
@@ -3,7 +3,7 @@
 #
 #   IPP/2.1 test suite.
 #
-#   Copyright 2007-2010 by Apple Inc.
+#   Copyright 2007-2011 by Apple Inc.
 #   Copyright 2001-2006 by Easy Software Products. All rights reserved.
 #
 #   These coded instructions, statements, and computer programs are the
@@ -23,9 +23,9 @@ INCLUDE "ipp-2.0.test"
 
 # Test required printer description attribute support.
 #
-# Required by: PWG 5100.10 section 6.3
+# Required by: PWG 5100.12 section 6.3
 {
-       NAME "PWG 5100.10 section 6.3 - Required Printer Description Attributes"
+       NAME "PWG 5100.12 section 6.3 - Required Printer Description Attributes"
        OPERATION Get-Printer-Attributes
        GROUP operation-attributes-tag
        ATTR charset attributes-charset utf-8
diff --git a/test/ipp-2.2.test b/test/ipp-2.2.test
new file mode 100644 (file)
index 0000000..19a4039
--- /dev/null
@@ -0,0 +1,95 @@
+#
+# "$Id$"
+#
+#   IPP/2.2 test suite.
+#
+#   Copyright 2007-2011 by Apple Inc.
+#   Copyright 2001-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/".
+#
+# Usage:
+#
+#   ./ipptool -V 2.2 {-d PRINTER_IS_COLOR=1} -f filename -t printer-uri ipp-2.2.test
+#
+
+# Do all of the IPP/1.1, IPP/2.0, and IPP/2.1 tests as an IPP/2.2 client
+INCLUDE "ipp-2.1.test"
+
+
+# Test required printer description attribute support.
+#
+# Required by: PWG 5100.12 section 6.3
+{
+       NAME "PWG 5100.12 section 6.3 - Required Printer Description Attributes"
+       OPERATION Get-Printer-Attributes
+       GROUP operation-attributes-tag
+       ATTR charset attributes-charset utf-8
+       ATTR naturalLanguage attributes-natural-language en
+       ATTR uri printer-uri $uri
+       ATTR name requesting-user-name $user
+       ATTR mimeMediaType document-format application/octet-stream
+
+       STATUS successful-ok
+
+       # Job template attributes
+       EXPECT job-hold-until-default OF-TYPE keyword|name IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT job-hold-until-supported OF-TYPE keyword|name IN-GROUP printer-attributes-tag WITH-VALUE no-hold
+       EXPECT job-priority-default OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE >0,<101
+       EXPECT job-priority-supported OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE >0,<101
+       EXPECT job-settable-attributes-supported OF-TYPE keyword IN-GROUP printer-attributes-tag
+       EXPECT job-sheets-default OF-TYPE keyword|name IN-GROUP printer-attributes-tag
+       EXPECT job-sheets-supported OF-TYPE keyword|name IN-GROUP printer-attributes-tag WITH-VALUE none
+       EXPECT media-col-default OF-TYPE collection IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT media-col-supported OF-TYPE keyword IN-GROUP printer-attributes-tag
+       EXPECT media-col-supported WITH-VALUE media-size
+       EXPECT media-default OF-TYPE keyword|name IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT media-supported OF-TYPE keyword|name IN-GROUP printer-attributes-tag
+
+       # Subscription attributes
+       EXPECT notify-events-default OF-TYPE keyword IN-GROUP printer-attributes-tag
+       EXPECT notify-events-supported OF-TYPE keyword IN-GROUP printer-attributes-tag
+       EXPECT notify-lease-duration-default OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT notify-lease-duration-supported OF-TYPE integer|rangeOfInteger IN-GROUP printer-attributes-tag
+       EXPECT notify-max-events-supported OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE >1
+       EXPECT notify-pull-method-supported OF-TYPE keyword IN-GROUP printer-attributes-tag WITH-VALUE ippget
+
+       # Printer description attributes
+       EXPECT ippget-event-life OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT multiple-operation-time-out OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1 WITH-VALUE >0
+
+       EXPECT operations-supported WITH-VALUE 0x0005 # Create-Job
+       EXPECT operations-supported WITH-VALUE 0x0006 # Send-Document
+       EXPECT operations-supported WITH-VALUE 0x000C # Hold-Job
+       EXPECT operations-supported WITH-VALUE 0x000D # Release-Job
+       EXPECT operations-supported WITH-VALUE 0x000E # Restart-Job
+       EXPECT operations-supported WITH-VALUE 0x0010 # Pause-Printer
+       EXPECT operations-supported WITH-VALUE 0x0011 # Resume-Printer
+       EXPECT operations-supported WITH-VALUE 0x0012 # Purge-Jobs
+       EXPECT operations-supported WITH-VALUE 0x0013 # Set-Printer-Attributes
+       EXPECT operations-supported WITH-VALUE 0x0014 # Set-Job-Attributes
+       EXPECT operations-supported WITH-VALUE 0x0015 # Get-Printer-Supported-Values
+       EXPECT operations-supported WITH-VALUE 0x0016 # Create-Printer-Subscriptions
+       EXPECT operations-supported WITH-VALUE 0x0018 # Get-Subscription-Attributes
+       EXPECT operations-supported WITH-VALUE 0x0019 # Get-Subscriptions
+       EXPECT operations-supported WITH-VALUE 0x001A # Renew-Subscription
+       EXPECT operations-supported WITH-VALUE 0x001B # Cancel-Subscription
+       EXPECT operations-supported WITH-VALUE 0x001C # Get-Notifications
+       EXPECT operations-supported WITH-VALUE 0x0022 # Enable-Printer
+       EXPECT operations-supported WITH-VALUE 0x0023 # Disable-Printer
+
+       EXPECT ?printer-alert OF-TYPE octetString IN-GROUP printer-attributes-tag
+       EXPECT ?printer-alert-description OF-TYPE text IN-GROUP printer-attributes-tag SAME-COUNT-AS printer-alert
+       EXPECT printer-settable-attributes-supported OF-TYPE keyword IN-GROUP printer-attributes-tag
+       EXPECT printer-state-change-time OF-TYPE integer IN-GROUP printer-attributes-tag COUNT 1
+       EXPECT printer-state-reasons OF-TYPE keyword IN-GROUP printer-attributes-tag
+}
+
+
+#
+# End of "$Id$".
+#
index cf73f9039175a62ee005b1a1751e9f9535c7349b..fc1e582b9d21a6d45970dd8da16baedf661d295a 100644 (file)
  *
  * Contents:
  *
- *   main()                       - Main entry to the sample server.
- *   clean_jobs()                 - Clean out old (completed) jobs.
- *   compare_jobs()               - Compare two jobs.
- *   copy_attribute()             - Copy a single attribute.
- *   copy_attributes()            - Copy attributes from one request to another.
- *   copy_job_attrs()             - Copy job attributes to the response.
- *   create_client()              - Accept a new network connection and create a
- *                                  client object.
- *   create_job()                 - Create a new job object from a Print-Job or
- *                                  Create-Job request.
- *   create_listener()            - Create a listener socket.
- *   create_media_col()           - Create a media-col value.
- *   create_printer()             - Create, register, and listen for connections
- *                                  to a printer object.
- *   create_requested_array()     - Create an array for requested-attributes.
- *   debug_attributes()           - Print attributes in a request or response.
- *   delete_client()              - Close the socket and free all memory used by
- *                                  a client object.
- *   delete_job()                 - Remove from the printer and free all memory
- *                                  used by a job object.
- *   delete_printer()             - Unregister, close listen sockets, and free
- *                                  all memory used by a printer object.
- *   dnssd_callback()             - Handle Bonjour registration events.
- *   find_job()                   - Find a job specified in a request.
- *   html_escape()                - Write a HTML-safe string.
- *   html_printf()                - Send formatted text to the client, quoting
- *                                  as needed.
- *   ipp_cancel_job()             - Cancel a job.
- *   ipp_create_job()             - Create a job object.
- *   ipp_get_job_attributes()     - Get the attributes for a job object.
- *   ipp_get_jobs()               - Get a list of job objects.
- *   ipp_get_printer_attributes() - Get the attributes for a printer object.
- *   ipp_print_job()              - Create a job object with an attached
- *                                  document.
- *   ipp_send_document()          - Add an attached document to a job object
- *                                  created with Create-Job.
- *   ipp_validate_job()           - Validate job creation attributes.
- *   process_client()             - Process client requests on a thread.
- *   process_http()               - Process a HTTP request.
- *   process_ipp()                - Process an IPP request.
- *   process_job()                - Process a print job.
- *   register_printer()           - Register a printer object via Bonjour.
- *   respond_http()               - Send a HTTP response.
- *   respond_ipp()                - Send an IPP response.
- *   run_printer()                - Run the printer service.
- *   usage()                      - Show program usage.
- *   valid_job_attributes()       - Determine whether the job attributes are
- *                                  valid.
  */
 
 /*
@@ -140,7 +92,7 @@ static const char * const media_supported[] =
   "na_index-3x5_3x5in",                        /* 3x5 */
   "oe_photo-l_3.5x5in",                        /* L */
   "na_index-4x6_4x6in",                        /* 4x6 */
-  "na_5x7_5x7in"                       /* 5x7 */
+  "na_5x7_5x7in"                       /* 5x7 aka 2L */
 };
 static const int media_col_sizes[][3] =
 {                                      /* media-col-database sizes */
@@ -154,7 +106,7 @@ static const int media_col_sizes[][3] =
   {  7630, 12700, _IPP_PHOTO_ONLY },   /* 3x5 */
   {  8890, 12700, _IPP_PHOTO_ONLY },   /* L */
   { 10160, 15240, _IPP_PHOTO_ONLY },   /* 4x6 */
-  { 12700, 17780, _IPP_PHOTO_ONLY }    /* 5x7 */
+  { 12700, 17780, _IPP_PHOTO_ONLY }    /* 5x7 aka 2L */
 };
 static const char * const media_type_supported[] =
                                      /* media-type-supported values */
@@ -288,16 +240,14 @@ static void               html_printf(_ipp_client_t *client, const char *format,
                                    ...) __attribute__((__format__(__printf__,
                                                                   2, 3)));
 static void            ipp_cancel_job(_ipp_client_t *client);
-#if 0
 static void            ipp_create_job(_ipp_client_t *client);
-#endif /* 0 */
 static void            ipp_get_job_attributes(_ipp_client_t *client);
 static void            ipp_get_jobs(_ipp_client_t *client);
 static void            ipp_get_printer_attributes(_ipp_client_t *client);
 static void            ipp_print_job(_ipp_client_t *client);
-#if 0
+static void            ipp_print_uri(_ipp_client_t *client);
 static void            ipp_send_document(_ipp_client_t *client);
-#endif /* 0 */
+static void            ipp_send_uri(_ipp_client_t *client);
 static void            ipp_validate_job(_ipp_client_t *client);
 static void            *process_client(_ipp_client_t *client);
 static int             process_http(_ipp_client_t *client);
@@ -317,6 +267,7 @@ static void         respond_ipp(_ipp_client_t *client, ipp_status_t status,
                        __attribute__ ((__format__ (__printf__, 3, 4)));
 static void            run_printer(_ipp_printer_t *printer);
 static void            usage(int status) __attribute__((noreturn));
+static int             valid_doc_attributes(_ipp_client_t *client);
 static int             valid_job_attributes(_ipp_client_t *client);
 
 
@@ -893,10 +844,11 @@ create_client(_ipp_printer_t *printer,    /* I - Printer */
     return (NULL);
   }
 
-  client->printer       = printer;
-  client->http.activity = time(NULL);
-  client->http.hostaddr = &(client->addr);
-  client->http.blocking = 1;
+  client->printer         = printer;
+  client->http.activity   = time(NULL);
+  client->http.hostaddr   = &(client->addr);
+  client->http.blocking   = 1;
+  client->http.wait_value = 60000;
 
  /*
   * Accept the client and get the remote address...
@@ -1207,9 +1159,11 @@ create_printer(const char *servername,   /* I - Server hostname (NULL for default)
   static const int     ops[] =         /* operations-supported values */
   {
     IPP_PRINT_JOB,
+    IPP_PRINT_URI,
     IPP_VALIDATE_JOB,
     IPP_CREATE_JOB,
     IPP_SEND_DOCUMENT,
+    IPP_SEND_URI,
     IPP_CANCEL_JOB,
     IPP_GET_JOB_ATTRIBUTES,
     IPP_GET_JOBS,
@@ -1258,6 +1212,14 @@ create_printer(const char *servername,   /* I - Server hostname (NULL for default)
     IPP_QUALITY_NORMAL,
     IPP_QUALITY_HIGH
   };
+  static const char * const referenced_uri_scheme_supported[] =
+  {                                    /* referenced-uri-scheme-supported */
+    "file",
+    "http"
+#ifdef HAVE_SSL
+    , "https"
+#endif /* HAVE_SSL */
+  };
   static const char * const sides_supported[] =
   {                                    /* sides-supported values */
     "one-sided",
@@ -1717,6 +1679,14 @@ create_printer(const char *servername,   /* I - Server hostname (NULL for default)
   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_URI,
                "printer-uri-supported", NULL, uri);
 
+  /* referenced-uri-scheme-supported */
+  ippAddStrings(printer->attrs, IPP_TAG_PRINTER,
+                IPP_TAG_URISCHEME | IPP_TAG_COPY,
+                "referenced-uri-scheme-supported",
+                (int)(sizeof(referenced_uri_scheme_supported) /
+                      sizeof(referenced_uri_scheme_supported[0])),
+                NULL, referenced_uri_scheme_supported);
+
   /* sides-default */
   ippAddString(printer->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
                "sides-default", NULL, "one-sided");
@@ -2461,7 +2431,10 @@ ipp_cancel_job(_ipp_client_t *client)    /* I - Client */
   */
 
   if ((job = find_job(client)) == NULL)
+  {
+    respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
     return;
+  }
 
  /*
   * See if the job is already completed, canceled, or aborted; if so,
@@ -2509,7 +2482,6 @@ ipp_cancel_job(_ipp_client_t *client)     /* I - Client */
 }
 
 
-#if 0
 /*
  * 'ipp_create_job()' - Create a job object.
  */
@@ -2517,8 +2489,56 @@ ipp_cancel_job(_ipp_client_t *client)    /* I - Client */
 static void
 ipp_create_job(_ipp_client_t *client)  /* I - Client */
 {
+  _ipp_job_t           *job;           /* New job */
+  cups_array_t         *ra;            /* Attributes to send in response */
+
+
+ /*
+  * Validate print job attributes...
+  */
+
+  if (!valid_job_attributes(client))
+  {
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * Do we have a file to print?
+  */
+
+  if (client->http.state == HTTP_POST_RECV)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST,
+                "Unexpected document data following request.");
+    return;
+  }
+
+ /*
+  * Create the job...
+  */
+
+  if ((job = create_job(client)) == NULL)
+  {
+    respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
+    return;
+  }
+
+ /*
+  * Return the job info...
+  */
+
+  respond_ipp(client, IPP_OK, NULL);
+
+  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+  cupsArrayAdd(ra, "job-id");
+  cupsArrayAdd(ra, "job-state");
+  cupsArrayAdd(ra, "job-state-reasons");
+  cupsArrayAdd(ra, "job-uri");
+
+  copy_job_attributes(client, job, ra);
+  cupsArrayDelete(ra);
 }
-#endif /* 0 */
 
 
 /*
@@ -2984,189 +3004,1007 @@ ipp_print_job(_ipp_client_t *client) /* I - Client */
 }
 
 
-#if 0
-/*
- * 'ipp_send_document()' - Add an attached document to a job object created with
- *                         Create-Job.
- */
-
-static void
-ipp_send_document(_ipp_client_t *client)/* I - Client */
-{
-}
-#endif /* 0 */
-
-
 /*
- * 'ipp_validate_job()' - Validate job creation attributes.
+ * 'ipp_print_uri()' - Create a job object with a referenced document.
  */
 
 static void
-ipp_validate_job(_ipp_client_t *client)        /* I - Client */
+ipp_print_uri(_ipp_client_t *client)   /* I - Client */
 {
-}
-
+  _ipp_job_t           *job;           /* New job */
+  ipp_attribute_t      *uri;           /* document-uri */
+  char                 scheme[256],    /* URI scheme */
+                       userpass[256],  /* Username and password info */
+                       hostname[256],  /* Hostname */
+                       resource[1024]; /* Resource path */
+  int                  port;           /* Port number */
+  http_uri_status_t    uri_status;     /* URI decode status */
+  http_encryption_t    encryption;     /* Encryption to use, if any */
+  http_t               *http;          /* Connection for http/https URIs */
+  http_status_t                status;         /* Access status for http/https URIs */
+  int                  infile;         /* Input file for local file URIs */
+  char                 filename[1024], /* Filename buffer */
+                       buffer[4096];   /* Copy buffer */
+  ssize_t              bytes;          /* Bytes read */
+  cups_array_t         *ra;            /* Attributes to send in response */
+  static const char * const uri_status_strings[] =
+  {                                    /* URI decode errors */
+    "URI too large.",
+    "Bad arguments to function.",
+    "Bad resource in URI.",
+    "Bad port number in URI.",
+    "Bad hostname in URI.",
+    "Bad username in URI.",
+    "Bad scheme in URI.",
+    "Bad/empty URI."
+  };
 
-/*
- * 'process_client()' - Process client requests on a thread.
- */
 
-static void *                          /* O - Exit status */
-process_client(_ipp_client_t *client)  /* I - Client */
-{
  /*
-  * Loop until we are out of requests or timeout (30 seconds)...
+  * Validate print job attributes...
   */
 
-  while (httpWait(&(client->http), 30000))
-    if (!process_http(client))
-      break;
+  if (!valid_job_attributes(client))
+  {
+    httpFlush(&(client->http));
+    return;
+  }
 
  /*
-  * Close the conection to the client and return...
+  * Do we have a file to print?
   */
 
-  delete_client(client);
-
-  return (NULL);
-}
-
-
-/*
- * 'process_http()' - Process a HTTP request.
- */
-
-int                                    /* O - 1 on success, 0 on failure */
-process_http(_ipp_client_t *client)    /* I - Client connection */
-{
-  char                 line[4096],     /* Line from client... */
-                       operation[64],  /* Operation code from socket */
-                       uri[1024],      /* URI */
-                       version[64],    /* HTTP version number string */
-                       *ptr;           /* Pointer into strings */
-  int                  major, minor;   /* HTTP version numbers */
-  http_status_t                status;         /* Transfer status */
-  ipp_state_t          state;          /* State of IPP transfer */
-
+  if (client->http.state == HTTP_POST_RECV)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST,
+                "Unexpected document data following request.");
+    return;
+  }
 
  /*
-  * Abort if we have an error on the connection...
+  * Do we have a document URI?
   */
 
-  if (client->http.error)
-    return (0);
+  if ((uri = ippFindAttribute(client->request, "document-uri",
+                              IPP_TAG_URI)) == NULL)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri.");
+    return;
+  }
 
- /*
-  * Clear state variables...
-  */
+  if (uri->num_values != 1)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values.");
+    return;
+  }
 
-  httpClearFields(&(client->http));
-  ippDelete(client->request);
-  ippDelete(client->response);
+  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
+                               scheme, sizeof(scheme), userpass,
+                               sizeof(userpass), hostname, sizeof(hostname),
+                               &port, resource, sizeof(resource));
+  if (uri_status < HTTP_URI_OK)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s",
+                uri_status_strings[uri_status - HTTP_URI_OVERFLOW]);
+    return;
+  }
 
-  client->http.activity       = time(NULL);
-  client->http.version        = HTTP_1_1;
-  client->http.keep_alive     = HTTP_KEEPALIVE_OFF;
-  client->http.data_encoding  = HTTP_ENCODE_LENGTH;
-  client->http.data_remaining = 0;
-  client->request             = NULL;
-  client->response            = NULL;
-  client->operation           = HTTP_WAITING;
+  if (strcmp(scheme, "file") &&
+#ifdef HAVE_SSL
+      strcmp(scheme, "https") &&
+#endif /* HAVE_SSL */
+      strcmp(scheme, "http"))
+  {
+    respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.",
+                scheme);
+    return;
+  }
+
+  if (!strcmp(scheme, "file") && access(resource, R_OK))
+  {
+    respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                strerror(errno));
+    return;
+  }
 
  /*
-  * Read a request from the connection...
+  * Print the job...
   */
 
-  while ((ptr = httpGets(line, sizeof(line) - 1, &(client->http))) != NULL)
-    if (*ptr)
-      break;
-
-  if (!ptr)
-    return (0);
+  if ((job = create_job(client)) == NULL)
+  {
+    respond_ipp(client, IPP_PRINTER_BUSY, "Currently printing another job.");
+    return;
+  }
 
  /*
-  * Parse the request line...
+  * Create a file for the request data...
   */
 
-  fprintf(stderr, "%s %s\n", client->http.hostname, line);
+  if (!_cups_strcasecmp(job->format, "image/jpeg"))
+    snprintf(filename, sizeof(filename), "%s/%d.jpg",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "image/png"))
+    snprintf(filename, sizeof(filename), "%s/%d.png",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "application/pdf"))
+    snprintf(filename, sizeof(filename), "%s/%d.pdf",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "application/postscript"))
+    snprintf(filename, sizeof(filename), "%s/%d.ps",
+             client->printer->directory, job->id);
+  else
+    snprintf(filename, sizeof(filename), "%s/%d.prn",
+             client->printer->directory, job->id);
 
-  switch (sscanf(line, "%63s%1023s%63s", operation, uri, version))
+  if ((job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
   {
-    case 1 :
-       fprintf(stderr, "%s Bad request line.\n", client->http.hostname);
-       respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
-       return (0);
+    job->state = IPP_JOB_ABORTED;
 
-    case 2 :
-       client->http.version = HTTP_0_9;
-       break;
+    respond_ipp(client, IPP_INTERNAL_ERROR,
+                "Unable to create print file: %s", strerror(errno));
+    return;
+  }
 
-    case 3 :
-       if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
-       {
-         fprintf(stderr, "%s Bad HTTP version.\n", client->http.hostname);
-         respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
-         return (0);
-       }
+  if (!strcmp(scheme, "file"))
+  {
+    if ((infile = open(resource, O_RDONLY)) < 0)
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 strerror(errno));
+      return;
+    }
 
-       if (major < 2)
-       {
-         client->http.version = (http_version_t)(major * 100 + minor);
-         if (client->http.version == HTTP_1_1)
-           client->http.keep_alive = HTTP_KEEPALIVE_ON;
-         else
-           client->http.keep_alive = HTTP_KEEPALIVE_OFF;
-       }
-       else
-       {
-         respond_http(client, HTTP_NOT_SUPPORTED, NULL, 0);
-         return (0);
-       }
-       break;
-  }
+    do
+    {
+      if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
+          (errno == EAGAIN || errno == EINTR))
+        bytes = 1;
+      else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
+      {
+       int error = errno;              /* Write error */
 
- /*
-  * Handle full URLs in the request line...
-  */
+       job->state = IPP_JOB_ABORTED;
 
-  if (!strncmp(client->uri, "http:", 5) || !strncmp(client->uri, "ipp:", 4))
-  {
-    char       scheme[32],             /* Method/scheme */
-               userpass[128],          /* Username:password */
-               hostname[HTTP_MAX_HOST];/* Hostname */
-    int                port;                   /* Port number */
+       close(job->fd);
+       job->fd = -1;
 
-   /*
-    * Separate the URI into its components...
-    */
+       unlink(filename);
+       close(infile);
 
-    if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
-                       userpass, sizeof(userpass),
-                       hostname, sizeof(hostname), &port,
-                       client->uri, sizeof(client->uri)) < HTTP_URI_OK)
-    {
-      fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
-      respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
-      return (0);
+       respond_ipp(client, IPP_INTERNAL_ERROR,
+                   "Unable to write print file: %s", strerror(error));
+       return;
+      }
     }
+    while (bytes > 0);
+
+    close(infile);
   }
   else
   {
-   /*
-    * Decode URI
-    */
+#ifdef HAVE_SSL
+    if (port == 443 || !strcmp(scheme, "https"))
+      encryption = HTTP_ENCRYPT_ALWAYS;
+    else
+#endif /* HAVE_SSL */
+    encryption = HTTP_ENCRYPT_IF_REQUESTED;
 
-    if (!_httpDecodeURI(client->uri, uri, sizeof(client->uri)))
+    if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL)
     {
-      fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
-      respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
-      return (0);
-    }
-  }
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 cupsLastErrorString());
+      job->state = IPP_JOB_ABORTED;
 
- /*
-  * Process the request...
-  */
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+      return;
+    }
+
+    if (!httpGet(http, resource))
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 cupsLastErrorString());
+
+      job->state = IPP_JOB_ABORTED;
+
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+      httpClose(http);
+      return;
+    }
+
+    while ((status = httpUpdate(http)) == HTTP_CONTINUE);
+
+    if (status != HTTP_OK)
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 httpStatus(status));
+
+      job->state = IPP_JOB_ABORTED;
+
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+      httpClose(http);
+      return;
+    }
+
+    while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
+    {
+      if (write(job->fd, buffer, bytes) < bytes)
+      {
+       int error = errno;              /* Write error */
+
+       job->state = IPP_JOB_ABORTED;
+
+       close(job->fd);
+       job->fd = -1;
+
+       unlink(filename);
+       httpClose(http);
+
+       respond_ipp(client, IPP_INTERNAL_ERROR,
+                   "Unable to write print file: %s", strerror(error));
+       return;
+      }
+    }
+
+    httpClose(http);
+  }
+
+  if (close(job->fd))
+  {
+    int error = errno;         /* Write error */
+
+    job->state = IPP_JOB_ABORTED;
+    job->fd    = -1;
+
+    unlink(filename);
+
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
+               strerror(error));
+    return;
+  }
+
+  job->fd       = -1;
+  job->filename = strdup(filename);
+  job->state    = IPP_JOB_PENDING;
+
+ /*
+  * Process the job...
+  */
+
+  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
+  {
+    job->state = IPP_JOB_ABORTED;
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
+    return;
+  }
+
+ /*
+  * Return the job info...
+  */
+
+  respond_ipp(client, IPP_OK, NULL);
+
+  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+  cupsArrayAdd(ra, "job-id");
+  cupsArrayAdd(ra, "job-state");
+  cupsArrayAdd(ra, "job-state-reasons");
+  cupsArrayAdd(ra, "job-uri");
+
+  copy_job_attributes(client, job, ra);
+  cupsArrayDelete(ra);
+}
+
+
+/*
+ * 'ipp_send_document()' - Add an attached document to a job object created with
+ *                         Create-Job.
+ */
+
+static void
+ipp_send_document(_ipp_client_t *client)/* I - Client */
+{
+  _ipp_job_t           *job;           /* Job information */
+  char                 filename[1024], /* Filename buffer */
+                       buffer[4096];   /* Copy buffer */
+  ssize_t              bytes;          /* Bytes read */
+  ipp_attribute_t      *attr;          /* Current attribute */
+  cups_array_t         *ra;            /* Attributes to send in response */
+
+
+ /*
+  * Get the job...
+  */
+
+  if ((job = find_job(client)) == NULL)
+  {
+    respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * See if we already have a document for this job or the job has already
+  * in a non-pending state...
+  */
+
+  if (job->state > IPP_JOB_HELD)
+  {
+    respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state.");
+    httpFlush(&(client->http));
+    return;
+  }
+  else if (job->filename || job->fd >= 0)
+  {
+    respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED,
+                "Multiple document jobs are not supported.");
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * Validate document attributes...
+  */
+
+  if (!valid_doc_attributes(client))
+  {
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * Get the document format for the job...
+  */
+
+  _cupsRWLockWrite(&(client->printer->rwlock));
+
+  if ((attr = ippFindAttribute(job->attrs, "document-format",
+                               IPP_TAG_MIMETYPE)) != NULL)
+    job->format = attr->values[0].string.text;
+  else
+    job->format = "application/octet-stream";
+
+ /*
+  * Create a file for the request data...
+  */
+
+  if (!_cups_strcasecmp(job->format, "image/jpeg"))
+    snprintf(filename, sizeof(filename), "%s/%d.jpg",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "image/png"))
+    snprintf(filename, sizeof(filename), "%s/%d.png",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "application/pdf"))
+    snprintf(filename, sizeof(filename), "%s/%d.pdf",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "application/postscript"))
+    snprintf(filename, sizeof(filename), "%s/%d.ps",
+             client->printer->directory, job->id);
+  else
+    snprintf(filename, sizeof(filename), "%s/%d.prn",
+             client->printer->directory, job->id);
+
+  job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+  _cupsRWUnlock(&(client->printer->rwlock));
+
+  if (job->fd < 0)
+  {
+    job->state = IPP_JOB_ABORTED;
+
+    respond_ipp(client, IPP_INTERNAL_ERROR,
+                "Unable to create print file: %s", strerror(errno));
+    return;
+  }
+
+  while ((bytes = httpRead2(&(client->http), buffer, sizeof(buffer))) > 0)
+  {
+    if (write(job->fd, buffer, bytes) < bytes)
+    {
+      int error = errno;               /* Write error */
+
+      job->state = IPP_JOB_ABORTED;
+
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+
+      respond_ipp(client, IPP_INTERNAL_ERROR,
+                  "Unable to write print file: %s", strerror(error));
+      return;
+    }
+  }
+
+  if (bytes < 0)
+  {
+   /*
+    * Got an error while reading the print data, so abort this job.
+    */
+
+    job->state = IPP_JOB_ABORTED;
+
+    close(job->fd);
+    job->fd = -1;
+
+    unlink(filename);
+
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to read print file.");
+    return;
+  }
+
+  if (close(job->fd))
+  {
+    int error = errno;         /* Write error */
+
+    job->state = IPP_JOB_ABORTED;
+    job->fd    = -1;
+
+    unlink(filename);
+
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
+                strerror(error));
+    return;
+  }
+
+  _cupsRWLockWrite(&(client->printer->rwlock));
+
+  job->fd       = -1;
+  job->filename = strdup(filename);
+  job->state    = IPP_JOB_PENDING;
+
+  _cupsRWUnlock(&(client->printer->rwlock));
+
+ /*
+  * Process the job...
+  */
+
+  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
+  {
+    job->state = IPP_JOB_ABORTED;
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
+    return;
+  }
+
+ /*
+  * Return the job info...
+  */
+
+  respond_ipp(client, IPP_OK, NULL);
+
+  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+  cupsArrayAdd(ra, "job-id");
+  cupsArrayAdd(ra, "job-state");
+  cupsArrayAdd(ra, "job-state-reasons");
+  cupsArrayAdd(ra, "job-uri");
+
+  copy_job_attributes(client, job, ra);
+  cupsArrayDelete(ra);
+}
+
+
+/*
+ * 'ipp_send_uri()' - Add a referenced document to a job object created with
+ *                    Create-Job.
+ */
+
+static void
+ipp_send_uri(_ipp_client_t *client)    /* I - Client */
+{
+  _ipp_job_t           *job;           /* Job information */
+  ipp_attribute_t      *uri;           /* document-uri */
+  char                 scheme[256],    /* URI scheme */
+                       userpass[256],  /* Username and password info */
+                       hostname[256],  /* Hostname */
+                       resource[1024]; /* Resource path */
+  int                  port;           /* Port number */
+  http_uri_status_t    uri_status;     /* URI decode status */
+  http_encryption_t    encryption;     /* Encryption to use, if any */
+  http_t               *http;          /* Connection for http/https URIs */
+  http_status_t                status;         /* Access status for http/https URIs */
+  int                  infile;         /* Input file for local file URIs */
+  char                 filename[1024], /* Filename buffer */
+                       buffer[4096];   /* Copy buffer */
+  ssize_t              bytes;          /* Bytes read */
+  ipp_attribute_t      *attr;          /* Current attribute */
+  cups_array_t         *ra;            /* Attributes to send in response */
+  static const char * const uri_status_strings[] =
+  {                                    /* URI decode errors */
+    "URI too large.",
+    "Bad arguments to function.",
+    "Bad resource in URI.",
+    "Bad port number in URI.",
+    "Bad hostname in URI.",
+    "Bad username in URI.",
+    "Bad scheme in URI.",
+    "Bad/empty URI."
+  };
+
+
+ /*
+  * Get the job...
+  */
+
+  if ((job = find_job(client)) == NULL)
+  {
+    respond_ipp(client, IPP_NOT_FOUND, "Job does not exist.");
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * See if we already have a document for this job or the job has already
+  * in a non-pending state...
+  */
+
+  if (job->state > IPP_JOB_HELD)
+  {
+    respond_ipp(client, IPP_NOT_POSSIBLE, "Job is not in a pending state.");
+    httpFlush(&(client->http));
+    return;
+  }
+  else if (job->filename || job->fd >= 0)
+  {
+    respond_ipp(client, IPP_MULTIPLE_JOBS_NOT_SUPPORTED,
+                "Multiple document jobs are not supported.");
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * Validate document attributes...
+  */
+
+  if (!valid_doc_attributes(client))
+  {
+    httpFlush(&(client->http));
+    return;
+  }
+
+ /*
+  * Do we have a file to print?
+  */
+
+  if (client->http.state == HTTP_POST_RECV)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST,
+                "Unexpected document data following request.");
+    return;
+  }
+
+ /*
+  * Do we have a document URI?
+  */
+
+  if ((uri = ippFindAttribute(client->request, "document-uri",
+                              IPP_TAG_URI)) == NULL)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST, "Missing document-uri.");
+    return;
+  }
+
+  if (uri->num_values != 1)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST, "Too many document-uri values.");
+    return;
+  }
+
+  uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
+                               scheme, sizeof(scheme), userpass,
+                               sizeof(userpass), hostname, sizeof(hostname),
+                               &port, resource, sizeof(resource));
+  if (uri_status < HTTP_URI_OK)
+  {
+    respond_ipp(client, IPP_BAD_REQUEST, "Bad document-uri: %s",
+                uri_status_strings[uri_status - HTTP_URI_OVERFLOW]);
+    return;
+  }
+
+  if (strcmp(scheme, "file") &&
+#ifdef HAVE_SSL
+      strcmp(scheme, "https") &&
+#endif /* HAVE_SSL */
+      strcmp(scheme, "http"))
+  {
+    respond_ipp(client, IPP_URI_SCHEME, "URI scheme \"%s\" not supported.",
+                scheme);
+    return;
+  }
+
+  if (!strcmp(scheme, "file") && access(resource, R_OK))
+  {
+    respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                strerror(errno));
+    return;
+  }
+
+ /*
+  * Get the document format for the job...
+  */
+
+  _cupsRWLockWrite(&(client->printer->rwlock));
+
+  if ((attr = ippFindAttribute(job->attrs, "document-format",
+                               IPP_TAG_MIMETYPE)) != NULL)
+    job->format = attr->values[0].string.text;
+  else
+    job->format = "application/octet-stream";
+
+ /*
+  * Create a file for the request data...
+  */
+
+  if (!_cups_strcasecmp(job->format, "image/jpeg"))
+    snprintf(filename, sizeof(filename), "%s/%d.jpg",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "image/png"))
+    snprintf(filename, sizeof(filename), "%s/%d.png",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "application/pdf"))
+    snprintf(filename, sizeof(filename), "%s/%d.pdf",
+             client->printer->directory, job->id);
+  else if (!_cups_strcasecmp(job->format, "application/postscript"))
+    snprintf(filename, sizeof(filename), "%s/%d.ps",
+             client->printer->directory, job->id);
+  else
+    snprintf(filename, sizeof(filename), "%s/%d.prn",
+             client->printer->directory, job->id);
+
+  job->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+
+  _cupsRWUnlock(&(client->printer->rwlock));
+
+  if (job->fd < 0)
+  {
+    job->state = IPP_JOB_ABORTED;
+
+    respond_ipp(client, IPP_INTERNAL_ERROR,
+                "Unable to create print file: %s", strerror(errno));
+    return;
+  }
+
+  if (!strcmp(scheme, "file"))
+  {
+    if ((infile = open(resource, O_RDONLY)) < 0)
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 strerror(errno));
+      return;
+    }
+
+    do
+    {
+      if ((bytes = read(infile, buffer, sizeof(buffer))) < 0 &&
+          (errno == EAGAIN || errno == EINTR))
+        bytes = 1;
+      else if (bytes > 0 && write(job->fd, buffer, bytes) < bytes)
+      {
+       int error = errno;              /* Write error */
+
+       job->state = IPP_JOB_ABORTED;
+
+       close(job->fd);
+       job->fd = -1;
+
+       unlink(filename);
+       close(infile);
+
+       respond_ipp(client, IPP_INTERNAL_ERROR,
+                   "Unable to write print file: %s", strerror(error));
+       return;
+      }
+    }
+    while (bytes > 0);
+
+    close(infile);
+  }
+  else
+  {
+#ifdef HAVE_SSL
+    if (port == 443 || !strcmp(scheme, "https"))
+      encryption = HTTP_ENCRYPT_ALWAYS;
+    else
+#endif /* HAVE_SSL */
+    encryption = HTTP_ENCRYPT_IF_REQUESTED;
+
+    if ((http = httpConnectEncrypt(hostname, port, encryption)) == NULL)
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 cupsLastErrorString());
+      job->state = IPP_JOB_ABORTED;
+
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+      return;
+    }
+
+    if (!httpGet(http, resource))
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 cupsLastErrorString());
+
+      job->state = IPP_JOB_ABORTED;
+
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+      httpClose(http);
+      return;
+    }
+
+    while ((status = httpUpdate(http)) == HTTP_CONTINUE);
+
+    if (status != HTTP_OK)
+    {
+      respond_ipp(client, IPP_DOCUMENT_ACCESS_ERROR, "Unable to access URI: %s",
+                 httpStatus(status));
+
+      job->state = IPP_JOB_ABORTED;
+
+      close(job->fd);
+      job->fd = -1;
+
+      unlink(filename);
+      httpClose(http);
+      return;
+    }
+
+    while ((bytes = httpRead2(http, buffer, sizeof(buffer))) > 0)
+    {
+      if (write(job->fd, buffer, bytes) < bytes)
+      {
+       int error = errno;              /* Write error */
+
+       job->state = IPP_JOB_ABORTED;
+
+       close(job->fd);
+       job->fd = -1;
+
+       unlink(filename);
+       httpClose(http);
+
+       respond_ipp(client, IPP_INTERNAL_ERROR,
+                   "Unable to write print file: %s", strerror(error));
+       return;
+      }
+    }
+
+    httpClose(http);
+  }
+
+  if (close(job->fd))
+  {
+    int error = errno;         /* Write error */
+
+    job->state = IPP_JOB_ABORTED;
+    job->fd    = -1;
+
+    unlink(filename);
+
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to write print file: %s",
+               strerror(error));
+    return;
+  }
+
+  _cupsRWLockWrite(&(client->printer->rwlock));
+
+  job->fd       = -1;
+  job->filename = strdup(filename);
+  job->state    = IPP_JOB_PENDING;
+
+  _cupsRWUnlock(&(client->printer->rwlock));
+
+ /*
+  * Process the job...
+  */
+
+  if (!_cupsThreadCreate((_cups_thread_func_t)process_job, job))
+  {
+    job->state = IPP_JOB_ABORTED;
+    respond_ipp(client, IPP_INTERNAL_ERROR, "Unable to process job.");
+    return;
+  }
+
+ /*
+  * Return the job info...
+  */
+
+  respond_ipp(client, IPP_OK, NULL);
+
+  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
+  cupsArrayAdd(ra, "job-id");
+  cupsArrayAdd(ra, "job-state");
+  cupsArrayAdd(ra, "job-state-reasons");
+  cupsArrayAdd(ra, "job-uri");
+
+  copy_job_attributes(client, job, ra);
+  cupsArrayDelete(ra);
+}
+
+
+/*
+ * 'ipp_validate_job()' - Validate job creation attributes.
+ */
+
+static void
+ipp_validate_job(_ipp_client_t *client)        /* I - Client */
+{
+  if (valid_job_attributes(client))
+    respond_ipp(client, IPP_OK, NULL);
+}
+
+
+/*
+ * 'process_client()' - Process client requests on a thread.
+ */
+
+static void *                          /* O - Exit status */
+process_client(_ipp_client_t *client)  /* I - Client */
+{
+ /*
+  * Loop until we are out of requests or timeout (30 seconds)...
+  */
+
+  while (httpWait(&(client->http), 30000))
+    if (!process_http(client))
+      break;
+
+ /*
+  * Close the conection to the client and return...
+  */
+
+  delete_client(client);
+
+  return (NULL);
+}
+
+
+/*
+ * 'process_http()' - Process a HTTP request.
+ */
+
+int                                    /* O - 1 on success, 0 on failure */
+process_http(_ipp_client_t *client)    /* I - Client connection */
+{
+  char                 line[4096],     /* Line from client... */
+                       operation[64],  /* Operation code from socket */
+                       uri[1024],      /* URI */
+                       version[64],    /* HTTP version number string */
+                       *ptr;           /* Pointer into strings */
+  int                  major, minor;   /* HTTP version numbers */
+  http_status_t                status;         /* Transfer status */
+  ipp_state_t          state;          /* State of IPP transfer */
+
+
+ /*
+  * Abort if we have an error on the connection...
+  */
+
+  if (client->http.error)
+    return (0);
+
+ /*
+  * Clear state variables...
+  */
+
+  httpClearFields(&(client->http));
+  ippDelete(client->request);
+  ippDelete(client->response);
+
+  client->http.activity       = time(NULL);
+  client->http.version        = HTTP_1_1;
+  client->http.keep_alive     = HTTP_KEEPALIVE_OFF;
+  client->http.data_encoding  = HTTP_ENCODE_LENGTH;
+  client->http.data_remaining = 0;
+  client->request             = NULL;
+  client->response            = NULL;
+  client->operation           = HTTP_WAITING;
+
+ /*
+  * Read a request from the connection...
+  */
+
+  while ((ptr = httpGets(line, sizeof(line) - 1, &(client->http))) != NULL)
+    if (*ptr)
+      break;
+
+  if (!ptr)
+    return (0);
+
+ /*
+  * Parse the request line...
+  */
+
+  fprintf(stderr, "%s %s\n", client->http.hostname, line);
+
+  switch (sscanf(line, "%63s%1023s%63s", operation, uri, version))
+  {
+    case 1 :
+       fprintf(stderr, "%s Bad request line.\n", client->http.hostname);
+       respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
+       return (0);
+
+    case 2 :
+       client->http.version = HTTP_0_9;
+       break;
+
+    case 3 :
+       if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
+       {
+         fprintf(stderr, "%s Bad HTTP version.\n", client->http.hostname);
+         respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
+         return (0);
+       }
+
+       if (major < 2)
+       {
+         client->http.version = (http_version_t)(major * 100 + minor);
+         if (client->http.version == HTTP_1_1)
+           client->http.keep_alive = HTTP_KEEPALIVE_ON;
+         else
+           client->http.keep_alive = HTTP_KEEPALIVE_OFF;
+       }
+       else
+       {
+         respond_http(client, HTTP_NOT_SUPPORTED, NULL, 0);
+         return (0);
+       }
+       break;
+  }
+
+ /*
+  * Handle full URLs in the request line...
+  */
+
+  if (!strncmp(client->uri, "http:", 5) || !strncmp(client->uri, "ipp:", 4))
+  {
+    char       scheme[32],             /* Method/scheme */
+               userpass[128],          /* Username:password */
+               hostname[HTTP_MAX_HOST];/* Hostname */
+    int                port;                   /* Port number */
+
+   /*
+    * Separate the URI into its components...
+    */
+
+    if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme),
+                       userpass, sizeof(userpass),
+                       hostname, sizeof(hostname), &port,
+                       client->uri, sizeof(client->uri)) < HTTP_URI_OK)
+    {
+      fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
+      respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
+      return (0);
+    }
+  }
+  else
+  {
+   /*
+    * Decode URI
+    */
+
+    if (!_httpDecodeURI(client->uri, uri, sizeof(client->uri)))
+    {
+      fprintf(stderr, "%s Bad URI \"%s\".\n", client->http.hostname, uri);
+      respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
+      return (0);
+    }
+  }
+
+ /*
+  * Process the request...
+  */
 
   if (!strcmp(operation, "GET"))
     client->http.state = HTTP_GET;
@@ -3375,7 +4213,7 @@ process_http(_ipp_client_t *client)       /* I - Client connection */
          if (state == IPP_ERROR)
          {
             fprintf(stderr, "%s IPP read error (%s).\n", client->http.hostname,
-                   ippOpString(client->request->request.op.operation_id));
+                   cupsLastErrorString());
            respond_http(client, HTTP_BAD_REQUEST, NULL, 0);
            return (0);
          }
@@ -3565,10 +4403,26 @@ process_ipp(_ipp_client_t *client)      /* I - Client */
               ipp_print_job(client);
               break;
 
+         case IPP_PRINT_URI :
+              ipp_print_uri(client);
+              break;
+
          case IPP_VALIDATE_JOB :
               ipp_validate_job(client);
               break;
 
+          case IPP_CREATE_JOB :
+              ipp_create_job(client);
+              break;
+
+          case IPP_SEND_DOCUMENT :
+              ipp_send_document(client);
+              break;
+
+          case IPP_SEND_URI :
+              ipp_send_uri(client);
+              break;
+
          case IPP_CANCEL_JOB :
               ipp_cancel_job(client);
               break;
@@ -3942,6 +4796,24 @@ respond_ipp(_ipp_client_t *client,       /* I - Client */
 }
 
 
+/*
+ * 'respond_unsupported()' - Respond with an unsupported attribute.
+ */
+
+static void
+respond_unsupported(
+    _ipp_client_t   *client,           /* I - Client */
+    ipp_attribute_t *attr)             /* I - Atribute */
+{
+  if (!client->response->attrs)
+    respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.",
+               attr->name, attr->num_values > 1 ? "1setOf " : "",
+               ippTagString(attr->value_tag));
+
+  copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0);
+}
+
+
 /*
  * 'run_printer()' - Run the printer service.
  */
@@ -4064,37 +4936,27 @@ usage(int status)                       /* O - Exit status */
 
 
 /*
- * 'valid_job_attributes()' - Determine whether the job attributes are valid.
+ * 'valid_doc_attributes()' - Determine whether the document attributes are
+ *                            valid.
  *
- * When one or more job attributes are invalid, this function adds a suitable
- * response and attributes to the unsupported group.
+ * When one or more document attributes are invalid, this function adds a
+ * suitable response and attributes to the unsupported group.
  */
 
 static int                             /* O - 1 if valid, 0 if not */
-valid_job_attributes(
+valid_doc_attributes(
     _ipp_client_t *client)             /* I - Client */
 {
   int                  i;              /* Looping var */
   ipp_attribute_t      *attr,          /* Current attribute */
                        *supported;     /* document-format-supported */
   const char           *format = NULL; /* document-format value */
-  int                  valid = 1;      /* Valid attributes? */
 
 
  /*
   * Check operation attributes...
   */
 
-#define respond_unsupported(client, attr) \
-  if (valid) \
-  { \
-    respond_ipp(client, IPP_ATTRIBUTES, "Unsupported %s %s%s value.", \
-               attr->name, attr->num_values > 1 ? "1setOf " : "", \
-               ippTagString(attr->value_tag)); \
-    valid = 0; \
-  } \
-  copy_attribute(client->response, attr, IPP_TAG_UNSUPPORTED_GROUP, 0)
-
   if ((attr = ippFindAttribute(client->request, "compression",
                                IPP_TAG_ZERO)) != NULL)
   {
@@ -4108,8 +4970,10 @@ valid_job_attributes(
       respond_unsupported(client, attr);
     }
     else
-      fprintf(stderr, "%s Print-Job compression=\"%s\"\n",
-              client->http.hostname, attr->values[0].string.text);
+      fprintf(stderr, "%s %s compression=\"%s\"\n",
+              client->http.hostname,
+              ippOpString(client->request->request.op.operation_id),
+              attr->values[0].string.text);
   }
 
  /*
@@ -4136,7 +5000,8 @@ valid_job_attributes(
     format = "application/octet-stream";
 
   if (!strcmp(format, "application/octet-stream") &&
-      client->request->request.op.operation_id != IPP_VALIDATE_JOB)
+      (client->request->request.op.operation_id == IPP_PRINT_JOB ||
+       client->request->request.op.operation_id != IPP_SEND_DOCUMENT))
   {
    /*
     * Auto-type the file using the first 4 bytes of the file...
@@ -4186,6 +5051,34 @@ valid_job_attributes(
     }
   }
 
+  return (!client->response->attrs ||
+          !client->response->attrs->next ||
+          !client->response->attrs->next->next);
+}
+
+
+/*
+ * 'valid_job_attributes()' - Determine whether the job attributes are valid.
+ *
+ * When one or more job attributes are invalid, this function adds a suitable
+ * response and attributes to the unsupported group.
+ */
+
+static int                             /* O - 1 if valid, 0 if not */
+valid_job_attributes(
+    _ipp_client_t *client)             /* I - Client */
+{
+  int                  i;              /* Looping var */
+  ipp_attribute_t      *attr,          /* Current attribute */
+                       *supported;     /* xxx-supported attribute */
+
+
+ /*
+  * Check operation attributes...
+  */
+
+  valid_doc_attributes(client);
+
  /*
   * Check the various job template attributes...
   */
@@ -4365,7 +5258,9 @@ valid_job_attributes(
     }
   }
 
-  return (valid);
+  return (!client->response->attrs ||
+          !client->response->attrs->next ||
+          !client->response->attrs->next->next);
 }
 
 
index a63b4ee5a24a38b4856a34a080cce811ec92a0a2..d908b553adabf65bfddb3f19aed3cf6148cbb0cb 100644 (file)
@@ -217,7 +217,8 @@ main(int  argc,                             /* I - Number of command-line args */
                        filename[1024], /* Real filename */
                        testname[1024], /* Real test filename */
                        uri[1024];      /* Copy of printer URI */
-  const char           *testfile;      /* Test file to use */
+  const char           *ext,           /* Extension on filename */
+                       *testfile;      /* Test file to use */
   int                  interval,       /* Test interval in microseconds */
                        repeat;         /* Repeat count */
   _cups_vars_t         vars;           /* Variables */
@@ -401,6 +402,43 @@ main(int  argc,                            /* I - Number of command-line args */
               }
               else
                vars.filename = argv[i];
+
+              if ((ext = strrchr(vars.filename, '.')) != NULL)
+              {
+               /*
+                * Guess the MIME media type based on the extension...
+                */
+
+                if (!_cups_strcasecmp(ext, ".gif"))
+                  set_variable(&vars, "filetype", "image/gif");
+                else if (!_cups_strcasecmp(ext, ".htm") ||
+                         !_cups_strcasecmp(ext, ".html"))
+                  set_variable(&vars, "filetype", "text/html");
+                else if (!_cups_strcasecmp(ext, ".jpg"))
+                  set_variable(&vars, "filetype", "image/jpeg");
+                else if (!_cups_strcasecmp(ext, ".pdf"))
+                  set_variable(&vars, "filetype", "application/pdf");
+                else if (!_cups_strcasecmp(ext, ".png"))
+                  set_variable(&vars, "filetype", "image/png");
+                else if (!_cups_strcasecmp(ext, ".ps"))
+                  set_variable(&vars, "filetype", "application/postscript");
+                else if (!_cups_strcasecmp(ext, ".ras"))
+                  set_variable(&vars, "filetype", "image/pwg-raster");
+                else if (!_cups_strcasecmp(ext, ".txt"))
+                  set_variable(&vars, "filetype", "text/plain");
+                else if (!_cups_strcasecmp(ext, ".xps"))
+                  set_variable(&vars, "filetype", "application/openxps");
+                else
+                 set_variable(&vars, "filetype", "application/octet-stream");
+              }
+              else
+              {
+               /*
+                * Use the "auto-type" MIME media type...
+                */
+
+               set_variable(&vars, "filetype", "application/octet-stream");
+              }
              break;
 
           case 'i' : /* Test every N seconds */
@@ -2092,6 +2130,9 @@ do_tests(_cups_vars_t *vars,              /* I - Variables */
          response = cupsGetResponse(http, resource);
          status   = httpGetStatus(http);
        }
+
+       if (!Cancel && status == HTTP_ERROR)
+         httpReconnect(http);
       }
     }