diff --git a/CHANGES.md b/CHANGES.md index 2fe3fdab6..5a3dc0455 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,8 @@ libcups v3.0rc1 (TBD) - Updated the various tool man pages, usage output, and examples. - Updated `ippCreateRequestedArray` for the Get-Documents and Get-Output-Device-Attributes operations. +- Updated `ipptool` to validate IPP, PDF, and .strings files using the + "WITH-[ALL-]CONTENT" predicate (Issue #87) - Now use installed PDFio library, if available. - Now use NotoSansMono font for `ipptransform` text conversions. - Brought back IPP/2.x and related conformance test files (Issue #85) diff --git a/examples/Makefile b/examples/Makefile index 3c6e32233..85fd31236 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -85,7 +85,8 @@ TESTFILES = \ rfc3995-3996.test \ rfc3998.test \ set-attrs-hold.test \ - validate-job.test + validate-job.test \ + validate-resources.test # diff --git a/examples/validate-resources.test b/examples/validate-resources.test new file mode 100644 index 000000000..7bd5849ac --- /dev/null +++ b/examples/validate-resources.test @@ -0,0 +1,29 @@ +# Validate printer resources +{ + NAME "Validate Printer Resources" + OPERATION Get-Printer-Attributes + GROUP operation-attributes-tag + ATTR charset attributes-charset utf-8 + ATTR language attributes-natural-language en + ATTR uri printer-uri $uri + ATTR name requesting-user-name $user + + STATUS successful-ok + + EXPECT ?printer-charge-info-uri OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-CONTENT valid + + EXPECT printer-icc-profiles OF-TYPE collection IN-GROUP printer-attributes-tag DEFINE-MATCH PRINTER_HAS_ICC_PROFILES + EXPECT printer-icc-profiles/profile-uri OF-TYPE uri COUNT 1 WITH-CONTENT valid IF-DEFINED PRINTER_HAS_ICC_PROFILES + + EXPECT ?printer-icons OF-TYPE uri IN-GROUP printer-attributes-tag WITH-ALL-CONTENT valid-icon + + EXPECT ?printer-more-info OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-CONTENT valid + + EXPECT ?printer-more-info-manufacturer OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-CONTENT valid + + EXPECT ?printer-privacy-policy-uri OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-CONTENT valid + + EXPECT ?printer-static-resource-directory-uri OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-CONTENT valid + + EXPECT ?printer-strings-uri OF-TYPE uri IN-GROUP printer-attributes-tag COUNT 1 WITH-CONTENT valid +} diff --git a/man/ipptool.1 b/man/ipptool.1 index 49b5c5cda..83a5292d2 100644 --- a/man/ipptool.1 +++ b/man/ipptool.1 @@ -1,13 +1,13 @@ .\" .\" ipptool man page. .\" -.\" Copyright © 2021-2023 by OpenPrinting. +.\" Copyright © 2021-2024 by OpenPrinting. .\" Copyright © 2010-2019 by Apple Inc. .\" .\" Licensed under Apache License v2.0. See the file "LICENSE" for more .\" information. .\" -.TH ipptool 1 "CUPS" "2023-11-22" "OpenPrinting" +.TH ipptool 1 "CUPS" "2024-09-12" "OpenPrinting" .SH NAME ipptool \- perform internet printing protocol requests .SH SYNOPSIS @@ -227,6 +227,8 @@ The following standard test files are available: identify\-printer\-display.test - Identify a printer via the display ipp\-1.1.test - Run IPP/1.1 conformance tests ipp\-2.0.test - Run IPP/2.0 conformance tests + ipp\-2.1.test - Run IPP/2.1 conformance tests + ipp\-2.2.test - Run IPP/2.2 conformance tests print\-job.test - Print a file print\-job\-and\-wait.test - Print a file and wait for completion print\-job\-deflate.test - Print a file with deflate compression @@ -239,8 +241,21 @@ The following standard test files are available: print\-job\-password.test - Print a file with a password/PIN print\-job\-raster.test - Print a generated raster file print\-uri.test - Print a URI/URL + pwg5100.1.test - Test PWG 5100.1 (Finishings) conformance + pwg5100.2.test - Test PWG 5100.2 (output-bin) conformance + pwg5100.3.test - Test PWG 5100.3 (Production) conformance + pwg5100.5.test - Test PWG 5100.5 (Document Object) conformance + pwg5100.6.test - Test PWG 5100.6 (Page Overrides) conformance + pwg5100.7.test - Test PWG 5100.7 (Job Extensions) conformance + pwg5100.8.test - Test PWG 5100.8 (-actuals) conformance + pwg5100.9.test - Test PWG 5100.9 (Alerts) conformance + pwg5100.11.test - Test PWG 5100.11 (Enterprise) conformance + rfc3380.test - Test RFC 3380 (Job and Printer Set) conformance + rfc3995-3996.test - Test RFC 3995/3996 (Notifications) conformance + rfc3998.test - Test RFC 3998 (Admin) conformance set\-attrs\-hold.test - Test setting job-hold-until to hold a job validate\-job.test - Validate a job ticket + validate\-resources.test - Validate printer resource files and web pages .fi .PP The following standard document files are available: @@ -287,4 +302,4 @@ IANA IPP Registry (https://www.iana.org/assignments/ipp\-registrations), PWG Internet Printing Protocol Workgroup (https://www.pwg.org/ipp), RFC 8011 (https://datatracker.ietf.org/doc/html/rfc8011) .SH COPYRIGHT -Copyright \[co] 2021-2023 by OpenPrinting. +Copyright \[co] 2021-2024 by OpenPrinting. diff --git a/man/ipptoolfile.5 b/man/ipptoolfile.5 index fa8ca5acf..1bf9e0182 100644 --- a/man/ipptoolfile.5 +++ b/man/ipptoolfile.5 @@ -1,13 +1,13 @@ .\" .\" ipptoolfile man page. .\" -.\" Copyright © 2021-2023 by OpenPrinting. +.\" Copyright © 2021-2024 by OpenPrinting. .\" Copyright © 2010-2021 by Apple Inc. .\" .\" Licensed under Apache License v2.0. See the file "LICENSE" for more .\" information. .\" -.TH ipptoolfile 5 "CUPS" "2023-11-17" "OpenPrinting" +.TH ipptoolfile 5 "CUPS" "2024-09-12" "OpenPrinting" .SH NAME ipptoolfile \- ipptool file format .SH DESCRIPTION @@ -352,6 +352,9 @@ A "http" or "https" URI must respond to a GET request while a "ipp" or "ipps" UR .TP 5 \fBWITH\-CONTENT valid\fR Requires that all "http" and "https" URI values be accessible and provide valid content. +Currently +.B ipptool +is able to validate CSS, HTML, ICC, IPP, JPEG, PDF, PNG, and Apple .strings files. .TP 5 \fBWITH\-ALL\-CONTENT valid-icon\fR .TP 5 @@ -886,4 +889,4 @@ PWG 5101.1-2023: PWG Media Standardized Names v2.1 (https://ftp.pwg.org/pub/pwg/ .PP RFC 8011 (https://datatracker.ietf.org/doc/html/rfc8011) .SH COPYRIGHT -Copyright \[co] 2021-2023 by OpenPrinting. +Copyright \[co] 2021-2024 by OpenPrinting. diff --git a/pdfio b/pdfio index 206f75403..986c5f043 160000 --- a/pdfio +++ b/pdfio @@ -1 +1 @@ -Subproject commit 206f75403a085c6bf2033f8e2c0eae2c81ac4e82 +Subproject commit 986c5f0438ea71f549244b841c3775d94659849a diff --git a/tools/Makefile b/tools/Makefile index 303d82952..6e9ecee5d 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -172,7 +172,7 @@ ippfind-static: ippfind.o ../cups/$(LIBCUPS_STATIC) ipptool: ipptool.o ../cups/$(LIBCUPS) ipptool-static echo Linking $@... - $(CC) $(LDFLAGS) $(OPTIM) -o $@ ipptool.o $(LINKCUPS) $(LIBS) + $(CC) $(LDFLAGS) $(OPTIM) -o $@ ipptool.o $(LINKCUPS) $(XFORMLIBS) $(LIBS) $(CODE_SIGN) $(CSFLAGS) $@ @@ -182,7 +182,7 @@ ipptool: ipptool.o ../cups/$(LIBCUPS) ipptool-static ipptool-static: ipptool.o ../cups/$(LIBCUPS_STATIC) echo Linking $@... - $(CC) $(LDFLAGS) $(OPTIM) -o $@ ipptool.o ../cups/$(LIBCUPS_STATIC) $(LIBS) + $(CC) $(LDFLAGS) $(OPTIM) -o $@ ipptool.o ../cups/$(LIBCUPS_STATIC) $(XFORMLIBS) $(LIBS) $(CODE_SIGN) $(CSFLAGS) $@ diff --git a/tools/ipptool.c b/tools/ipptool.c index 653647704..b6aa1ff2e 100644 --- a/tools/ipptool.c +++ b/tools/ipptool.c @@ -1,7 +1,7 @@ // // ipptool command for CUPS. // -// Copyright © 2021-2023 by OpenPrinting. +// Copyright © 2021-2024 by OpenPrinting. // Copyright © 2020 by The Printer Working Group. // Copyright © 2007-2021 by Apple Inc. // Copyright © 1997-2007 by Easy Software Products. @@ -26,6 +26,7 @@ #ifndef O_BINARY # define O_BINARY 0 #endif // !O_BINARY +#include // @@ -238,6 +239,7 @@ static bool parse_generate_file(ipp_file_t *f, ipptool_test_t *data); static bool parse_monitor_printer_state(ipp_file_t *f, ipptool_test_t *data); static const char *password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data); static void pause_message(const char *message); +static bool pdf_error_cb(pdfio_file_t *pdf, const char *message, cups_array_t *errors); static void print_attr(cups_file_t *outfile, ipptool_output_t output, ipp_attribute_t *attr, ipp_tag_t *group); static ipp_attribute_t *print_csv(ipptool_test_t *data, ipp_t *ipp, ipp_attribute_t *attr, int num_displayed, char **displayed, size_t *widths); static void print_fatal_error(ipptool_test_t *data, const char *s, ...) _CUPS_FORMAT(2, 3); @@ -4121,6 +4123,23 @@ pause_message(const char *message) // I - Message } +// +// 'pdf_error_cb()' - PDFio error callback. +// + +static bool // O - `false` to stop loading +pdf_error_cb(pdfio_file_t *pdf, // I - PDF file + const char *message, // I - Error message + cups_array_t *errors) // I - Array of error messages +{ + (void)pdf; + + add_stringf(errors, "Unable to open PDF file: %s", message); + + return (false); +} + + // // 'print_attr()' - Print an attribute on the screen. // @@ -6700,7 +6719,8 @@ with_content( char scheme[256], // Scheme userpass[256], // Username:password (not used) host[256], // Hostname - resource[256]; // Resource path + resource[256], // Resource path + *resptr; // Pointer into resource int port; // Port number http_encryption_t encryption; // Encryption mode http_uri_status_t uri_status; // URI decoding status @@ -6720,6 +6740,9 @@ with_content( continue; } + if ((resptr = strchr(resource, '#')) != NULL) + *resptr = '\0'; // Strip HTML target ("...#target") + if (strcmp(scheme, "http") && strcmp(scheme, "https") && strcmp(scheme, "ipp") && strcmp(scheme, "ipps")) { add_stringf(errors, "Unsupported URI scheme for '%s'.", uri); @@ -6789,7 +6812,7 @@ with_content( if (status != HTTP_STATUS_OK) { - add_stringf(errors, "Got unexpected status %d for HEAD request to '%s'.", (int)status, uri); + add_stringf(errors, "Got unexpected status %d for GET request to '%s'.", (int)status, uri); ret = false; goto get_done; } @@ -6833,14 +6856,70 @@ with_content( } else if (!_cups_strcasecmp(content_type, "image/jpeg") || !_cups_strcasecmp(content_type, "image/png")) { + // Validate image content if (!valid_image(filename, &width, &height, &depth)) { - add_stringf(errors, "Unable to load image '%s'.", uri); + add_stringf(errors, "Unable to open image '%s'.", uri); ret = false; goto get_done; } } - else if (!_cups_strcasecmp(content_type, "application/pdf") || !_cups_strcasecmp(content_type, "application/ipp") || !_cups_strcasecmp(content_type, "application/vnd.iccprofile") || !_cups_strcasecmp(content_type, "text/css") || !_cups_strcasecmp(content_type, "text/html") || !_cups_strcasecmp(content_type, "text/strings")) + else if (!_cups_strcasecmp(content_type, "text/strings")) + { + // Validate .strings content... + cups_lang_t *lang; // Temporary language + + if ((lang = cupsLangFind("zz")) == NULL) + { + add_stringf(errors, "Unable to validate '%s'.", uri); + ret = false; + goto get_done; + } + + if (!cupsLangLoadStrings(lang, filename, /*strings*/NULL)) + { + add_stringf(errors, "Unable to open '%s': %s", uri, cupsGetErrorString()); + ret = false; + goto get_done; + } + } + else if (!_cups_strcasecmp(content_type, "application/pdf")) + { + // Validate PDF content... + pdfio_file_t *pdf; // PDF file + + if ((pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_data*/NULL, (pdfio_error_cb_t)pdf_error_cb, errors)) == NULL) + { + ret = false; + goto get_done; + } + + pdfioFileClose(pdf); + } + else if (!_cups_strcasecmp(content_type, "application/ipp")) + { + ipp_t *ipp = ippNew(); // IPP message + + if ((fd = open(filename, O_RDONLY | O_BINARY)) < 0) + { + add_stringf(errors, "Unable to open '%s': %s", uri, strerror(errno)); + ippDelete(ipp); + ret = false; + goto get_done; + } + else if (ippReadFile(fd, ipp) != IPP_STATE_DATA) + { + add_stringf(errors, "Unable to read '%s': %s", uri, cupsGetErrorString()); + ippDelete(ipp); + close(fd); + ret = false; + goto get_done; + } + + ippDelete(ipp); + close(fd); + } + else if (!_cups_strcasecmp(content_type, "application/vnd.iccprofile") || !_cups_strcasecmp(content_type, "text/css") || !_cups_strcasecmp(content_type, "text/html") || !_cups_strncasecmp(content_type, "text/html;", 10)) { // Just require these files to be non-empty for now, might add more checks in the future... if (fileinfo.st_size == 0) @@ -6852,7 +6931,7 @@ with_content( } else { - add_stringf(errors, "Got unexpected Content-Type '%s' for HEAD request to '%s'.", content_type, uri); + add_stringf(errors, "Got unexpected Content-Type '%s' for GET request to '%s'.", content_type, uri); ret = false; goto get_done; }