]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/ipp.c
Merge pull request #5621 from zdohnal/cgigetarray-sigsegv
[thirdparty/cups.git] / scheduler / ipp.c
index 5c0d35e0b54ce512828754a08a83d56762b2cf82..2fe3bf25cc3ac6d384663a95788ae9bf6f18deb2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * IPP routines for the CUPS scheduler.
  *
- * Copyright © 2007-2018 by Apple Inc.
+ * Copyright © 2007-2019 by Apple Inc.
  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
  *
  * This file contains Kerberos support code, copyright 2006 by
 #include <cups/ppd-private.h>
 
 #ifdef __APPLE__
-/*#  include <ApplicationServices/ApplicationServices.h>
-extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
-#  include <CoreFoundation/CoreFoundation.h>*/
 #  ifdef HAVE_MEMBERSHIP_H
 #    include <membership.h>
 #  endif /* HAVE_MEMBERSHIP_H */
-#  ifdef HAVE_MEMBERSHIPPRIV_H
-#    include <membershipPriv.h>
-#  else
 extern int mbr_user_name_to_uuid(const char* name, uuid_t uu);
 extern int mbr_group_name_to_uuid(const char* name, uuid_t uu);
 extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember);
-#  endif /* HAVE_MEMBERSHIPPRIV_H */
 #endif /* __APPLE__ */
 
 
