From 5a662dc06039959a7a12925c21abf9d2e233b618 Mon Sep 17 00:00:00 2001 From: msweet Date: Wed, 16 Dec 2009 00:13:28 +0000 Subject: [PATCH] Merge changes from CUPS 1.5svn-r8933. git-svn-id: svn+ssh://src.apple.com/svn/cups/easysw/current@1788 a1ca3aef-8c08-0410-bb20-df032aa958be --- CHANGES-1.4.txt | 6 + berkeley/lpq.c | 18 +- config-scripts/cups-common.m4 | 2 + config-scripts/cups-defaults.m4 | 15 +- cups/dest.c | 6 + cups/util.c | 41 ++- doc/help/ref-cupsd-conf.html.in | 6 +- doc/help/spec-ppd.html | 16 +- scheduler/ipp.c | 21 +- scheduler/job.c | 2 +- scheduler/main.c | 30 ++- scheduler/printers.c | 18 +- scheduler/printers.h | 1 - systemv/lpstat.c | 6 +- test/4.3-job-ops.test | 3 + test/Dependencies | 1 + test/ipptest.c | 440 ++++++++++++++++++++++++++------ 17 files changed, 503 insertions(+), 129 deletions(-) diff --git a/CHANGES-1.4.txt b/CHANGES-1.4.txt index 74c595971..5508abe0e 100644 --- a/CHANGES-1.4.txt +++ b/CHANGES-1.4.txt @@ -6,6 +6,12 @@ CHANGES IN CUPS V1.4.3 - SECURITY: The scheduler could try responding on a closed client connection, leading to a crash (STR #3200) - Localization updates (STR #3352, STR #3409, STR #3422) + - Documentation update (STR #3451) + - IPP conformance: Get-Jobs has a default value for requested-attributes + (STR #3383) + - cupsPrintFiles() did not report all errors (STR #3449) + - cupsAddDest() could read freed memory (STR #3448) + - The DBUS notifier did not build (STR #3447) - The scheduler would crash when an active printer was deleted. - The snmp backend did not work with some printers (STR #3413) - The web interface did not show the conflicting values when setting diff --git a/berkeley/lpq.c b/berkeley/lpq.c index b744848f7..8304bdc4e 100644 --- a/berkeley/lpq.c +++ b/berkeley/lpq.c @@ -340,7 +340,18 @@ show_jobs(const char *command, /* I - Command name */ char resource[1024]; /* Resource string */ char rankstr[255]; /* Rank string */ char namestr[1024]; /* Job name string */ - static const char *ranks[10] = /* Ranking strings */ + static const char * const jobattrs[] =/* Job attributes we want to see */ + { + "copies", + "job-id", + "job-k-octets", + "job-name", + "job-originating-user-name", + "job-printer-uri", + "job-priority", + "job-state" + }; + static const char * const ranks[10] = /* Ranking strings */ { "th", "st", @@ -368,6 +379,7 @@ show_jobs(const char *command, /* I - Command name */ * attributes-charset * attributes-natural-language * job-uri or printer-uri + * requested-attributes */ request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS); @@ -397,6 +409,10 @@ show_jobs(const char *command, /* I - Command name */ ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); } + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (int)(sizeof(jobattrs) / sizeof(jobattrs[0])), NULL, jobattrs); + /* * Do the request and get back a response... */ diff --git a/config-scripts/cups-common.m4 b/config-scripts/cups-common.m4 index 8583acfaf..51b108beb 100644 --- a/config-scripts/cups-common.m4 +++ b/config-scripts/cups-common.m4 @@ -262,6 +262,8 @@ if test "x$enable_dbus" != xno; then AC_DEFINE(HAVE_DBUS) CFLAGS="$CFLAGS `$PKGCONFIG --cflags dbus-1` -DDBUS_API_SUBJECT_TO_CHANGE" CUPSDLIBS="$CUPSDLIBS `$PKGCONFIG --libs dbus-1`" + DBUS_NOTIFIER="dbus" + DBUS_NOTIFIERLIBS="`$PKGCONFIG --libs dbus-1`" AC_CHECK_LIB(dbus-1, dbus_message_iter_init_append, AC_DEFINE(HAVE_DBUS_MESSAGE_ITER_INIT_APPEND),, diff --git a/config-scripts/cups-defaults.m4 b/config-scripts/cups-defaults.m4 index 1a4eb7b89..4f455a823 100644 --- a/config-scripts/cups-defaults.m4 +++ b/config-scripts/cups-defaults.m4 @@ -208,6 +208,10 @@ AC_ARG_WITH(cups_user, [ --with-cups-user set default user for CUPS], AC_MSG_RESULT(no password file, using "$CUPS_USER") fi) +if test "x$CUPS_USER" = "xroot" -o "x$CUPS_USER" = "x0"; then + AC_MSG_ERROR([The default user for CUPS cannot be root!]) +fi + AC_ARG_WITH(cups_group, [ --with-cups-group set default group for CUPS], CUPS_GROUP="$withval", AC_MSG_CHECKING(for default print group) @@ -238,6 +242,10 @@ AC_ARG_WITH(cups_group, [ --with-cups-group set default group for CUPS], AC_MSG_RESULT(no group file, using "$CUPS_GROUP") fi) +if test "x$CUPS_GROUP" = "xroot" -o "x$CUPS_GROUP" = "xwheel" -o "x$CUPS_GROUP" = "x0"; then + AC_MSG_ERROR([The default group for CUPS cannot be root!]) +fi + AC_ARG_WITH(system_groups, [ --with-system-groups set default system groups for CUPS], CUPS_SYSTEM_GROUPS="$withval", if test x$uname = xDarwin; then @@ -269,9 +277,14 @@ AC_ARG_WITH(system_groups, [ --with-system-groups set default system groups fi fi) - CUPS_PRIMARY_SYSTEM_GROUP="`echo $CUPS_SYSTEM_GROUPS | awk '{print $1}'`" +for group in $CUPS_SYSTEM_GROUPS; do + if test "x$CUPS_GROUP" = "x$group"; then + AC_MSG_ERROR([The default system groups cannot contain the default CUPS group!]) + fi +done + AC_SUBST(CUPS_USER) AC_SUBST(CUPS_GROUP) AC_SUBST(CUPS_SYSTEM_GROUPS) diff --git a/cups/dest.c b/cups/dest.c index 099f451d8..85a2b3836 100644 --- a/cups/dest.c +++ b/cups/dest.c @@ -144,6 +144,12 @@ cupsAddDest(const char *name, /* I - Destination name */ dest = cups_add_dest(name, instance, &num_dests, dests); + /* + * Find the base dest again now the array has been realloc'd. + */ + + parent = cupsGetDest(name, NULL, num_dests, *dests); + if (instance && parent && parent->num_options > 0) { /* diff --git a/cups/util.c b/cups/util.c index 87ba2c0ce..7bfbe29a2 100644 --- a/cups/util.c +++ b/cups/util.c @@ -570,17 +570,17 @@ cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_D _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */ static const char * const attrs[] = /* Requested attributes */ { + "document-format", "job-id", - "job-priority", "job-k-octets", + "job-name", + "job-originating-user-name", + "job-printer-uri", + "job-priority", "job-state", "time-at-completed", "time-at-creation", - "time-at-processing", - "job-printer-uri", - "document-format", - "job-name", - "job-originating-user-name" + "time-at-processing" }; @@ -1456,6 +1456,9 @@ cupsPrintFiles2( char buffer[8192]; /* Copy buffer */ ssize_t bytes; /* Bytes in buffer */ http_status_t status; /* Status of write */ + _cups_globals_t *cg = _cupsGlobals(); /* Global data */ + ipp_status_t cancel_status; /* Status code to preserve */ + char *cancel_message; /* Error message to preserve */ DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, " @@ -1507,8 +1510,8 @@ cupsPrintFiles2( * Unable to open print file, cancel the job and return... */ - cupsCancelJob2(http, name, job_id, 0); - return (0); + _cupsSetError(IPP_DOCUMENT_ACCESS_ERROR, NULL, 0); + goto cancel_job; } do @@ -1550,12 +1553,30 @@ cupsPrintFiles2( * Unable to queue, cancel the job and return... */ - cupsCancelJob2(http, name, job_id, 0); - return (0); + goto cancel_job; } } return (job_id); + + /* + * If we get here, something happened while sending the print job so we need + * to cancel the job without setting the last error (since we need to preserve + * the current error... + */ + + cancel_job: + + cancel_status = cg->last_error; + cancel_message = cg->last_status_message ? + _cupsStrRetain(cg->last_status_message) : NULL; + + cupsCancelJob2(http, name, job_id, 0); + + cg->last_error = cancel_status; + cg->last_status_message = cancel_message; + + return (0); } diff --git a/doc/help/ref-cupsd-conf.html.in b/doc/help/ref-cupsd-conf.html.in index 4f880b461..6b3384bbc 100644 --- a/doc/help/ref-cupsd-conf.html.in +++ b/doc/help/ref-cupsd-conf.html.in @@ -123,7 +123,8 @@ to the access log file. The following levels are defined:

Allow from nnn.nnn.nnn.nnn Allow from nnn.nnn.nnn.nnn/mm Allow from nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm - Allow from xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx + Allow from [xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx] + Allow from [xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]/mmm Allow from @LOCAL Allow from @IF(name) </Location> @@ -1071,7 +1072,8 @@ printers are shared (published) by default. The default is Deny from nnn.nnn.nnn.nnn Deny from nnn.nnn.nnn.nnn/mm Deny from nnn.nnn.nnn.nnn/mmm.mmm.mmm.mmm - Deny from xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx + Deny from [xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx] + Deny from [xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]/mmm Deny from @LOCAL Deny from @IF(name) </Location> diff --git a/doc/help/spec-ppd.html b/doc/help/spec-ppd.html index 9fce558dc..2ac011e77 100644 --- a/doc/help/spec-ppd.html +++ b/doc/help/spec-ppd.html @@ -161,9 +161,8 @@ based on well-known colorspaces such as sRGB and Adobe RGB.

Note: -

At this time, none of the CUPS raster -filters support ICC profiles. This will be addressed as time -and resources permit.

+

At this time, none of the CUPS raster filters support ICC profiles. This +will be addressed as time and resources permit.

@@ -174,7 +173,7 @@ gamma m00 m01 m02 m10 m11 m12 m20 m21 m22"

