copy_job_attrs(cupsd_client_t *con,	/* I - Client connection */
	       cupsd_job_t    *job,	/* I - Job */
	       cups_array_t   *ra,	/* I - Requested attributes array */
	       cups_array_t   *exclude)	/* I - Private attributes array */
{
  char	job_uri[HTTP_MAX_URI];		/* Job URI */


 /*
  * Send the requested attributes for each job...
  */

  if (!cupsArrayFind(exclude, "all"))
  {
    if ((!exclude || !cupsArrayFind(exclude, "number-of-documents")) &&
        (!ra || cupsArrayFind(ra, "number-of-documents")))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
		    "number-of-documents", job->num_files);

    if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
        (!ra || cupsArrayFind(ra, "job-media-progress")))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
		    "job-media-progress", job->progress);

    if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
        (!ra || cupsArrayFind(ra, "job-more-info")))
    {
      httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "http",
                       NULL, con->clientname, con->clientport, "/jobs/%d",
		       job->id);
      ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
		   "job-more-info", NULL, job_uri);
    }

    if (job->state_value > IPP_JOB_PROCESSING &&
	(!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
        (!ra || cupsArrayFind(ra, "job-preserved")))
      ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
		    job->num_files > 0);

    if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
        (!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-printer-uri"))
  {
    httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
		     con->clientname, con->clientport,
		     (job->dtype & CUPS_PRINTER_CLASS) ? "/classes/%s" :
		                                         "/printers/%s",
		     job->dest);
    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
        	 "job-printer-uri", NULL, job_uri);
  }

  if (!ra || cupsArrayFind(ra, "job-uri"))
  {
    httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
		     con->clientname, con->clientport, "/jobs/%d",
		     job->id);
    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
        	 "job-uri", NULL, job_uri);
  }

  if (job->attrs)
  {
    copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
  }
  else
  {
   /*
    * Generate attributes from the job structure...
    */

    if (job->completed_time && (!ra || cupsArrayFind(ra, "date-time-at-completed")))
      ippAddDate(con->response, IPP_TAG_JOB, "date-time-at-completed", ippTimeToDate(job->completed_time));

    if (job->creation_time && (!ra || cupsArrayFind(ra, "date-time-at-creation")))
      ippAddDate(con->response, IPP_TAG_JOB, "date-time-at-creation", ippTimeToDate(job->creation_time));

    if (!ra || cupsArrayFind(ra, "job-id"))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);

    if (!ra || cupsArrayFind(ra, "job-k-octets"))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-k-octets", job->koctets);

    if (job->name && (!ra || cupsArrayFind(ra, "job-name")))
      ippAddString(con->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_NAME), "job-name", NULL, job->name);

    if (job->username && (!ra || cupsArrayFind(ra, "job-originating-user-name")))
      ippAddString(con->response, IPP_TAG_JOB, IPP_CONST_TAG(IPP_TAG_NAME), "job-originating-user-name", NULL, job->username);

    if (!ra || cupsArrayFind(ra, "job-state"))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);

    if (!ra || cupsArrayFind(ra, "job-state-reasons"))
    {
      switch (job->state_value)
      {
        default : /* Should never get here for processing, pending, held, or stopped jobs since they don't get unloaded... */
	    break;
        case IPP_JSTATE_ABORTED :
	    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-aborted-by-system");
	    break;
        case IPP_JSTATE_CANCELED :
	    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user");
	    break;
        case IPP_JSTATE_COMPLETED :
	    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-completed-successfully");
	    break;
      }
    }

    if (job->completed_time && (!ra || cupsArrayFind(ra, "time-at-completed")))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-completed", (int)job->completed_time);

    if (job->creation_time && (!ra || cupsArrayFind(ra, "time-at-creation")))
      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation", (int)job->creation_time);
  }
}