@@ -115,9 +108,7 @@ static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job,
 static void    send_document(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    send_http_error(cupsd_client_t *con, http_status_t status,
                                cupsd_printer_t *printer);
-static void    send_ipp_status(cupsd_client_t *con, ipp_status_t status,
-                               const char *message, ...)
-               __attribute__((__format__(__printf__, 3, 4)));
+static void    send_ipp_status(cupsd_client_t *con, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
 static void    set_default(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -250,7 +241,7 @@ cupsdProcessIPPRequest(
       */
 
       attr = con->request->attrs;
-      if (attr && attr->name && !strcmp(attr->name, "attributes-charset") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
+      if (attr && attr->name && !strcmp(attr->name, "attributes-charset") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET && attr->group_tag == IPP_TAG_OPERATION)
        charset = attr;
       else
        charset = NULL;
@@ -258,7 +249,7 @@ cupsdProcessIPPRequest(
       if (attr)
         attr = attr->next;
 
-      if (attr && attr->name && !strcmp(attr->name, "attributes-natural-language") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
+      if (attr && attr->name && !strcmp(attr->name, "attributes-natural-language") && (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE && attr->group_tag == IPP_TAG_OPERATION)
       {
        language = attr;
 
@@ -276,12 +267,12 @@ cupsdProcessIPPRequest(
       else
        language = NULL;
 
-      if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
+      if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL && attr->group_tag == IPP_TAG_OPERATION)
        uri = attr;
-      else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
+      else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL && attr->group_tag == IPP_TAG_OPERATION)
        uri = attr;
-      else if (con->request->request.op.operation_id == CUPS_GET_PPD)
-        uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
+      else if (con->request->request.op.operation_id == CUPS_GET_PPD && (attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL && attr->group_tag == IPP_TAG_OPERATION)
+        uri = attr;
       else
        uri = NULL;
 
@@ -1911,18 +1902,14 @@ add_job(cupsd_client_t  *con,           /* I - Client connection */
   * Fill in the response info...
   */
 
-  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);
+  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);
 
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
 
-  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
-                job->state_value);
+  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_TEXT, "job-state-message", NULL, "");
-  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
-               NULL, job->reasons->values[0].string.text);
+  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, job->reasons->values[0].string.text);
 
   con->response->request.status.status_code = IPP_OK;
 
@@ -2014,7 +2001,7 @@ add_job_subscriptions(
                host[HTTP_MAX_URI],     /* Host portion of URI */
                resource[HTTP_MAX_URI]; /* Resource portion of URI */
         int    port;                   /* Port portion of URI */
-
+        struct stat info;              /* File information */
 
         recipient = attr->values[0].string.text;
 
@@ -2030,9 +2017,8 @@ add_job_subscriptions(
          return;
        }
 
-        snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
-                scheme);
-        if (access(notifier, X_OK))
+        snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin, scheme);
+        if (access(notifier, X_OK) || stat(notifier, &info) || !S_ISREG(info.st_mode))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
                          _("notify-recipient-uri URI \"%s\" uses unknown "
@@ -2604,8 +2590,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
       if (!strcmp(attr->values[i].string.text, "none"))
         continue;
 
-      printer->reasons[printer->num_reasons] =
-          _cupsStrRetain(attr->values[i].string.text);
+      printer->reasons[printer->num_reasons] = _cupsStrAlloc(attr->values[i].string.text);
       printer->num_reasons ++;
 
       if (!strcmp(attr->values[i].string.text, "paused") &&
@@ -3294,7 +3279,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
       * Cancel all jobs on all printers...
       */
 
-      cupsdCancelJobs(NULL, username, purge);
+      cupsdCancelJobs(NULL, username, purge != CUPSD_JOB_DEFAULT);
 
       cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
                      purge == CUPSD_JOB_PURGE ? "purged" : "canceled",
@@ -3353,7 +3338,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
       * Cancel all of the jobs on the named printer...
       */
 
-      cupsdCancelJobs(printer->name, username, purge);
+      cupsdCancelJobs(printer->name, username, purge != CUPSD_JOB_DEFAULT);
 
       cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
                      printer->name,
@@ -4009,8 +3994,7 @@ close_job(cupsd_client_t  *con,           /* I - Client connection */
 
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
 
-  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
-                job->state_value);
+  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
 
   con->response->request.status.status_code = IPP_OK;
 
@@ -4816,10 +4800,10 @@ copy_job_attrs(cupsd_client_t *con,     /* I - Client connection */
       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);
+      ippAddString(con->response, IPP_TAG_JOB, 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);
+      ippAddString(con->response, IPP_TAG_JOB, 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);
@@ -4895,7 +4879,7 @@ copy_printer_attrs(
 
         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);
+          member_uris->values[i].string.text = _cupsStrAlloc(p2_uri->values[0].string.text);
         }
         else
        {
@@ -4945,9 +4929,9 @@ copy_printer_attrs(
     };
 
     if (printer->type & CUPS_PRINTER_CLASS)
-      ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY, "printer-error-policy-supported", NULL, "retry-current-job");
+      ippAddString(con->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "printer-error-policy-supported", NULL, "retry-current-job");
     else
-      ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY, "printer-error-policy-supported", sizeof(errors) / sizeof(errors[0]), NULL, errors);
+      ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_CONST_TAG(IPP_TAG_NAME), "printer-error-policy-supported", sizeof(errors) / sizeof(errors[0]), NULL, errors);
   }
 
   if (!ra || cupsArrayFind(ra, "printer-icons"))
@@ -4976,7 +4960,7 @@ copy_printer_attrs(
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME, "printer-op-policy", NULL, printer->op_policy);
 
   if (!ra || cupsArrayFind(ra, "printer-state"))
-    ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", printer->state);
+    ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
 
   if (!ra || cupsArrayFind(ra, "printer-state-change-date-time"))
     ippAddDate(con->response, IPP_TAG_PRINTER, "printer-state-change-date-time", ippTimeToDate(printer->state_time));
@@ -5082,9 +5066,7 @@ copy_subscription_attrs(
        * Simple event list...
        */
 
-       ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
-                    (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
-                    "notify-events", NULL, name);
+       ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events", NULL, name);
       }
       else
       {
@@ -5096,15 +5078,12 @@ copy_subscription_attrs(
          if (sub->mask & mask)
            count ++;
 
-       attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
-                            (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
-                            "notify-events", count, NULL, NULL);
+       attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-events", count, NULL, NULL);
 
        for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
          if (sub->mask & mask)
          {
-           attr->values[count].string.text =
-               (char *)cupsdEventName((cupsd_eventmask_t)mask);
+           attr->values[count].string.text = (char *)cupsdEventName((cupsd_eventmask_t)mask);
 
            count ++;
          }
@@ -5264,6 +5243,11 @@ create_local_bg_thread(
                *response;              /* Response from printer */
   ipp_attribute_t *attr;               /* Attribute in response */
   ipp_status_t status;                 /* Status code */
+  static const char * const pattrs[] = /* Printer attributes we need */
+  {
+    "all",
+    "media-col-database"
+  };
 
 
  /*
@@ -5298,7 +5282,7 @@ create_local_bg_thread(
   request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
   ippSetVersion(request, 2, 0);
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer->device_uri);
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", NULL, "all");
+  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
 
   response = cupsDoRequest(http, request, resource);
   status   = cupsLastError();
@@ -5521,7 +5505,7 @@ create_local_printer(
   add_printer_attributes:
 
   ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
-  ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", printer->state);
+  ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
   add_printer_state_reasons(con, printer);
 
   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
@@ -5758,7 +5742,7 @@ create_subscriptions(
 
         snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
                 scheme);
-        if (access(notifier, X_OK))
+        if (access(notifier, X_OK) || !strcmp(scheme, ".") || !strcmp(scheme, ".."))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
                          _("notify-recipient-uri URI \"%s\" uses unknown "
@@ -5848,7 +5832,26 @@ create_subscriptions(
     }
 
     if (recipient)
+    {
       cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
+
+
+      if (!strncmp(recipient, "mailto:", 7) && user_data)
+      {
+        char   temp[64];               /* Temporary string */
+
+       memcpy(temp, user_data->values[0].unknown.data, (size_t)user_data->values[0].unknown.length);
+       temp[user_data->values[0].unknown.length] = '\0';
+
+       if (httpSeparateURI(HTTP_URI_CODING_ALL, temp, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_OK)
+       {
+         send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad notify-user-data \"%s\"."), temp);
+         ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM, "notify-status-code", IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES);
+         return;
+       }
+      }
+    }
+
     if (pullmethod)
       cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
     cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