This string attribute specifies an sRGB-based color profile consisting of gamma and density controls and a 3x3 CMY color -transform matrix.

+transform matrix. This attribute is not supported on Mac OS X.

The Resolution and MediaType values may be "-" to act as a wildcard. Otherwise they must match one of the @@ -277,6 +276,15 @@ data as requested by the driver. The APCustomColorMatchingProfile and APDefaultColorMatchingProfile attributes specify alternate color profiles (sRGB or AdobeRGB) to use for 3-color (RGB) raster data.

+
Note: + +

Prior to Mac OS X 10.6, the default RGB color space was Apple's "GenericRGB". +The new default in Mac OS X 10.6 and later is "sRGB". For more information, see +"Mac OS X v10.6: About gamma +2.2" on Apple's support site.

+ +
+

Mac OS X 10.5APCustomColorMatchingName

*APCustomColorMatchingName name/text: ""

diff --git a/scheduler/ipp.c b/scheduler/ipp.c index f95417e64..b4659fe3f 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -5430,14 +5430,6 @@ copy_printer_attrs( curtime = time(NULL); -#ifdef __APPLE__ - if ((!ra || cupsArrayFind(ra, "com.apple.print.recoverable-message")) && - printer->recoverable) - ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "com.apple.print.recoverable-message", NULL, - printer->recoverable); -#endif /* __APPLE__ */ - if (!ra || cupsArrayFind(ra, "marker-change-time")) ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "marker-change-time", printer->marker_time); @@ -7007,7 +6999,18 @@ get_jobs(cupsd_client_t *con, /* I - Client connection */ else username[0] = '\0'; - ra = create_requested_array(con->request); + if ((ra = create_requested_array(con->request)) == NULL && + !ippFindAttribute(con->request, "requested-attributes", IPP_TAG_KEYWORD)) + { + /* + * IPP conformance - Get-Jobs has a default requested-attributes value of + * "job-id" and "job-uri". + */ + + ra = cupsArrayNew((cups_array_func_t)strcmp, NULL); + cupsArrayAdd(ra, "job-id"); + cupsArrayAdd(ra, "job-uri"); + } /* * OK, build a list of jobs for this printer... diff --git a/scheduler/job.c b/scheduler/job.c index b33856707..dd3df67a8 100644 --- a/scheduler/job.c +++ b/scheduler/job.c @@ -2441,7 +2441,7 @@ cupsdSetJobState( * Finalize the job immediately if we forced things... */ - if (action >= CUPSD_JOB_FORCE) + if (action >= CUPSD_JOB_FORCE && job->printer) finalize_job(job, 0); /* diff --git a/scheduler/main.c b/scheduler/main.c index 28ae983f0..60e97d095 100644 --- a/scheduler/main.c +++ b/scheduler/main.c @@ -134,7 +134,7 @@ main(int argc, /* I - Number of command-line args */ senddoc_time, /* Send-Document time */ expire_time, /* Subscription expire time */ report_time, /* Malloc/client/job report time */ - event_time; /* Last time an event notification was done */ + event_time; /* Last event notification time */ long timeout; /* Timeout for cupsdDoSelect() */ struct rlimit limit; /* Runtime limit */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) @@ -270,19 +270,22 @@ main(int argc, /* I - Number of command-line args */ break; case 'p' : /* Stop immediately for profiling */ - puts("Warning: -p (startup profiling) is for internal testing use only!"); + fputs("cupsd: -p (startup profiling) is for internal testing " + "use only!\n", stderr); stop_scheduler = 1; fg = 1; break; case 'P' : /* Disable security profiles */ - puts("Warning: -P (disable security profiles) is for internal testing use only!"); + fputs("cupsd: -P (disable security profiles) is for internal " + "testing use only!\n", stderr); UseProfiles = 0; break; #ifdef __APPLE__ case 'S' : /* Disable system management functions */ - puts("Warning: -S (disable system management) for internal testing use only!"); + fputs("cupsd: -S (disable system management) for internal " + "testing use only!\n", stderr); use_sysman = 0; break; #endif /* __APPLE__ */ @@ -763,7 +766,8 @@ main(int argc, /* I - Number of command-line args */ if (Launchd) { /* - * If we were started by launchd get the listen sockets file descriptors... + * If we were started by launchd, get the listen socket file + * descriptors... */ launchd_checkin(); @@ -1566,8 +1570,8 @@ launchd_checkin(void) if ((lis = calloc(1, sizeof(cupsd_listener_t))) == NULL) { cupsdLogMessage(CUPSD_LOG_ERROR, - "launchd_checkin: Unable to allocate listener - %s.", - strerror(errno)); + "launchd_checkin: Unable to allocate listener - " + "%s.", strerror(errno)); exit(EXIT_FAILURE); } @@ -1623,7 +1627,8 @@ launchd_checkout(void) (BrowseLocalProtocols && NumBrowsers && cupsArrayCount(Printers)))))) { cupsdLogMessage(CUPSD_LOG_DEBUG, - "Creating launchd keepalive file \"" CUPS_KEEPALIVE "\"..."); + "Creating launchd keepalive file \"" CUPS_KEEPALIVE + "\"..."); if ((fd = open(CUPS_KEEPALIVE, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR)) >= 0) close(fd); @@ -1631,7 +1636,8 @@ launchd_checkout(void) else { cupsdLogMessage(CUPSD_LOG_DEBUG, - "Removing launchd keepalive file \"" CUPS_KEEPALIVE "\"..."); + "Removing launchd keepalive file \"" CUPS_KEEPALIVE + "\"..."); unlink(CUPS_KEEPALIVE); } @@ -1767,7 +1773,7 @@ process_children(void) job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT, "job-printer-state-message", - NULL, ""); + NULL, NULL); } if (job->printer_message) @@ -1781,7 +1787,7 @@ process_children(void) * filters are done, and if so move to the next file. */ - if (job->current_file < job->num_files) + if (job->current_file < job->num_files && job->printer) { for (i = 0; job->filters[i] < 0; i ++); @@ -1794,7 +1800,7 @@ process_children(void) cupsdContinueJob(job); } } - else if (job->state_value == IPP_JOB_CANCELED) + else if (job->state_value >= IPP_JOB_CANCELED) { /* * Remove the job from the active list if there are no processes still diff --git a/scheduler/printers.c b/scheduler/printers.c index b12a5498e..1e615ebe0 100644 --- a/scheduler/printers.c +++ b/scheduler/printers.c @@ -205,11 +205,6 @@ cupsdAddPrinterHistory( ippAddBoolean(history, IPP_TAG_PRINTER, "printer-is-shared", p->shared); ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-state-message", NULL, p->state_message); -#ifdef __APPLE__ - if (p->recoverable) - ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_TEXT, - "com.apple.print.recoverable-message", NULL, p->recoverable); -#endif /* __APPLE__ */ if (p->num_reasons == 0) ippAddString(history, IPP_TAG_PRINTER, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, "none"); @@ -649,7 +644,10 @@ cupsdDeleteAllPrinters(void) for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) + { + p->op_policy_ptr = DefaultPolicyPtr; cupsdDeletePrinter(p, 0); + } } @@ -841,10 +839,6 @@ cupsdDeletePrinter( if (p->browse_attrs) free(p->browse_attrs); -#ifdef __APPLE__ - cupsdClearString(&p->recoverable); -#endif /* __APPLE__ */ - cupsFreeOptions(p->num_options, p->options); free(p); @@ -1094,7 +1088,6 @@ cupsdLoadAllPrinters(void) else if (!strcasecmp(line, "Reason")) { if (value && - strcmp(value, "com.apple.print.recoverable-warning") && strcmp(value, "connecting-to-device") && strcmp(value, "cups-insecure-filter-warning") && strcmp(value, "cups-missing-filter-warning")) @@ -1599,8 +1592,7 @@ cupsdSaveAllPrinters(void) cupsFilePrintf(fp, "StateTime %d\n", (int)printer->state_time); for (i = 0; i < printer->num_reasons; i ++) - if (strcmp(printer->reasons[i], "com.apple.print.recoverable-warning") && - strcmp(printer->reasons[i], "connecting-to-device") && + if (strcmp(printer->reasons[i], "connecting-to-device") && strcmp(printer->reasons[i], "cups-insecure-filter-warning") && strcmp(printer->reasons[i], "cups-missing-filter-warning")) cupsFilePutConf(fp, "Reason", printer->reasons[i]); @@ -3756,7 +3748,7 @@ add_printer_formats(cupsd_printer_t *p) /* I - Printer */ filter; filter = (mime_filter_t *)cupsArrayNext(MimeDatabase->filters)) { - if (filter->dst == p->filetype && filter->filter && + if (filter->dst == p->filetype && filter->filter && strstr(filter->filter, "PrintJobMgr")) break; } diff --git a/scheduler/printers.h b/scheduler/printers.h index 292a4b562..713539941 100644 --- a/scheduler/printers.h +++ b/scheduler/printers.h @@ -94,7 +94,6 @@ typedef struct cupsd_printer_s time_t marker_time; /* Last time marker attributes were updated */ cups_array_t *filters, /* Filters for queue */ *pre_filters; /* Pre-filters for queue */ - char *recoverable; /* com.apple.print.recoverable-message */ #ifdef HAVE_DNSSD char *reg_name, /* Name used for service registration */ diff --git a/systemv/lpstat.c b/systemv/lpstat.c index 603ecdd25..e98aaa70d 100644 --- a/systemv/lpstat.c +++ b/systemv/lpstat.c @@ -1287,10 +1287,10 @@ show_jobs(const char *dests, /* I - Destinations */ "job-id", "job-k-octets", "job-name", - "time-at-creation", - "job-printer-uri", "job-originating-user-name", - "job-state-reasons" + "job-printer-uri", + "job-state-reasons", + "time-at-creation" }; diff --git a/test/4.3-job-ops.test b/test/4.3-job-ops.test index 1b0a71afa..2f2236b0c 100644 --- a/test/4.3-job-ops.test +++ b/test/4.3-job-ops.test @@ -296,6 +296,7 @@ # What attributes do we expect? EXPECT attributes-charset EXPECT attributes-natural-language + EXPECT !job-printer-uri } { # The name of the test... @@ -310,6 +311,7 @@ ATTR charset attributes-charset utf-8 ATTR language attributes-natural-language en ATTR uri printer-uri $scheme://$hostname:$port/ + ATTR keyword requested-attributes all # What statuses are OK? STATUS successful-ok @@ -320,6 +322,7 @@ EXPECT job-uri EXPECT job-id EXPECT job-state + EXPECT job-printer-uri } # diff --git a/test/Dependencies b/test/Dependencies index 43ea40074..a395277e9 100644 --- a/test/Dependencies +++ b/test/Dependencies @@ -3,3 +3,4 @@ ipptest.o: ../cups/string.h ../config.h ../cups/cups.h ../cups/ipp.h ipptest.o: ../cups/http.h ../cups/versioning.h ../cups/ppd.h ../cups/array.h ipptest.o: ../cups/file.h ../cups/language.h ../cups/language.h +ipptest.o: ../cups/http-private.h ../cups/md5.h ../cups/ipp-private.h diff --git a/test/ipptest.c b/test/ipptest.c index 59f2fb803..28404388d 100644 --- a/test/ipptest.c +++ b/test/ipptest.c @@ -14,11 +14,13 @@ * * Contents: * - * main() - Parse options and do tests. - * do_tests() - Do tests as specified in the test file. - * get_token() - Get a token from a file. - * print_attr() - Print an attribute on the screen. - * usage() - Show program usage. + * main() - Parse options and do tests. + * do_tests() - Do tests as specified in the test file. + * expect_matches() - Return true if the tag matches the specification. + * get_token() - Get a token from a file. + * print_attr() - Print an attribute on the screen. + * print_col() - Print a collection attribute on the screen. + * usage() - Show program usage. */ /* @@ -33,12 +35,31 @@ #include #include +#include +#ifndef O_BINARY +# define O_BINARY 0 +#endif /* !O_BINARY */ + + +/* + * Types... + */ + +typedef struct _cups_expect_s /**** Expected attribute info ****/ +{ + int not_expect; /* Don't expect attribute? */ + char *name, /* Attribute name */ + *of_type, /* Type name */ + *same_count_as, /* Parallel attribute name */ + *if_defined; /* Only required if variable defined */ +} _cups_expect_t; /* * Globals... */ +int Chunking = 0; /* Use chunked requests */ int Verbosity = 0; /* Show all attributes? */ @@ -46,13 +67,13 @@ int Verbosity = 0; /* Show all attributes? */ * Local functions... */ -int do_tests(const char *, const char *); -ipp_op_t ippOpValue(const char *); -ipp_status_t ippErrorValue(const char *); -char *get_token(FILE *, char *, int, int *linenum); -void print_attr(ipp_attribute_t *); -void print_col(ipp_t *col); -void usage(const char *option); +static int do_tests(const char *uri, const char *testfile); +static int expect_matches(_cups_expect_t *expect, ipp_tag_t value_tag); +static char *get_token(FILE *fp, char *buf, int buflen, + int *linenum); +static void print_attr(ipp_attribute_t *attr); +static void print_col(ipp_t *col); +static void usage(void); /* @@ -60,13 +81,14 @@ void usage(const char *option); */ int /* O - Exit status */ -main(int argc, /* I - Number of command-line arguments */ +main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { int i; /* Looping var */ int status; /* Status of tests... */ - const char *uri; /* URI to use */ - const char *testfile; /* Test file to use */ + char *opt; /* Current option */ + const char *uri, /* URI to use */ + *testfile; /* Test file to use */ int interval; /* Test interval */ @@ -85,28 +107,48 @@ main(int argc, /* I - Number of command-line arguments */ { if (argv[i][0] == '-') { - if (!strcmp(argv[i], "-v")) - Verbosity ++; - else if (!strcmp(argv[i], "-d")) + for (opt = argv[i] + 1; *opt; opt ++) { - i ++; + switch (*opt) + { + case 'c' : /* Enable HTTP chunking */ + Chunking = 1; + break; - if (i >= argc) - usage(NULL); - else - putenv(argv[i]); - } - else if (!strcmp(argv[i], "-i")) - { - i++; + case 'd' : /* Define a variable */ + i ++; - if (i >= argc) - usage(NULL); - else - interval = atoi(argv[i]); + if (i >= argc) + { + fputs("ipptest: Missing name=value for \"-d\"!\n", stderr); + usage(); + } + else + putenv(argv[i]); + break; + + case 'i' : /* Test every N seconds */ + i++; + + if (i >= argc) + { + fputs("ipptest: Missing seconds for \"-i\"!\n", stderr); + usage(); + } + else + interval = atoi(argv[i]); + break; + + case 'v' : /* Be verbose */ + Verbosity ++; + break; + + default : + fprintf(stderr, "ipptest: Unknown option \"-%c\"!\n", *opt); + usage(); + break; + } } - else - usage(argv[i]); } else if (!strncmp(argv[i], "ipp://", 6) || !strncmp(argv[i], "http://", 7) || @@ -117,7 +159,11 @@ main(int argc, /* I - Number of command-line arguments */ */ if (!testfile && uri) - usage(NULL); + { + fputs("ipptest: May only specify a single URI before a test!\n", + stderr); + usage(); + } uri = argv[i]; testfile = NULL; @@ -136,7 +182,7 @@ main(int argc, /* I - Number of command-line arguments */ } if (!uri || !testfile) - usage(NULL); + usage(); /* * Loop if the interval is set... @@ -158,12 +204,12 @@ main(int argc, /* I - Number of command-line arguments */ return (status); } - + /* * 'do_tests()' - Do tests as specified in the test file. */ -int /* 1 = success, 0 = failure */ +static int /* 1 = success, 0 = failure */ do_tests(const char *uri, /* I - URI to connect on */ const char *testfile) /* I - Test file to use */ { @@ -186,12 +232,15 @@ do_tests(const char *uri, /* I - URI to connect on */ ipp_op_t op; /* Operation */ ipp_tag_t group; /* Current group */ ipp_tag_t value; /* Current value type */ - ipp_attribute_t *attrptr; /* Attribute pointer */ + ipp_attribute_t *attrptr, /* Attribute pointer */ + *found; /* Found attribute */ char attr[128]; /* Attribute name */ int num_statuses; /* Number of valid status codes */ ipp_status_t statuses[100]; /* Valid status codes */ int num_expects; /* Number of expected attributes */ - char *expects[100]; /* Expected attributes */ + _cups_expect_t expects[100], /* Expected attributes */ + *expect, /* Current expected attribute */ + *last_expect; /* Last EXPECT (for predicates) */ int num_displayed; /* Number of displayed attributes */ char *displayed[100]; /* Displayed attributes */ char name[1024]; /* Name of test */ @@ -265,6 +314,7 @@ do_tests(const char *uri, /* I - URI to connect on */ num_statuses = 0; num_expects = 0; num_displayed = 0; + last_expect = NULL; filename[0] = '\0'; strcpy(name, testfile); @@ -277,6 +327,12 @@ do_tests(const char *uri, /* I - URI to connect on */ while (get_token(fp, token, sizeof(token), &linenum) != NULL) { + if (strcasecmp(token, "EXPECT") && + strcasecmp(token, "IF-DEFINED") && + strcasecmp(token, "OF-TYPE") && + strcasecmp(token, "SAME-COUNT-AS")) + last_expect = NULL; + if (!strcmp(token, "}")) break; else if (!strcasecmp(token, "NAME")) @@ -526,16 +582,87 @@ do_tests(const char *uri, /* I - URI to connect on */ statuses[num_statuses] = ippErrorValue(token); num_statuses ++; } - else if (!strcasecmp(token, "EXPECT") && - num_expects < (int)(sizeof(expects) / sizeof(expects[0]))) + else if (!strcasecmp(token, "EXPECT")) { /* * Expected attributes... */ + if (num_expects >= (int)(sizeof(expects) / sizeof(expects[0]))) + { + fprintf(stderr, "ipptest: Too many EXPECT's on line %d\n", linenum); + httpClose(http); + ippDelete(request); + return (0); + } + get_token(fp, token, sizeof(token), &linenum); - expects[num_expects] = strdup(token); + + last_expect = expects + num_expects; num_expects ++; + + if (token[0] == '!') + { + last_expect->not_expect = 1; + last_expect->name = strdup(token + 1); + } + else + { + last_expect->not_expect = 0; + last_expect->name = strdup(token); + } + + last_expect->of_type = NULL; + last_expect->same_count_as = NULL; + last_expect->if_defined = NULL; + } + else if (!strcasecmp(token, "OF-TYPE")) + { + get_token(fp, token, sizeof(token), &linenum); + + if (last_expect) + last_expect->of_type = strdup(token); + else + { + fprintf(stderr, + "ipptest: OF-TYPE without a preceding EXPECT on line %d\n", + linenum); + httpClose(http); + ippDelete(request); + return (0); + } + } + else if (!strcasecmp(token, "SAME-COUNT-AS")) + { + get_token(fp, token, sizeof(token), &linenum); + + if (last_expect) + last_expect->same_count_as = strdup(token); + else + { + fprintf(stderr, + "ipptest: SAME-COUNT-AS without a preceding EXPECT on line " + "%d\n", linenum); + httpClose(http); + ippDelete(request); + return (0); + } + } + else if (!strcasecmp(token, "IF-DEFINED")) + { + get_token(fp, token, sizeof(token), &linenum); + + if (last_expect) + last_expect->if_defined = strdup(token); + else + { + fprintf(stderr, + "ipptest: IF-DEFINED without a preceding EXPECT on line %d\n", + linenum); + httpClose(http); + ippDelete(request); + return (0); + } } else if (!strcasecmp(token, "DISPLAY") && num_displayed < (int)(sizeof(displayed) / sizeof(displayed[0]))) @@ -550,8 +677,9 @@ do_tests(const char *uri, /* I - URI to connect on */ } else { - printf("Unexpected token %s seen on line %d - aborting test!\n", token, - linenum); + fprintf(stderr, + "ipptest: Unexpected token %s seen on line %d - aborting " + "test!\n", token, linenum); httpClose(http); ippDelete(request); return (0); @@ -569,7 +697,7 @@ do_tests(const char *uri, /* I - URI to connect on */ if (Verbosity) { - printf("%s:\n", ippOpString(op)); + printf(" %s:\n", ippOpString(op)); for (attrptr = request->attrs; attrptr; attrptr = attrptr->next) print_attr(attrptr); @@ -578,7 +706,36 @@ do_tests(const char *uri, /* I - URI to connect on */ printf(" %-60.60s [", name); fflush(stdout); - if (filename[0]) + if (Chunking) + { + http_status_t status = cupsSendRequest(http, request, resource, 0); + + if (status == HTTP_CONTINUE && filename[0]) + { + int fd; /* File to send */ + char buffer[8192]; /* Copy buffer */ + ssize_t bytes; /* Bytes read/written */ + + + if ((fd = open(filename, O_RDONLY | O_BINARY)) >= 0) + { + while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) + if ((status = cupsWriteRequestData(http, buffer, + bytes)) != HTTP_CONTINUE) + break; + } + else + status = HTTP_ERROR; + } + + ippDelete(request); + + if (status == HTTP_CONTINUE) + response = cupsGetResponse(http, resource); + else + response = NULL; + } + else if (filename[0]) response = cupsDoFileRequest(http, request, resource, filename); else response = cupsDoIORequest(http, request, resource, -1, @@ -597,6 +754,9 @@ do_tests(const char *uri, /* I - URI to connect on */ } else { + if (http->version != HTTP_1_1) + pass = 0; + if ((attrptr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL) job_id = attrptr->values[0].integer; @@ -613,12 +773,32 @@ do_tests(const char *uri, /* I - URI to connect on */ pass = 0; else { - for (i = 0; i < num_expects; i ++) - if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL) - { - pass = 0; - break; - } + for (i = num_expects, expect = expects; i > 0; i --, expect ++) + { + if (expect->if_defined && !getenv(expect->if_defined)) + continue; + + found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO); + + if ((found == NULL) != expect->not_expect || + (found && !expect_matches(expect, found->value_tag))) + { + pass = 0; + break; + } + + if (found && expect->same_count_as) + { + attrptr = ippFindAttribute(response, expect->same_count_as, + IPP_TAG_ZERO); + + if (!attrptr || attrptr->num_values != found->num_values) + { + pass = 0; + break; + } + } + } } if (pass) @@ -629,21 +809,31 @@ do_tests(const char *uri, /* I - URI to connect on */ if (Verbosity) { - for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next) + for (attrptr = response->attrs; + attrptr != NULL; + attrptr = attrptr->next) + { print_attr(attrptr); + } } else if (num_displayed > 0) { - for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next) + for (attrptr = response->attrs; + attrptr != NULL; + attrptr = attrptr->next) + { if (attrptr->name) { for (i = 0; i < num_displayed; i ++) + { if (!strcmp(displayed[i], attrptr->name)) { print_attr(attrptr); break; } + } } + } } } else @@ -652,6 +842,10 @@ do_tests(const char *uri, /* I - URI to connect on */ printf(" RECEIVED: %lu bytes in response\n", (unsigned long)ippLength(response)); + if (http->version != HTTP_1_1) + printf(" BAD HTTP VERSION (%d.%d)\n", http->version / 100, + http->version % 100); + for (i = 0; i < num_statuses; i ++) if (response->request.status.status_code == statuses[i]) break; @@ -662,9 +856,43 @@ do_tests(const char *uri, /* I - URI to connect on */ printf(" status-code = %04x (%s)\n", cupsLastError(), ippErrorString(cupsLastError())); - for (i = 0; i < num_expects; i ++) - if (ippFindAttribute(response, expects[i], IPP_TAG_ZERO) == NULL) - printf(" EXPECTED: %s\n", expects[i]); + for (i = num_expects, expect = expects; i > 0; i --, expect ++) + { + if (expect->if_defined && !getenv(expect->if_defined)) + continue; + + found = ippFindAttribute(response, expect->name, IPP_TAG_ZERO); + + if ((found == NULL) != expect->not_expect) + { + if (expect->not_expect) + printf(" NOT EXPECTED: %s\n", expect->name); + else + printf(" EXPECTED: %s\n", expect->name); + } + else if (found) + { + if (!expect_matches(expect, found->value_tag)) + printf(" EXPECTED: %s of type %s but got %s\n", + expect->name, expect->of_type, + ippTagString(found->value_tag)); + else if (expect->same_count_as) + { + attrptr = ippFindAttribute(response, expect->same_count_as, + IPP_TAG_ZERO); + + if (!attrptr) + printf(" EXPECTED: %s (%d values) same count as %s " + "(not returned)\n", + expect->name, found->num_values, expect->same_count_as); + else if (attrptr->num_values != found->num_values) + printf(" EXPECTED: %s (%d values) same count as %s " + "(%d values)\n", + expect->name, found->num_values, expect->same_count_as, + attrptr->num_values); + } + } + } for (attrptr = response->attrs; attrptr != NULL; attrptr = attrptr->next) print_attr(attrptr); @@ -673,9 +901,16 @@ do_tests(const char *uri, /* I - URI to connect on */ ippDelete(response); } - for (i = 0; i < num_expects; i ++) - free(expects[i]); - + for (i = num_expects, expect = expects; i > 0; i --, expect ++) + { + free(expect->name); + if (expect->of_type) + free(expect->of_type); + if (expect->same_count_as) + free(expect->same_count_as); + if (expect->if_defined) + free(expect->if_defined); + } if (!pass) break; } @@ -687,11 +922,71 @@ do_tests(const char *uri, /* I - URI to connect on */ } +/* + * 'expect_matches()' - Return true if the tag matches the specification. + */ + +static int /* O - 1 if matches, 0 otherwise */ +expect_matches( + _cups_expect_t *expect, /* I - Expected attribute */ + ipp_tag_t value_tag) /* I - Value tag for attribute */ +{ + int match; /* Match? */ + char *of_type, /* Type name to match */ + *next; /* Next name to match */ + + + /* + * If we don't expect a particular type, return immediately... + */ + + if (!expect->of_type) + return (1); + + /* + * Parse the "of_type" value since the string can contain multiple attribute + * types separated by "|"... + */ + + for (of_type = expect->of_type, match = 0; !match && of_type; of_type = next) + { + /* + * Find the next separator, and set it (temporarily) to nul if present. + */ + + if ((next = strchr(of_type, '|')) != NULL) + *next = '\0'; + + /* + * Support some meta-types to make it easier to write the test file. + */ + + if (!strcmp(of_type, "text")) + match = value_tag == IPP_TAG_TEXTLANG || value_tag == IPP_TAG_TEXT; + else if (!strcmp(of_type, "name")) + match = value_tag == IPP_TAG_NAMELANG || value_tag == IPP_TAG_NAME; + else if (!strcmp(of_type, "collection")) + match = value_tag == IPP_TAG_BEGIN_COLLECTION; + else + match = value_tag == ippTagValue(of_type); + + /* + * Restore the separator if we have one... + */ + + if (next) + *next++ = '|'; + } + + return (match); +} + + /* * 'get_token()' - Get a token from a file. */ -char * /* O - Token from file or NULL on EOF */ +static char * /* O - Token from file or NULL on EOF */ get_token(FILE *fp, /* I - File to read from */ char *buf, /* I - Buffer to read into */ int buflen, /* I - Length of buffer */ @@ -783,7 +1078,7 @@ get_token(FILE *fp, /* I - File to read from */ * 'print_attr()' - Print an attribute on the screen. */ -void +static void print_attr(ipp_attribute_t *attr) /* I - Attribute to print */ { int i; /* Looping var */ @@ -861,7 +1156,7 @@ print_attr(ipp_attribute_t *attr) /* I - Attribute to print */ print_col(attr->values[i].collection); } break; - + default : break; /* anti-compiler-warning-code */ } @@ -874,7 +1169,7 @@ print_attr(ipp_attribute_t *attr) /* I - Attribute to print */ * 'print_col()' - Print a collection attribute on the screen. */ -void +static void print_col(ipp_t *col) /* I - Collection attribute to print */ { int i; /* Looping var */ @@ -947,7 +1242,7 @@ print_col(ipp_t *col) /* I - Collection attribute to print */ putchar(' '); } break; - + default : break; /* anti-compiler-warning-code */ } @@ -961,17 +1256,18 @@ print_col(ipp_t *col) /* I - Collection attribute to print */ * 'usage()' - Show program usage. */ -void -usage(const char *option) /* I - Option string or NULL */ +static void +usage(void) { - if (option) - fprintf(stderr, "ipptest: Unknown option \"%s\"!\n", option); - fputs("Usage: ipptest [options] URL testfile [ ... testfileN ]\n", stderr); fputs("Options:\n", stderr); fputs("\n", stderr); - fputs("-i N Repeat the last test file once every N seconds.\n", stderr); - fputs("-v Show all attributes in response, even on success.\n", stderr); + fputs("-c Send requests using chunking.\n", stderr); + fputs("-d name=value Define variable.\n", stderr); + fputs("-i seconds Repeat the last test file with the given interval.\n", + stderr); + fputs("-v Show all attributes in response, even on success.\n", + stderr); exit(1); } -- 2.39.2