read_job_ticket(cupsd_client_t *con)	/* I - Client connection */
{
  cups_file_t		*fp;		/* File to read from */
  char			line[256];	/* Line data */
  int			num_options;	/* Number of options */
  cups_option_t		*options;	/* Options */
  ipp_t			*ticket;	/* New attributes */
  ipp_attribute_t	*attr,		/* Current attribute */
			*attr2,		/* Job attribute */
			*prev2;		/* Previous job attribute */


 /*
  * First open the print file...
  */

  if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
  {
    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Unable to open print file for job ticket - %s",
                    strerror(errno));
    return;
  }

 /*
  * Skip the first line...
  */

  if (cupsFileGets(fp, line, sizeof(line)) == NULL)
  {
    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Unable to read from print file for job ticket - %s",
                    strerror(errno));
    cupsFileClose(fp);
    return;
  }

  if (strncmp(line, "%!PS-Adobe-", 11) && strncmp(line, "%PDF-", 5))
  {
   /*
    * Not a DSC-compliant file, so no job ticket info will be available...
    */

    cupsFileClose(fp);
    return;
  }

 /*
  * Read job ticket info from the file...
  */

  num_options = 0;
  options     = NULL;

  while (cupsFileGets(fp, line, sizeof(line)))
  {
   /*
    * Stop at the first non-ticket line...
    */

    if (strncmp(line, "%cupsJobTicket:", 15))
      break;

   /*
    * Add the options to the option array...
    */

    num_options = cupsParseOptions(line + 15, num_options, &options);
  }

 /*
  * Done with the file; see if we have any options...
  */

  cupsFileClose(fp);

  if (num_options == 0)
    return;

 /*
  * OK, convert the options to an attribute list, and apply them to
  * the request...
  */

  ticket = ippNew();
  cupsEncodeOptions(ticket, num_options, options);

 /*
  * See what the user wants to change.
  */

  for (attr = ticket->attrs; attr; attr = attr->next)
  {
    if (attr->group_tag != IPP_TAG_JOB || !attr->name)
      continue;

    if (!strncmp(attr->name, "date-time-at-", 13) ||
        !strcmp(attr->name, "job-impressions-completed") ||
	!strcmp(attr->name, "job-media-sheets-completed") ||
	!strncmp(attr->name, "job-k-octets", 12) ||
	!strcmp(attr->name, "job-id") ||
	!strcmp(attr->name, "job-originating-host-name") ||
        !strcmp(attr->name, "job-originating-user-name") ||
	!strcmp(attr->name, "job-pages-completed") ||
	!strcmp(attr->name, "job-printer-uri") ||
	!strncmp(attr->name, "job-state", 9) ||
	!strcmp(attr->name, "job-uri") ||
	!strncmp(attr->name, "time-at-", 8))
      continue; /* Read-only attrs */

    if ((attr2 = ippFindAttribute(con->request, attr->name,
                                  IPP_TAG_ZERO)) != NULL)
    {
     /*
      * Some other value; first free the old value...
      */

      if (con->request->attrs == attr2)
      {
	con->request->attrs = attr2->next;
	prev2               = NULL;
      }
      else
      {
	for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
	  if (prev2->next == attr2)
	  {
	    prev2->next = attr2->next;
	    break;
	  }
      }

      if (con->request->last == attr2)
        con->request->last = prev2;

      ippDeleteAttribute(NULL, attr2);
    }

   /*
    * Add new option by copying it...
    */

    ippCopyAttribute(con->request, attr, 0);
  }

 /*
  * Then free the attribute list and option array...
  */

  ippDelete(ticket);
  cupsFreeOptions(num_options, options);
}