@@ -7959,13 +7962,16 @@ hold_job(cupsd_client_t  *con,          /* I - Client connection */
   * Hold the job and return...
   */
 
-  if ((attr = ippFindAttribute(con->request, "job-hold-until",
-                              IPP_TAG_KEYWORD)) == NULL)
-    attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
-
-  if (attr)
+  if ((attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_ZERO)) != NULL)
   {
-    when = attr->values[0].string.text;
+    if ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || !ippValidateAttribute(attr))
+    {
+      send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
+      ippCopyAttribute(con->response, attr, 0);
+      return;
+    }
+
+    when = ippGetString(attr, 0, NULL);
 
     cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
                  "Job job-hold-until value changed by user.");
@@ -9936,17 +9942,13 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
   * Fill in the response info...
   */
 
-  httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
-                   con->clientname, con->clientport, "/jobs/%d", jobid);
-  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
-               job_uri);
+  httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL, con->clientname, con->clientport, "/jobs/%d", jobid);
+  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
 
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
 
-  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
-                job->state_value);
-  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons",
-               NULL, job->reasons->values[0].string.text);
+  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state", (int)job->state_value);
+  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD, "job-state-reasons", NULL, job->reasons->values[0].string.text);
 
   con->response->request.status.status_code = IPP_OK;
 
@@ -10329,7 +10331,39 @@ set_job_attrs(cupsd_client_t  *con,    /* I - Client connection */
       continue;
     }
 
