CHANGES IN CUPS V1.3.9
+ - Firefox 3 did not work with the CUPS web interface in SSL
+ mode (STR #2892)
+ - Custom options with multiple parameters were not emitted
+ correctly.
- Refined the cupstestppd utility.
- ppdEmit*() did not support custom JCL options (STR #2889)
- The cupstestppd utility incorrectly reported missing
-CHANGES.txt - 2008-07-28
+CHANGES.txt - 2008-08-04
------------------------
CHANGES IN CUPS V1.4b1
+ - The sample HP driver now supports A5 (STR #2798)
+ - The CUPS web interface menu item now uses the xdg-open
+ command, when available (STR #2724)
+ - The cups-lpd program now supports the -h option (STR #2794)
+ - The scheduler now sets the PAM_TTY parameter and the
+ PAM_ESTABLISH_CRED credential flag (STR #2745)
+ - The scheduler now logs unsuccessful requests to the error_log
+ file as errors (STR #2616)
+ - Added support for a "retry-current-job" error policy that
+ retries the current job immediately when the backend encounters
+ an error (STR #2555)
+ - The scheduler now returns a "forbidden" error when a user
+ correctly authenticates but does not have permission to
+ continue further (STR #2101)
+ - The scheduler now loads both the server and CA certificates
+ (if present) from the ServerCertificate file (STR #2146)
+ - New RSS subscriptions now create their feed files immediately
+ (STR #2853)
- Added support for a device-location attribute which provides
the physical location of a printer device.
- Added a cupsBackendReport() API which handles quoting of the
AC_PATH_PROG(RM,rm)
AC_PATH_PROG(RMDIR,rmdir)
AC_PATH_PROG(SED,sed)
+AC_PATH_PROG(XDGOPEN,xdg-open)
+if test "x$XDGOPEN" = x; then
+ CUPS_HTMLVIEW="htmlview"
+else
+ CUPS_HTMLVIEW="$XDGOPEN"
+fi
+AC_SUBST(CUPS_HTMLVIEW)
AC_MSG_CHECKING(for install-sh script)
INSTALL="`pwd`/install-sh -c"
dnl See if we have POSIX ACL support...
SAVELIBS="$LIBS"
LIBS=""
-AC_SEARCH_LIBS(acl_init, acl, AC_DEFINE(HAVE_ACL_INIT))
-CUPSDLIBS="$CUPSDLIBS $LIBS"
+AC_ARG_ENABLE(acl, [ --enable-acl enable POSIX ACL support, default=auto])
+if test "x$enable_acl" != xno; then
+ AC_SEARCH_LIBS(acl_init, acl, AC_DEFINE(HAVE_ACL_INIT))
+ CUPSDLIBS="$CUPSDLIBS $LIBS"
+fi
LIBS="$SAVELIBS"
dnl Check for DBUS support
AC_CHECK_LIB(dl,dlopen)
AC_CHECK_LIB(pam,pam_start)
AC_CHECK_LIB(pam,pam_set_item,AC_DEFINE(HAVE_PAM_SET_ITEM))
+ AC_CHECK_LIB(pam,pam_setcred,AC_DEFINE(HAVE_PAM_SETCRED))
AC_CHECK_HEADER(security/pam_appl.h)
if test x$ac_cv_header_security_pam_appl_h != xyes; then
AC_CHECK_HEADER(pam/pam_appl.h,
#undef HAVE_PAM_PAM_APPL_H
#undef HAVE_PAM_SET_ITEM
+#undef HAVE_PAM_SETCRED
/*
AC_OUTPUT(Makedefs packaging/cups.list init/cups.sh init/cups-lpd cups-config
conf/cupsd.conf conf/mime.convs conf/pam.std conf/snmp.conf
+ desktop/cups.desktop
doc/index.html doc/help/ref-cupsd-conf.html doc/help/standard.html
init/org.cups.cups-lpd.plist init/cups.xml
man/client.conf.man man/cups-deviced.man man/cups-driverd.man
*
* Contents:
*
- * ppdCollect() - Collect all marked options that reside in the
- * specified section.
- * ppdCollect2() - Collect all marked options that reside in the
- * specified section and minimum order.
- * ppdEmit() - Emit code for marked options to a file.
- * ppdEmitAfterOrder() - Emit a subset of the code for marked options to a
- * file.
- * ppdEmitFd() - Emit code for marked options to a file.
- * ppdEmitJCL() - Emit code for JCL options to a file.
- * ppdEmitJCLEnd() - Emit JCLEnd code to a file.
- * ppdEmitString() - Get a string containing the code for marked options.
- * ppd_handle_media() - Handle media selection...
+ * ppdCollect() - Collect all marked options that reside in the
+ * specified section.
+ * ppdCollect2() - Collect all marked options that reside in the
+ * specified section and minimum order.
+ * ppdEmit() - Emit code for marked options to a file.
+ * ppdEmitAfterOrder() - Emit a subset of the code for marked options to a
+ * file.
+ * ppdEmitFd() - Emit code for marked options to a file.
+ * ppdEmitJCL() - Emit code for JCL options to a file.
+ * ppdEmitJCLEnd() - Emit JCLEnd code to a file.
+ * ppdEmitString() - Get a string containing the code for marked
+ * options.
+ * ppd_compare_cparams() - Compare the order of two custom parameters.
+ * ppd_handle_media() - Handle media selection...
*/
/*
* Local functions...
*/
+static int ppd_compare_cparams(ppd_cparam_t *a, ppd_cparam_t *b);
static void ppd_handle_media(ppd_file_t *ppd);
*/
const char *s; /* Pointer into string value */
+ cups_array_t *params; /* Parameters in the correct output order */
+ params = cupsArrayNew((cups_array_func_t)ppd_compare_cparams, NULL);
+
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ cparam;
+ cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ cupsArrayAdd(params, cparam);
+
snprintf(bufptr, bufend - bufptr + 1,
"%%%%BeginFeature: *Custom%s True\n", coption->keyword);
bufptr += strlen(bufptr);
- for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
+ for (cparam = (ppd_cparam_t *)cupsArrayFirst(params);
cparam;
- cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
+ cparam = (ppd_cparam_t *)cupsArrayNext(params))
{
switch (cparam->type)
{
break;
}
}
+
+ cupsArrayDelete(params);
}
else
{
}
+/*
+ * 'ppd_compare_cparams()' - Compare the order of two custom parameters.
+ */
+
+static int /* O - Result of comparison */
+ppd_compare_cparams(ppd_cparam_t *a, /* I - First parameter */
+ ppd_cparam_t *b) /* I - Second parameter */
+{
+ return (a->order - b->order);
+}
+
+
/*
* 'ppd_handle_media()' - Handle media selection...
*/
static void
-ppd_handle_media(ppd_file_t *ppd)
+ppd_handle_media(ppd_file_t *ppd) /* I - PPD file */
{
ppd_choice_t *manual_feed, /* ManualFeed choice, if any */
*input_slot, /* InputSlot choice, if any */
# elif defined(HAVE_CDSASSL)
size_t bytes; /* Bytes that are available */
- if (!SSLGetBufferedReadSize(((http_tls_t *)http->tls)->session, &bytes) && bytes > 0)
+ if (!SSLGetBufferedReadSize(((http_tls_t *)(http->tls))->session, &bytes) &&
+ bytes > 0)
return (1);
# endif /* HAVE_LIBSSL */
}
if ((coption = ppdFindCustomOption(ppd, option)) != NULL)
{
- num_vals = cupsParseOptions(choice + 1, 0, &vals);
+ num_vals = cupsParseOptions(choice, 0, &vals);
for (i = 0, val = vals; i < num_vals; i ++, val ++)
{
return (num_options);
}
- ptr = copyarg;
+ if (*copyarg == '{')
+ {
+ /*
+ * Remove surrounding {} so we can parse "{name=value ... name=value}"...
+ */
+
+ if ((ptr = copyarg + strlen(copyarg) - 1) > copyarg && *ptr == '}')
+ {
+ *ptr = '\0';
+ ptr = copyarg + 1;
+ }
+ else
+ ptr = copyarg;
+ }
+ else
+ ptr = copyarg;
/*
* Skip leading spaces...
status = http->status;
}
- if (status == HTTP_FORBIDDEN)
+ if (status == HTTP_FORBIDDEN || status == HTTP_ERROR ||
+ status == HTTP_SERVICE_UNAVAILABLE)
+ {
+ _cupsSetHTTPError(status);
break;
+ }
if (response)
{
if (ippFindAttribute(request, "auth-info", IPP_TAG_TEXT) &&
!httpAddrLocalhost(http->hostaddr) && !http->tls &&
httpEncryption(http, HTTP_ENCRYPT_REQUIRED))
- return (HTTP_ERROR);
+ return (HTTP_SERVICE_UNAVAILABLE);
#endif /* HAVE_SSL */
/*
if (httpPost(http, resource))
{
if (httpReconnect(http))
- return (HTTP_ERROR);
+ return (HTTP_SERVICE_UNAVAILABLE);
else
continue;
}
*CloseUI: *StringOption
*CustomStringOption True/Custom String: "StringOption=Custom"
-*ParamCustomStringOption String: 1 string 1 10
+*ParamCustomStringOption String1: 2 string 1 10
+*ParamCustomStringOption String2: 1 string 1 10
*CloseGroup: Extended
"%%EndFeature\n"
"} stopped cleartomark\n"
"[{\n"
- "%%BeginFeature: *StringOption None\n"
+ "%%BeginFeature: *CustomStringOption True\n"
+ "(value\\0502\\051)\n"
+ "(value 1)\n"
+ "StringOption=Custom\n"
"%%EndFeature\n"
"} stopped cleartomark\n"
"[{\n"
if (s)
free(s);
- fputs("ppdEmitString (custom size): ", stdout);
+ fputs("ppdEmitString (custom size and string): ", stdout);
ppdMarkOption(ppd, "PageSize", "Custom.400x500");
+ ppdMarkOption(ppd, "StringOption", "{String1=\"value 1\" String2=value(2)}");
if ((s = ppdEmitString(ppd, PPD_ORDER_ANY, 0.0)) != NULL &&
!strcmp(s, custom_code))
[Desktop Entry]
Categories=Application;System;X-Red-Hat-Base;
Encoding=UTF-8
-Exec=htmlview http://localhost:631/
+Exec=@CUPS_HTMLVIEW@ http://localhost:631/
GenericName=
Icon=cups
MimeType=
<LI><CODE>abort-job</CODE> - Abort the job and proceed
with the next job in the queue</LI>
+ <LI><CODE>retry-current-job</CODE> - Retry the current job
+ immediately</LI>
+
<LI><CODE>retry-job</CODE> - Retry the job after waiting
for N seconds; the <VAR>cupsd.conf</VAR> <A
HREF="ref-cupsd-conf.html#JobRetryInterval"><CODE>JobRetryInterval</CODE></A>
printf("\033&l80A"); /* Set page size */
break;
+ case 595 : /* A5 */
+ printf("\033&l25A"); /* Set page size */
+ break;
+
case 624 : /* DL Envelope */
printf("\033&l90A"); /* Set page size */
break;
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
+ $Id$
+
Service manifest for CUPS.
This service manifest introduces smf(5) services for CUPS. The services
Norm.Jacobs@Sun.COM
- $Id$
+ CUPS data files in @datarootdir@...
-->
<service_bundle type='manifest' name='SUNWcups:services'>
.\"
.\" cups-lpd man page for the Common UNIX Printing System (CUPS).
.\"
-.\" Copyright 2007 by Apple Inc.
+.\" Copyright 2007-2008 by Apple Inc.
.\" Copyright 1997-2006 by Easy Software Products.
.\"
.\" These coded instructions, statements, and computer programs are the
.\" which should have been included with this file. If this file is
.\" file is missing or damaged, see the license at "http://www.cups.org/".
.\"
-.TH cups-lpd 8 "Common UNIX Printing System" "24 April 2006" "Apple Inc."
+.TH cups-lpd 8 "Common UNIX Printing System" "4 August 2008" "Apple Inc."
.SH NAME
cups-lpd \- receive print jobs and report printer status to lpd clients
.SH SYNOPSIS
.B cups-lpd
-[ -n ] [ -o
+[ -h
+.I hostname[:port]
+] [ -n ] [ -o
.I option=value
]
.SH DESCRIPTION
.fi
.SH OPTIONS
.TP 5
+.h hostname[:port]
+.br
+Sets the CUPS server (and port) to use.
+.TP 5
-n
.br
Disables reverse address lookups; normally \fIcups-lpd\fR will
#include <cups/string.h>
#include <cups/array.h>
#include <errno.h>
+#include <sys/select.h>
/*
cups_array_t *rss; /* RSS message array */
_cups_rss_t *msg; /* RSS message */
char baseurl[1024]; /* Base URL */
+ fd_set input; /* Input set for select() */
+ struct timeval timeout; /* Timeout for select() */
+ int changed; /* Has the RSS data changed? */
+ int exit_status; /* Exit status */
fprintf(stderr, "DEBUG: argc=%d\n", argc);
load_rss(rss, filename);
+ changed = cupsArrayCount(rss) == 0;
+
/*
* Localize for the user's chosen language...
*/
* Read events and update the RSS file until we are out of events.
*/
- for (;;)
+ for (exit_status = 0, event = NULL;;)
{
+ if (changed)
+ {
+ /*
+ * Save the messages to the file again, uploading as needed...
+ */
+
+ if (save_rss(rss, newname, baseurl))
+ {
+ if (http)
+ {
+ /*
+ * Upload the RSS file...
+ */
+
+ if ((status = cupsPutFile(http, resource, filename)) != HTTP_CREATED)
+ fprintf(stderr, "ERROR: Unable to PUT %s from %s on port %d: %d %s\n",
+ resource, host, port, status, httpStatus(status));
+ }
+ else
+ {
+ /*
+ * Move the new RSS file over top the old one...
+ */
+
+ if (rename(newname, filename))
+ fprintf(stderr, "ERROR: Unable to rename %s to %s: %s\n",
+ newname, filename, strerror(errno));
+ }
+
+ changed = 0;
+ }
+ }
+
+ /*
+ * Wait up to 30 seconds for an event...
+ */
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+
+ FD_ZERO(&input);
+ FD_SET(0, &input);
+
+ if (select(1, &input, NULL, NULL, &timeout) < 0)
+ continue;
+ else if (!FD_ISSET(0, &input))
+ {
+ fprintf(stderr, "DEBUG: %s is bored, exiting...\n", argv[1]);
+ break;
+ }
+
/*
* Read the next event...
*/
fputs("DEBUG: ippReadFile() returned IPP_ERROR!\n", stderr);
if (state <= IPP_IDLE)
- {
- ippDelete(event);
-
- if (http)
- unlink(filename);
-
- httpClose(http);
-
- return (0);
- }
+ break;
/*
* Collect the info from the event...
{
fprintf(stderr, "ERROR: Unable to create message: %s\n",
strerror(errno));
-
- ippDelete(event);
-
- if (http)
- unlink(filename);
-
- httpClose(http);
-
- return (1);
+ exit_status = 1;
+ break;
}
/*
cupsArrayAdd(rss, msg);
+ changed = 1;
+
/*
* Trim the array as needed...
*/
delete_message(msg);
}
-
- /*
- * Save the messages to the file again, uploading as needed...
- */
-
- if (save_rss(rss, newname, baseurl))
- {
- if (http)
- {
- /*
- * Upload the RSS file...
- */
-
- if ((status = cupsPutFile(http, resource, filename)) != HTTP_CREATED)
- fprintf(stderr, "ERROR: Unable to PUT %s from %s on port %d: %d %s\n",
- resource, host, port, status, httpStatus(status));
- }
- else
- {
- /*
- * Move the new RSS file over top the old one...
- */
-
- if (rename(newname, filename))
- fprintf(stderr, "ERROR: Unable to rename %s to %s: %s\n",
- newname, filename, strerror(errno));
- }
- }
}
if (subject)
free(text);
ippDelete(event);
+ event = NULL;
}
+
+ /*
+ * We only get here when idle or error...
+ */
+
+ ippDelete(event);
+
+ if (http)
+ {
+ unlink(filename);
+ httpClose(http);
+ }
+
+ return (exit_status);
}
return;
}
-# if defined(HAVE_PAM_SET_ITEM) && defined(PAM_RHOST)
+# ifdef HAVE_PAM_SET_ITEM
+# ifdef PAM_RHOST
pamerr = pam_set_item(pamh, PAM_RHOST, con->http.hostname);
if (pamerr != PAM_SUCCESS)
cupsdLogMessage(CUPSD_LOG_WARN,
- "cupsdAuthorize: pam_set_item() returned %d "
- "(%s)!", pamerr, pam_strerror(pamh, pamerr));
-# endif /* HAVE_PAM_SET_ITEM && PAM_RHOST */
+ "cupsdAuthorize: pam_set_item(PAM_RHOST) "
+ "returned %d (%s)!", pamerr,
+ pam_strerror(pamh, pamerr));
+# endif /* PAM_RHOST */
+
+# ifdef PAM_TTY
+ pamerr = pam_set_item(pamh, PAM_TTY, "cups");
+ if (pamerr != PAM_SUCCESS)
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "cupsdAuthorize: pam_set_item(PAM_TTY) "
+ "returned %d (%s)!", pamerr,
+ pam_strerror(pamh, pamerr));
+# endif /* PAM_TTY */
+# endif /* HAVE_PAM_SET_ITEM */
+
+# ifdef HAVE_PAM_SETCRED
+ pamerr = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT);
+ if (pamerr != PAM_SUCCESS)
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "cupsdAuthorize: pam_setcred() "
+ "returned %d (%s)!", pamerr,
+ pam_strerror(pamh, pamerr));
+# endif /* HAVE_PAM_SETCRED */
pamerr = pam_authenticate(pamh, PAM_SILENT);
if (pamerr != PAM_SUCCESS)
return (HTTP_OK);
}
- return (HTTP_UNAUTHORIZED);
+ return (HTTP_FORBIDDEN);
}
#endif /* HAVE_AUTHORIZATION_H */
return (HTTP_OK);
}
- return (HTTP_UNAUTHORIZED);
+ return (HTTP_FORBIDDEN);
}
/*
cupsdLogMessage(CUPSD_LOG_DEBUG,
"cupsdIsAuthorized: User not in group(s)!");
- return (HTTP_UNAUTHORIZED);
+ return (HTTP_FORBIDDEN);
}
* cupsdWriteClient() - Write data to a client as needed.
* check_if_modified() - Decode an "If-Modified-Since" line.
* compare_clients() - Compare two client connections.
+ * data_ready() - Check whether data is available from a client.
* encrypt_client() - Enable encryption for the client...
* get_cdsa_certificate() - Convert a keychain name into the CFArrayRef
* required by SSLSetCertificate.
struct stat *filestats);
static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
void *data);
+static int data_ready(cupsd_client_t *con);
#ifdef HAVE_SSL
static int encrypt_client(cupsd_client_t *con);
#endif /* HAVE_SSL */
*/
while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
- if (con->http.used == 0 ||
- !memchr(con->http.buffer, '\n', con->http.used))
+ if (!data_ready(con))
break;
if (status != HTTP_OK && status != HTTP_CONTINUE)
}
}
}
- while (con->http.state == HTTP_PUT_RECV && con->http.used > 0);
+ while (con->http.state == HTTP_PUT_RECV && data_ready(con));
if (con->http.state == HTTP_WAITING)
{
}
}
}
- while (con->http.state == HTTP_POST_RECV && con->http.used > 0);
+ while (con->http.state == HTTP_POST_RECV && data_ready(con));
if (con->http.state == HTTP_POST_SEND)
{
}
+/*
+ * 'data_ready()' - Check whether data is available from a client.
+ */
+
+static int /* O - 1 if data is ready, 0 otherwise */
+data_ready(cupsd_client_t *con) /* I - Client */
+{
+ if (con->http.used > 0)
+ return (1);
+#ifdef HAVE_SSL
+ else if (con->http.tls)
+ {
+# ifdef HAVE_LIBSSL
+ if (SSL_pending((SSL *)(con->http.tls)))
+ return (1);
+# elif defined(HAVE_GNUTLS)
+ if (gnutls_record_check_pending(((http_tls_t *)(con->http.tls))->session))
+ return (1);
+# elif defined(HAVE_CDSASSL)
+ size_t bytes; /* Bytes that are available */
+
+ if (!SSLGetBufferedReadSize(((http_tls_t *)(con->http.tls))->session,
+ &bytes) && bytes > 0)
+ return (1);
+# endif /* HAVE_LIBSSL */
+ }
+#endif /* HAVE_SSL */
+
+ return (0);
+}
+
+
#ifdef HAVE_SSL
/*
* 'encrypt_client()' - Enable encryption for the client...
SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
- SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
+ SSL_CTX_use_certificate_chain_file(context, ServerCertificate,
+ SSL_FILETYPE_PEM);
bio = BIO_new(_httpBIOMethods());
BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
{
switch (argv[i][1])
{
+ case 'h' : /* -h hostname[:port] */
+ if (argv[i][2])
+ cupsSetServer(argv[i] + 2);
+ else
+ {
+ i ++;
+ if (i < argc)
+ cupsSetServer(argv[i]);
+ else
+ syslog(LOG_WARNING, "Expected hostname string after -h option!");
+ }
+ break;
+
case 'o' : /* Option */
if (argv[i][2])
num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
* Sending data from the scheduler...
*/
- cupsdLogMessage(CUPSD_LOG_DEBUG,
- "cupsdProcessIPPRequest: %d status_code=%x (%s)",
- con->http.fd, con->response->request.status.status_code,
- ippErrorString(con->response->request.status.status_code));
+ cupsdLogMessage(con->response->request.status.status_code
+ >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
+ "Returning %s for %s from %s",
+ ippErrorString(con->response->request.status.status_code),
+ ippOpString(con->request->request.op.operation_id),
+ con->http.hostname);
if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE))
{
continue;
if (strcmp(attr->values[0].string.text, "abort-job") &&
+ strcmp(attr->values[0].string.text, "retry-current-job") &&
strcmp(attr->values[0].string.text, "retry-job") &&
strcmp(attr->values[0].string.text, "stop-printer"))
{
cupsdMarkDirty(CUPSD_DIRTY_JOBS);
/*
- * If the job was queued to a class, try requeuing it... For
- * faxes and retry-job queues, hold the current job for 5 minutes.
+ * If the job was queued to a class or the error policy is
+ * "retry-current-job", try requeuing it... For faxes and retry-job
+ * queues, hold the current job for 5 minutes.
*/
- if (job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT))
+ if ((job->dtype & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) ||
+ !strcmp(printer->error_policy, "retry-current-job"))
cupsdCheckJobs();
else if ((printer->type & CUPS_PRINTER_FAX) ||
!strcmp(printer->error_policy, "retry-job"))
static const char * const errors[] = /* printer-error-policy-supported values */
{
"abort-job",
+ "retry-current-job",
"retry-job",
"stop-printer"
};
cupsArrayAdd(Subscriptions, temp);
+ /*
+ * For RSS subscriptions, run the notifier immediately...
+ */
+
+ if (uri && !strncmp(uri, "rss:", 4))
+ cupsd_start_notifier(temp);
+
return (temp);
}
*
* Subscription definitions for the Common UNIX Printing System (CUPS) scheduler.
*
- * Copyright 2007 by Apple Inc.
+ * Copyright 2007-2008 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the