+ if (is_member)
+ break;
+
+#else
+ if (cupsdCheckGroup(username, pw, p->users[i] + 1))
+ break;
+#endif /* HAVE_MBR_UID_TO_UUID */
+ }
+#ifdef HAVE_MBR_UID_TO_UUID
+ else
+ {
+ if (p->users[i][0] == '#')
+ {
+ if (uuid_parse((char *)p->users[i] + 1, usr2_uuid))
+ uuid_clear(usr2_uuid);
+ }
+ else if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i],
+ usr2_uuid)) != 0)
+ {
+ /*
+ * Invalid ACL entries are ignored for matching; just record a
+ * warning in the log...
+ */
+
+ cupsdLogMessage(CUPSD_LOG_DEBUG,
+ "check_quotas: UUID lookup failed for ACL entry "
+ "\"%s\" (err=%d)", p->users[i], mbr_err);
+ cupsdLogMessage(CUPSD_LOG_WARN,
+ "Access control entry \"%s\" not a valid user name; "
+ "entry ignored", p->users[i]);
+ }
+
+ if (!uuid_compare(usr_uuid, usr2_uuid))
+ break;
+ }
+#else
+ else if (!strcasecmp(username, p->users[i]))
+ break;
+#endif /* HAVE_MBR_UID_TO_UUID */
+
+ if ((i < p->num_users) == p->deny_users)
+ {
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "Denying user \"%s\" access to printer \"%s\"...",
+ username, p->name);
+ return (0);
+ }
+ }
+
+ /*
+ * Check quotas...
+ */
+
+#ifdef __APPLE__
+ if (AppleQuotas && (q = cupsdFindQuota(p, username)) != NULL)
+ {
+ /*
+ * 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 if (attr->value_tag & IPP_TAG_COPY)
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ toattr->values[i].string.text =
+ _cupsStrAlloc(attr->values[i].string.text);
+ }
+ else
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ toattr->values[i].string.text =
+ _cupsStrRetain(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 if (attr->value_tag & IPP_TAG_COPY)
+ {
+ 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);
+ }
+ }
+ else
+ {
+ for (i = 0; i < attr->num_values; i ++)
+ {
+ if (!i)
+ toattr->values[i].string.charset =
+ _cupsStrRetain(attr->values[i].string.charset);
+ else
+ toattr->values[i].string.charset =
+ toattr->values[0].string.charset;
+
+ toattr->values[i].string.text =
+ _cupsStrRetain(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))
+ {
+ /*
+ * Don't send collection attributes by default to IPP/1.x clients
+ * since many do not support collections...
+ */
+
+ if (fromattr->value_tag == IPP_TAG_BEGIN_COLLECTION &&
+ !ra && to->request.status.version[0] == 1)
+ continue;
+
+ 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))
+ {
+ struct timeval tv = { attr->values[i].integer, 0 };
+ cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
+ }
+ 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, NULL, &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...
+ */
+
+ cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file!");
+ unlink(tempfile);
+ return (-1);
+ }
+
+ /*
+ * Read the source file and see what page sizes are supported...
+ */
+
+ if ((ppd = ppdOpenFile(tempfile)) == NULL)
+ {
+ unlink(tempfile);
+ return (-1);
+ }
+
+ /*
+ * Open the destination (if possible) and set the default options...
+ */
+
+ num_defaults = 0;
+ defaults = NULL;
+ cups_protocol[0] = '\0';
+
+ if ((dst = cupsFileOpen(to, "rb")) != NULL)
+ {
+ /*
+ * Read all of the default lines from the old PPD...
+ */
+
+ while (cupsFileGets(dst, buffer, sizeof(buffer)))
+ if (!strncmp(buffer, "*Default", 8))
+ {
+ /*
+ * Add the default option...
+ */
+
+ if (!ppd_parse_line(buffer, option, sizeof(option),
+ choice, sizeof(choice)))
+ {
+ ppd_option_t *ppdo; /* PPD option */
+
+
+ /*
+ * Only add the default if the default hasn't already been
+ * set and the choice exists in the new PPD...
+ */
+
+ if (!cupsGetOption(option, num_defaults, defaults) &&
+ (ppdo = ppdFindOption(ppd, option)) != NULL &&
+ ppdFindChoice(ppdo, choice))
+ num_defaults = cupsAddOption(option, choice, num_defaults,
+ &defaults);
+ }
+ }
+ else if (!strncmp(buffer, "*cupsProtocol:", 14))
+ strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
+
+ cupsFileClose(dst);
+ }
+ else if (ppdPageSize(ppd, DefaultPaperSize))
+ {
+ /*
+ * Add the default media sizes...
+ */
+
+ num_defaults = cupsAddOption("PageSize", DefaultPaperSize,
+ num_defaults, &defaults);
+ num_defaults = cupsAddOption("PageRegion", DefaultPaperSize,
+ num_defaults, &defaults);
+ num_defaults = cupsAddOption("PaperDimension", DefaultPaperSize,
+ num_defaults, &defaults);
+ num_defaults = cupsAddOption("ImageableArea", DefaultPaperSize,
+ num_defaults, &defaults);
+ }
+
+ ppdClose(ppd);
+
+ /*
+ * Open the source file for a copy...
+ */
+
+ if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
+ {
+ cupsFreeOptions(num_defaults, defaults);
+ unlink(tempfile);
+ return (-1);
+ }
+
+ /*
+ * Open the destination file for a copy...
+ */
+
+ if ((dst = cupsFileOpen(to, "wb")) == NULL)
+ {
+ cupsFreeOptions(num_defaults, defaults);
+ cupsFileClose(src);
+ unlink(tempfile);
+ return (-1);
+ }
+
+ /*
+ * Copy the source file to the destination...
+ */
+
+ while (cupsFileGets(src, buffer, sizeof(buffer)))
+ {
+ if (!strncmp(buffer, "*Default", 8))
+ {
+ /*
+ * Check for an previous default option choice...
+ */
+
+ if (!ppd_parse_line(buffer, option, sizeof(option),
+ choice, sizeof(choice)))
+ {
+ const char *val; /* Default option value */
+
+
+ if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
+ {
+ /*
+ * Substitute the previous choice...
+ */
+
+ snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
+ }
+ }
+ }
+
+ cupsFilePrintf(dst, "%s\n", buffer);
+ }
+
+ if (cups_protocol[0])
+ cupsFilePrintf(dst, "%s\n", cups_protocol);
+
+ cupsFreeOptions(num_defaults, defaults);
+
+ /*
+ * Close both files and return...
+ */
+
+ cupsFileClose(src);
+
+ unlink(tempfile);
+
+ return (cupsFileClose(dst));
+}
+
+
+/*
+ * 'copy_job_attrs()' - Copy job attributes.
+ */
+
+static void
+copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
+ cupsd_job_t *job, /* I - Job */
+ cups_array_t *ra) /* I - Requested attributes array */
+{
+ char job_uri[HTTP_MAX_URI]; /* Job URI */
+
+
+ /*
+ * Send the requested attributes for each job...
+ */
+
+ httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
+ con->servername, con->serverport, "/jobs/%d",
+ job->id);
+
+ if (!ra || cupsArrayFind(ra, "document-count"))
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "document-count", job->num_files);
+
+ if (!ra || cupsArrayFind(ra, "job-media-progress"))
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-media-progress", job->progress);
+
+ if (!ra || cupsArrayFind(ra, "job-more-info"))
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-more-info", NULL, job_uri);
+
+ if (job->state_value > IPP_JOB_PROCESSING &&
+ (!ra || cupsArrayFind(ra, "job-preserved")))
+ ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
+ job->num_files > 0);
+
+ if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
+ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+ "job-printer-up-time", time(NULL));
+
+ if (!ra || cupsArrayFind(ra, "job-state-reasons"))
+ add_job_state_reasons(con, job);
+
+ if (!ra || cupsArrayFind(ra, "job-uri"))
+ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+ "job-uri", NULL, job_uri);
+
+ copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0);
+}
+
+
+/*
+ * 'copy_printer_attrs()' - Copy printer attributes.
+ */
+
+static void
+copy_printer_attrs(
+ cupsd_client_t *con, /* I - Client connection */
+ cupsd_printer_t *printer, /* I - Printer */
+ cups_array_t *ra) /* I - Requested attributes array */
+{
+ char printer_uri[HTTP_MAX_URI];
+ /* Printer URI */
+ time_t curtime; /* Current time */
+ int i; /* Looping var */
+ ipp_attribute_t *history; /* History collection */
+
+
+ /*
+ * Copy the printer attributes to the response using requested-attributes
+ * and document-format attributes that may be provided by the client.
+ */
+
+ 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);
+
+ if (printer->num_printers > 0 &&
+ (!ra || cupsArrayFind(ra, "member-uris")))
+ {
+ ipp_attribute_t *member_uris; /* member-uris attribute */
+ cupsd_printer_t *p2; /* Printer in class */
+ ipp_attribute_t *p2_uri; /* printer-uri-supported for class printer */
+
+
+ if ((member_uris = ippAddStrings(con->response, IPP_TAG_PRINTER,
+ IPP_TAG_URI, "member-uris",
+ printer->num_printers, NULL,
+ NULL)) != NULL)
+ {
+ for (i = 0; i < printer->num_printers; i ++)
+ {
+ p2 = printer->printers[i];
+
+ if ((p2_uri = ippFindAttribute(p2->attrs, "printer-uri-supported",
+ IPP_TAG_URI)) != NULL)
+ member_uris->values[i].string.text =
+ _cupsStrRetain(p2_uri->values[0].string.text);