-    if (!strcmp(attr->name, "job-priority"))
+    if (!ippValidateAttribute(attr))
+    {
+      send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Bad '%s' value."), attr->name);
+      ippCopyAttribute(con->response, attr, 0);
+      return;
+    }
+
+    if (!strcmp(attr->name, "job-hold-until"))
+    {
+      const char *when = ippGetString(attr, 0, NULL);
+                                       /* job-hold-until value */
+
+      if ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1)
+      {
+       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
+       ippCopyAttribute(con->response, attr, 0);
+       return;
+      }
+
+      cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s", when);
+      cupsdSetJobHoldUntil(job, when, 0);
+
+      if (!strcmp(when, "no-hold"))
+      {
+       cupsdReleaseJob(job);
+       check_jobs = 1;
+      }
+      else
+       cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT, "Job held by \"%s\".", username);
+
+      event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
+    }
+    else if (!strcmp(attr->name, "job-priority"))
     {
      /*
       * Change the job priority...
@@ -10395,7 +10429,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
 
          case IPP_JOB_PROCESSING :
          case IPP_JOB_STOPPED :
-             if (job->state_value != attr->values[0].integer)
+             if (job->state_value != (ipp_jstate_t)attr->values[0].integer)
              {
                send_ipp_status(con, IPP_NOT_POSSIBLE,
                                _("Job state cannot be changed."));
@@ -10449,28 +10483,6 @@ set_job_attrs(cupsd_client_t  *con,    /* I - Client connection */
       */
 
       ippCopyAttribute(job->attrs, attr, 0);
-
-     /*
-      * See if the job-name or job-hold-until is being changed.
-      */
-
-      if (!strcmp(attr->name, "job-hold-until"))
-      {
-        cupsdLogJob(job, CUPSD_LOG_DEBUG, "Setting job-hold-until to %s",
-                   attr->values[0].string.text);
-        cupsdSetJobHoldUntil(job, attr->values[0].string.text, 0);
-
-       if (!strcmp(attr->values[0].string.text, "no-hold"))
-       {
-         cupsdReleaseJob(job);
-          check_jobs = 1;
-       }
-       else
-         cupsdSetJobState(job, IPP_JOB_HELD, CUPSD_JOB_DEFAULT,
-                          "Job held by \"%s\".", username);
-
-        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
-      }
     }
     else if (attr->value_tag == IPP_TAG_DELETEATTR)
     {
@@ -11288,81 +11300,35 @@ validate_job(cupsd_client_t  *con,    /* I - Client connection */
     }
   }
 
+ /*
+  * Is the job-hold-until value valid?
+  */
+
+  if ((attr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_ZERO)) != NULL && ((ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG) || ippGetCount(attr) != 1 || !ippValidateAttribute(attr)))
+  {
+    send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-hold-until' value."));
+    ippCopyAttribute(con->response, attr, 0);
+    return;
+  }
+
  /*
   * Is the job-name valid?
   */
 
   if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
   {
-    int bad_name = 0;                  /* Is the job-name value bad? */
-
     if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
-        name->num_values != 1)
-    {
-      bad_name = 1;
-    }
-    else
-    {
-     /*
-      * Validate that job-name conforms to RFC 5198 (Network Unicode) and
-      * IPP Everywhere requirements for "name" values...
-      */
-
-      const unsigned char *nameptr;    /* Pointer into "job-name" attribute */
-
-      for (nameptr = (unsigned char *)name->values[0].string.text;
-           *nameptr;
-           nameptr ++)
-      {
-        if (*nameptr < ' ' && *nameptr != '\t')
-          break;
-        else if (*nameptr == 0x7f)
-          break;
-        else if ((*nameptr & 0xe0) == 0xc0)
-        {
-          if ((nameptr[1] & 0xc0) != 0x80)
-            break;
-
-          nameptr ++;
-        }
-        else if ((*nameptr & 0xf0) == 0xe0)
-        {
-          if ((nameptr[1] & 0xc0) != 0x80 ||
-              (nameptr[2] & 0xc0) != 0x80)
-           break;
-
-         nameptr += 2;
-       }
-        else if ((*nameptr & 0xf8) == 0xf0)
-        {
-          if ((nameptr[1] & 0xc0) != 0x80 ||
-             (nameptr[2] & 0xc0) != 0x80 ||
-             (nameptr[3] & 0xc0) != 0x80)
-           break;
-
-         nameptr += 3;
-       }
-        else if (*nameptr & 0x80)
-          break;
-      }
-
-      if (*nameptr)
-        bad_name = 1;
-    }
-
-    if (bad_name)
+        name->num_values != 1 || !ippValidateAttribute(name))
     {
       if (StrictConformance)
       {
-       send_ipp_status(con, IPP_ATTRIBUTES,
-                       _("Unsupported 'job-name' value."));
+       send_ipp_status(con, IPP_STATUS_ERROR_ATTRIBUTES_OR_VALUES, _("Unsupported 'job-name' value."));
        ippCopyAttribute(con->response, name, 0);
        return;
       }
       else
       {
-        cupsdLogMessage(CUPSD_LOG_WARN,
-                        "Unsupported 'job-name' value, deleting from request.");
+        cupsdLogMessage(CUPSD_LOG_WARN, "Unsupported 'job-name' value, deleting from request.");
         ippDeleteAttribute(con->request, name);
       }
     }