+ * TODO: Define these special page count values as constants!
+ */
+
+ if (q->page_count == -4) /* special case: unlimited user */
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "User \"%s\" request approved for printer %s (%s): "
+ "unlimited quota.",
+ username, p->name, p->info);
+ q->page_count = 0; /* allow user to print */
+ return (1);
+ }
+ else if (q->page_count == -3) /* quota exceeded */
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "User \"%s\" request denied for printer %s (%s): "
+ "quota limit exceeded.",
+ username, p->name, p->info);
+ q->page_count = 2; /* force quota exceeded failure */
+ return (-1);
+ }
+ else if (q->page_count == -2) /* quota disabled for user */
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "User \"%s\" request denied for printer %s (%s): "
+ "printing disabled for user.",
+ username, p->name, p->info);
+ q->page_count = 2; /* force quota exceeded failure */
+ return (-1);
+ }
+ else if (q->page_count == -1) /* quota access error */
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "User \"%s\" request denied for printer %s (%s): "
+ "unable to determine quota limit.",
+ username, p->name, p->info);
+ q->page_count = 2; /* force quota exceeded failure */
+ return (-1);
+ }
+ else if (q->page_count < 0) /* user not found or other error */
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "User \"%s\" request denied for printer %s (%s): "
+ "user disabled / missing quota.",
+ username, p->name, p->info);
+ q->page_count = 2; /* force quota exceeded failure */
+ return (-1);
+ }
+ else /* page within user limits */
+ {
+ q->page_count = 0; /* allow user to print */
+ return (1);
+ }
+ }
+ else
+#endif /* __APPLE__ */
+ if (p->k_limit || p->page_limit)
+ {
+ if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to allocate quota data for user \"%s\"!",
+ username);
+ return (-1);
+ }
+
+ if ((q->k_count >= p->k_limit && p->k_limit) ||
+ (q->page_count >= p->page_limit && p->page_limit))
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
+ username);
+ return (-1);
+ }
+ }
+
+ /*
+ * If we have gotten this far, we're done!
+ */
+
+ return (1);
+}
+
+
+/*
+ * 'copy_attribute()' - Copy a single attribute.
+ */
+
+static ipp_attribute_t * /* O - New attribute */
+copy_attribute(
+ ipp_t *to, /* O - Destination request/response */
+ ipp_attribute_t *attr, /* I - Attribute to copy */
+ int quickcopy) /* I - Do a quick copy? */
+{
+ int i; /* Looping var */
+ ipp_attribute_t *toattr; /* Destination attribute */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "copy_attribute(%p, %p[%s,%x,%x])", to, attr,
+ attr->name ? attr->name : "(null)", attr->group_tag,
+ attr->value_tag);
+
+ switch (attr->value_tag & ~IPP_TAG_COPY)
+ {
+ case IPP_TAG_ZERO :
+ toattr = ippAddSeparator(to);
+ break;
+
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
+ attr->name, attr->num_values, NULL);
+
+ for (i = 0; i < attr->num_values; i ++)
+ toattr->values[i].integer = attr->values[i].integer;
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ toattr = ippAddBooleans(to, attr->group_tag, attr->name,
+ attr->num_values, NULL);
+
+ for (i = 0; i < attr->num_values; i ++)
+ toattr->values[i].boolean = attr->values[i].boolean;
+ break;
+
+ case IPP_TAG_STRING :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_URI :
+ case IPP_TAG_URISCHEME :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ case IPP_TAG_MIMETYPE :
+ toattr = ippAddStrings(to, attr->group_tag,
+ (ipp_tag_t)(attr->value_tag | quickcopy),
+ attr->name, attr->num_values, NULL, NULL);
+
+ if (quickcopy)
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ toattr->values[i].string.text = attr->values[i].string.text;
+ }
+ else
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ toattr->values[i].string.text =
+ _cupsStrAlloc(attr->values[i].string.text);
+ }
+ break;
+
+ case IPP_TAG_DATE :
+ toattr = ippAddDate(to, attr->group_tag, attr->name,
+ attr->values[0].date);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ toattr = ippAddResolutions(to, attr->group_tag, attr->name,
+ attr->num_values, IPP_RES_PER_INCH,
+ NULL, NULL);
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
+ toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
+ toattr->values[i].resolution.units = attr->values[i].resolution.units;
+ }
+ break;
+
+ case IPP_TAG_RANGE :
+ toattr = ippAddRanges(to, attr->group_tag, attr->name,
+ attr->num_values, NULL, NULL);
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ toattr->values[i].range.lower = attr->values[i].range.lower;
+ toattr->values[i].range.upper = attr->values[i].range.upper;
+ }
+ break;
+
+ case IPP_TAG_TEXTLANG :
+ case IPP_TAG_NAMELANG :
+ toattr = ippAddStrings(to, attr->group_tag,
+ (ipp_tag_t)(attr->value_tag | quickcopy),
+ attr->name, attr->num_values, NULL, NULL);
+
+ if (quickcopy)
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ toattr->values[i].string.charset = attr->values[i].string.charset;
+ toattr->values[i].string.text = attr->values[i].string.text;
+ }
+ }
+ else
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (!i)
+ toattr->values[i].string.charset =
+ _cupsStrAlloc(attr->values[i].string.charset);
+ else
+ toattr->values[i].string.charset =
+ toattr->values[0].string.charset;
+
+ toattr->values[i].string.text =
+ _cupsStrAlloc(attr->values[i].string.text);
+ }
+ }
+ break;
+
+ case IPP_TAG_BEGIN_COLLECTION :
+ toattr = ippAddCollections(to, attr->group_tag, attr->name,
+ attr->num_values, NULL);
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ toattr->values[i].collection = ippNew();
+ copy_attrs(toattr->values[i].collection, attr->values[i].collection,
+ NULL, IPP_TAG_ZERO, 0);
+ }
+ break;
+
+ default :
+ toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
+ attr->name, attr->num_values, NULL);
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ toattr->values[i].unknown.length = attr->values[i].unknown.length;
+
+ if (toattr->values[i].unknown.length > 0)
+ {
+ if ((toattr->values[i].unknown.data =
+ malloc(toattr->values[i].unknown.length)) == NULL)
+ toattr->values[i].unknown.length = 0;
+ else
+ memcpy(toattr->values[i].unknown.data,
+ attr->values[i].unknown.data,
+ toattr->values[i].unknown.length);
+ }
+ }
+ break; /* anti-compiler-warning-code */
+ }
+
+ return (toattr);
+}
+
+
+/*
+ * 'copy_attrs()' - Copy attributes from one request to another.
+ */
+
+static void
+copy_attrs(ipp_t *to, /* I - Destination request */
+ ipp_t *from, /* I - Source request */
+ cups_array_t *ra, /* I - Requested attributes */
+ ipp_tag_t group, /* I - Group to copy */
+ int quickcopy) /* I - Do a quick copy? */
+{
+ ipp_attribute_t *fromattr; /* Source attribute */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
+ to, from, ra, group, quickcopy);
+
+ if (!to || !from)
+ return;
+
+ for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
+ {
+ /*
+ * Filter attributes as needed...
+ */
+
+ if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
+ fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
+ continue;
+
+ if (!ra || cupsArrayFind(ra, fromattr->name))
+ copy_attribute(to, fromattr, quickcopy);
+ }
+}
+
+
+/*
+ * 'copy_banner()' - Copy a banner file to the requests directory for the
+ * specified job.
+ */
+
+static int /* O - Size of banner file in kbytes */
+copy_banner(cupsd_client_t *con, /* I - Client connection */
+ cupsd_job_t *job, /* I - Job information */
+ const char *name) /* I - Name of banner */
+{
+ int i; /* Looping var */
+ int kbytes; /* Size of banner file in kbytes */
+ char filename[1024]; /* Job filename */
+ cupsd_banner_t *banner; /* Pointer to banner */
+ cups_file_t *in; /* Input file */
+ cups_file_t *out; /* Output file */
+ int ch; /* Character from file */
+ char attrname[255], /* Name of attribute */
+ *s; /* Pointer into name */
+ ipp_attribute_t *attr; /* Attribute */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
+ con, con ? con->http.fd : -1, job, job->id,
+ name ? name : "(null)");
+
+ /*
+ * Find the banner; return if not found or "none"...
+ */
+
+ if (!name || !strcmp(name, "none") ||
+ (banner = cupsdFindBanner(name)) == NULL)
+ return (0);
+
+ /*
+ * Open the banner and job files...
+ */
+
+ if (add_file(con, job, banner->filetype, 0))
+ return (-1);
+
+ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
+ job->num_files);
+ if ((out = cupsFileOpen(filename, "w")) == NULL)
+ {
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to create banner job file %s - %s",
+ filename, strerror(errno));
+ job->num_files --;
+ return (0);
+ }
+
+ fchmod(cupsFileNumber(out), 0640);
+ fchown(cupsFileNumber(out), RunUser, Group);
+
+ /*
+ * Try the localized banner file under the subdirectory...
+ */
+
+ strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
+ sizeof(attrname));
+ if (strlen(attrname) > 2 && attrname[2] == '-')
+ {
+ /*
+ * Convert ll-cc to ll_CC...
+ */
+
+ attrname[2] = '_';
+ attrname[3] = toupper(attrname[3] & 255);
+ attrname[4] = toupper(attrname[4] & 255);
+ }
+
+ snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
+ attrname, name);
+
+ if (access(filename, 0) && strlen(attrname) > 2)
+ {
+ /*
+ * Wasn't able to find "ll_CC" locale file; try the non-national
+ * localization banner directory.
+ */
+
+ attrname[2] = '\0';
+
+ snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
+ attrname, name);
+ }
+
+ if (access(filename, 0))
+ {
+ /*
+ * Use the non-localized banner file.
+ */
+
+ snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
+ }
+
+ if ((in = cupsFileOpen(filename, "r")) == NULL)
+ {
+ cupsFileClose(out);
+ unlink(filename);
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Unable to open banner template file %s - %s",
+ filename, strerror(errno));
+ job->num_files --;
+ return (0);
+ }
+
+ /*
+ * Parse the file to the end...
+ */
+
+ while ((ch = cupsFileGetChar(in)) != EOF)
+ if (ch == '{')
+ {
+ /*
+ * Get an attribute name...
+ */
+
+ for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
+ if (!isalpha(ch & 255) && ch != '-' && ch != '?')
+ break;
+ else if (s < (attrname + sizeof(attrname) - 1))
+ *s++ = ch;
+ else
+ break;
+
+ *s = '\0';
+
+ if (ch != '}')
+ {
+ /*
+ * Ignore { followed by stuff that is not an attribute name...
+ */
+
+ cupsFilePrintf(out, "{%s%c", attrname, ch);
+ continue;
+ }
+
+ /*
+ * See if it is defined...
+ */
+
+ if (attrname[0] == '?')
+ s = attrname + 1;
+ else
+ s = attrname;
+
+ if (!strcmp(s, "printer-name"))
+ {
+ cupsFilePuts(out, job->dest);
+ continue;
+ }
+ else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
+ {
+ /*
+ * See if we have a leading question mark...
+ */
+
+ if (attrname[0] != '?')
+ {
+ /*
+ * Nope, write to file as-is; probably a PostScript procedure...
+ */
+
+ cupsFilePrintf(out, "{%s}", attrname);
+ }
+
+ continue;
+ }
+
+ /*
+ * Output value(s)...
+ */
+
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (i)
+ cupsFilePutChar(out, ',');
+
+ switch (attr->value_tag)
+ {
+ case IPP_TAG_INTEGER :
+ case IPP_TAG_ENUM :
+ if (!strncmp(s, "time-at-", 8))
+ cupsFilePuts(out, cupsdGetDateTime(attr->values[i].integer));
+ else
+ cupsFilePrintf(out, "%d", attr->values[i].integer);
+ break;
+
+ case IPP_TAG_BOOLEAN :
+ cupsFilePrintf(out, "%d", attr->values[i].boolean);
+ break;
+
+ case IPP_TAG_NOVALUE :
+ cupsFilePuts(out, "novalue");
+ break;
+
+ case IPP_TAG_RANGE :
+ cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
+ attr->values[i].range.upper);
+ break;
+
+ case IPP_TAG_RESOLUTION :
+ cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
+ attr->values[i].resolution.yres,
+ attr->values[i].resolution.units == IPP_RES_PER_INCH ?
+ "dpi" : "dpc");
+ break;
+
+ case IPP_TAG_URI :
+ case IPP_TAG_STRING :
+ case IPP_TAG_TEXT :
+ case IPP_TAG_NAME :
+ case IPP_TAG_KEYWORD :
+ case IPP_TAG_CHARSET :
+ case IPP_TAG_LANGUAGE :
+ if (!strcasecmp(banner->filetype->type, "postscript"))
+ {
+ /*
+ * Need to quote strings for PS banners...
+ */
+
+ const char *p;
+
+ for (p = attr->values[i].string.text; *p; p ++)
+ {
+ if (*p == '(' || *p == ')' || *p == '\\')
+ {
+ cupsFilePutChar(out, '\\');
+ cupsFilePutChar(out, *p);
+ }
+ else if (*p < 32 || *p > 126)
+ cupsFilePrintf(out, "\\%03o", *p & 255);
+ else
+ cupsFilePutChar(out, *p);
+ }
+ }
+ else
+ cupsFilePuts(out, attr->values[i].string.text);
+ break;
+
+ default :
+ break; /* anti-compiler-warning-code */
+ }
+ }
+ }
+ else if (ch == '\\') /* Quoted char */
+ {
+ ch = cupsFileGetChar(in);
+
+ if (ch != '{') /* Only do special handling for \{ */
+ cupsFilePutChar(out, '\\');
+
+ cupsFilePutChar(out, ch);
+ }
+ else
+ cupsFilePutChar(out, ch);
+
+ cupsFileClose(in);
+
+ kbytes = (cupsFileTell(out) + 1023) / 1024;
+
+ if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
+ IPP_TAG_INTEGER)) != NULL)
+ attr->values[0].integer += kbytes;
+
+ cupsFileClose(out);
+
+ return (kbytes);
+}
+
+
+/*
+ * 'copy_file()' - Copy a PPD file or interface script...
+ */
+
+static int /* O - 0 = success, -1 = error */
+copy_file(const char *from, /* I - Source file */
+ const char *to) /* I - Destination file */
+{
+ cups_file_t *src, /* Source file */
+ *dst; /* Destination file */
+ int bytes; /* Bytes to read/write */
+ char buffer[2048]; /* Copy buffer */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
+
+ /*
+ * Open the source and destination file for a copy...
+ */
+
+ if ((src = cupsFileOpen(from, "rb")) == NULL)
+ return (-1);
+
+ if ((dst = cupsFileOpen(to, "wb")) == NULL)
+ {
+ cupsFileClose(src);
+ return (-1);
+ }
+
+ /*
+ * Copy the source file to the destination...
+ */
+
+ while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
+ if (cupsFileWrite(dst, buffer, bytes) < bytes)
+ {
+ cupsFileClose(src);
+ cupsFileClose(dst);
+ return (-1);
+ }
+
+ /*
+ * Close both files and return...
+ */
+
+ cupsFileClose(src);
+
+ return (cupsFileClose(dst));
+}
+
+
+/*
+ * 'copy_model()' - Copy a PPD model file, substituting default values
+ * as needed...
+ */
+
+static int /* O - 0 = success, -1 = error */
+copy_model(cupsd_client_t *con, /* I - Client connection */
+ const char *from, /* I - Source file */
+ const char *to) /* I - Destination file */
+{
+ fd_set input; /* select() input set */
+ struct timeval timeout; /* select() timeout */
+ int maxfd; /* Max file descriptor for select() */
+ char tempfile[1024]; /* Temporary PPD file */
+ int tempfd; /* Temporary PPD file descriptor */
+ int temppid; /* Process ID of cups-driverd */
+ int temppipe[2]; /* Temporary pipes */
+ char *argv[4], /* Command-line arguments */
+ *envp[MAX_ENV]; /* Environment */
+ cups_file_t *src, /* Source file */
+ *dst; /* Destination file */
+ ppd_file_t *ppd; /* PPD file */
+ int bytes, /* Bytes from pipe */
+ total; /* Total bytes from pipe */
+ char buffer[2048]; /* Copy buffer */
+ int i; /* Looping var */
+ char option[PPD_MAX_NAME], /* Option name */
+ choice[PPD_MAX_NAME]; /* Choice name */
+ int num_defaults; /* Number of default options */
+ cups_option_t *defaults; /* Default options */
+ char cups_protocol[PPD_MAX_LINE];
+ /* cupsProtocol attribute */
+
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG2,
+ "copy_model(con=%p, from=\"%s\", to=\"%s\")",
+ con, from, to);
+
+ /*
+ * Run cups-driverd to get the PPD file...
+ */
+
+ argv[0] = "cups-driverd";
+ argv[1] = "cat";
+ argv[2] = (char *)from;
+ argv[3] = NULL;
+
+ cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
+
+ snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
+ snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
+ tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (tempfd < 0)
+ return (-1);
+
+ cupsdOpenPipe(temppipe);
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "copy_model: Running \"cups-driverd cat %s\"...", from);
+
+ if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
+ -1, -1, 0, DefaultProfile, &temppid))
+ {
+ close(tempfd);
+ unlink(tempfile);
+
+ return (-1);
+ }
+
+ close(temppipe[1]);
+
+ /*
+ * Wait up to 30 seconds for the PPD file to be copied...
+ */
+
+ total = 0;
+
+ if (temppipe[0] > CGIPipes[0])
+ maxfd = temppipe[0] + 1;
+ else
+ maxfd = CGIPipes[0] + 1;
+
+ for (;;)
+ {
+ /*
+ * See if we have data ready...
+ */
+
+ FD_ZERO(&input);
+ FD_SET(temppipe[0], &input);
+ FD_SET(CGIPipes[0], &input);
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+
+ if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ break;
+ }
+ else if (i == 0)
+ {
+ /*
+ * We have timed out...
+ */
+
+ break;
+ }
+
+ if (FD_ISSET(temppipe[0], &input))
+ {
+ /*
+ * Read the PPD file from the pipe, and write it to the PPD file.
+ */
+
+ if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
+ {
+ if (write(tempfd, buffer, bytes) < bytes)
+ break;
+
+ total += bytes;
+ }
+ else
+ break;
+ }
+
+ if (FD_ISSET(CGIPipes[0], &input))
+ cupsdUpdateCGI();
+ }
+
+ close(temppipe[0]);
+ close(tempfd);
+
+ if (!total)
+ {
+ /*
+ * No data from cups-deviced...