]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/ipp.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / ipp.c
index e94c7310ac271efbd8b48d5564bb5d3057b6cf6b..22a21f496a50d3489fe514fa2f31d8f318823b20 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: ipp.c 5383 2006-04-07 15:36:10Z mike $"
+ * "$Id: ipp.c 5970 2006-09-19 20:11:08Z mike $"
  *
  *   IPP routines for the Common UNIX Printing System (CUPS) scheduler.
  *
@@ -88,6 +88,7 @@
  *   set_printer_defaults()      - Set printer default options from a request.
  *   start_printer()             - Start a printer.
  *   stop_printer()              - Stop a printer.
+ *   url_encode_attr()           - URL-encode a string attribute.
  *   user_allowed()              - See if a user is allowed to print to a queue.
  *   validate_job()              - Validate printer options and destination.
  *   validate_name()             - Make sure the printer name only contains
@@ -202,6 +203,8 @@ static void set_printer_defaults(cupsd_client_t *con,
                                     cupsd_printer_t *printer);
 static void    start_printer(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    stop_printer(cupsd_client_t *con, ipp_attribute_t *uri);
+static void    url_encode_attr(ipp_attribute_t *attr, char *buffer,
+                               int bufsize);
 static int     user_allowed(cupsd_printer_t *p, const char *username);
 static void    validate_job(cupsd_client_t *con, ipp_attribute_t *uri);
 static int     validate_name(const char *name);
@@ -624,24 +627,42 @@ cupsdProcessIPPRequest(
 
     if (cupsdSendHeader(con, HTTP_OK, "application/ipp"))
     {
+#ifdef CUPSD_USE_CHUNKING
+     /*
+      * Because older versions of CUPS (1.1.17 and older) and some IPP
+      * clients do not implement chunking properly, we cannot use
+      * chunking by default.  This may become the default in future
+      * CUPS releases, or we might add a configuration directive for
+      * it.
+      */
+
       if (con->http.version == HTTP_1_1)
       {
-       con->http.data_encoding = HTTP_ENCODE_CHUNKED;
+       if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
+         return (0);
+
+       if (cupsdFlushHeader(con) < 0)
+         return (0);
 
-       httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n");
+       con->http.data_encoding = HTTP_ENCODE_CHUNKED;
       }
       else
+#endif /* CUPSD_USE_CHUNKING */
       {
-       con->http.data_encoding  = HTTP_ENCODE_LENGTH;
-       con->http.data_remaining = ippLength(con->response);
+        size_t length;                 /* Length of response */
 
-        if (con->http.data_remaining < INT_MAX)
-         con->http._data_remaining = con->http.data_remaining;
-       else
-         con->http._data_remaining = INT_MAX;
 
-       httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
-                  CUPS_LLCAST con->http.data_remaining);
+       length = ippLength(con->response);
+
+       if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
+                      CUPS_LLCAST length) < 0)
+         return (0);
+
+       if (cupsdFlushHeader(con) < 0)
+         return (0);
+
+       con->http.data_encoding  = HTTP_ENCODE_LENGTH;
+       con->http.data_remaining = length;
       }
 
       cupsdLogMessage(CUPSD_LOG_DEBUG2,
@@ -771,7 +792,6 @@ add_class(cupsd_client_t  *con,             /* I - Client connection */
   cupsd_printer_t *pclass,             /* Class */
                *member;                /* Member printer/class */
   cups_ptype_t dtype;                  /* Destination type */
-  const char   *dest;                  /* Printer or class name */
   ipp_attribute_t *attr;               /* Printer attribute */
   int          modify;                 /* Non-zero if we just modified */
   char         newname[IPP_MAX_NAME];  /* New class name */
@@ -994,7 +1014,7 @@ add_class(cupsd_client_t  *con,            /* I - Client connection */
                       sizeof(method), username, sizeof(username), host,
                      sizeof(host), &port, resource, sizeof(resource));
 
-      if ((dest = cupsdValidateDest(host, resource, &dtype, &member)) == NULL)
+      if (!cupsdValidateDest(host, resource, &dtype, &member))
       {
        /*
        * Bad URI...
@@ -1107,7 +1127,7 @@ add_file(cupsd_client_t *con,             /* I - Connection to client */
 
   if (!compressions || !filetypes)
   {
-    cupsdCancelJob(job, 1);
+    cupsdCancelJob(job, 1, IPP_JOB_ABORTED);
 
     send_ipp_status(con, IPP_INTERNAL_ERROR,
                     _("Unable to allocate memory for file types!"));
@@ -1454,7 +1474,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
   job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
                              "job-state", IPP_JOB_STOPPED);
-  job->state_value = job->state->values[0].integer;
+  job->state_value = (ipp_jstate_t)job->state->values[0].integer;
   job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
                               "job-media-sheets-completed", 0);
   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
@@ -1715,7 +1735,7 @@ add_job_state_reasons(
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
                   con, con->http.fd, job ? job->id : 0);
 
-  switch (job ? job->state_value : IPP_JOB_CANCELLED)
+  switch (job ? job->state_value : IPP_JOB_CANCELED)
   {
     case IPP_JOB_PENDING :
        dest = cupsdFindDest(job->dest);
@@ -1750,7 +1770,7 @@ add_job_state_reasons(
                     "job-state-reasons", NULL, "job-stopped");
         break;
 
-    case IPP_JOB_CANCELLED :
+    case IPP_JOB_CANCELED :
         ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
                     "job-state-reasons", NULL, "job-canceled-by-user");
         break;
@@ -1948,7 +1968,6 @@ add_job_uuid(cupsd_client_t *con, /* I - Client connection */
              cupsd_job_t    *job)      /* I - Job */
 {
   char                 uuid[1024];     /* job-uuid string */
-  ipp_attribute_t      *attr;          /* job-uuid attribute */
   _cups_md5_state_t    md5state;       /* MD5 state */
   unsigned char                md5sum[16];     /* MD5 digest/sum */
 
@@ -1957,7 +1976,7 @@ add_job_uuid(cupsd_client_t *con, /* I - Client connection */
   * First see if the job already has a job-uuid attribute; if so, return...
   */
 
-  if ((attr = ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI)) != NULL)
+  if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
     return;
 
  /*
@@ -2859,7 +2878,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
     cupsdCancelJobs(NULL, username, purge);
 
     cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
-                    purge ? "purged" : "cancelled", get_username(con));
+                    purge ? "purged" : "canceled", get_username(con));
   }
   else
   {
@@ -2880,7 +2899,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
     cupsdCancelJobs(dest, username, purge);
 
     cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
-                    dest, purge ? "purged" : "cancelled", get_username(con));
+                    dest, purge ? "purged" : "canceled", get_username(con));
   }
 
   con->response->request.status.status_code = IPP_OK;
@@ -3030,17 +3049,17 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
   }
 
  /*
-  * See if the job is already completed, cancelled, or aborted; if so,
+  * See if the job is already completed, canceled, or aborted; if so,
   * we can't cancel...
   */
 
-  if (job->state_value >= IPP_JOB_CANCELLED)
+  if (job->state_value >= IPP_JOB_CANCELED)
   {
     switch (job->state_value)
     {
-      case IPP_JOB_CANCELLED :
+      case IPP_JOB_CANCELED :
          send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Job #%d is already cancelled - can\'t cancel."),
+                         _("Job #%d is already canceled - can\'t cancel."),
                          jobid);
           break;
 
@@ -3064,13 +3083,10 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
   * Cancel the job and return...
   */
 
-  cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
-                "Job cancelled by \"%s\".", username);
-
-  cupsdCancelJob(job, 0);
+  cupsdCancelJob(job, 0, IPP_JOB_CANCELED);
   cupsdCheckJobs();
 
-  cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was cancelled by \"%s\".", jobid,
+  cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was canceled by \"%s\".", jobid,
                   username);
 
   con->response->request.status.status_code = IPP_OK;
@@ -4321,7 +4337,8 @@ copy_printer_attrs(
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
                      "ipp", NULL, con->servername, con->serverport,
-                    "/printers/%s", printer->name);
+                    (printer->type & CUPS_PRINTER_CLASS) ?
+                        "/classes/%s" : "/printers/%s", printer->name);
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
                 "printer-uri-supported", NULL, printer_uri);
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
@@ -4368,7 +4385,7 @@ copy_subscription_attrs(
       */
 
       ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
-                   IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                   (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
                    "notify-events", NULL, name);
     }
     else
@@ -4382,7 +4399,7 @@ copy_subscription_attrs(
           count ++;
 
       attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
-                           IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                           (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
                            "notify-events", count, NULL, NULL);
 
       for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
@@ -4602,8 +4619,8 @@ create_requested_array(ipp_t *request)    /* I - IPP request */
       cupsArrayAdd(ra, "notify-lease-duration-default");
       cupsArrayAdd(ra, "notify-lease-duration-supported");
       cupsArrayAdd(ra, "notify-max-events-supported");
-      cupsArrayAdd(ra, "notify-notify-events-default");
-      cupsArrayAdd(ra, "notify-notify-events-supported");
+      cupsArrayAdd(ra, "notify-events-default");
+      cupsArrayAdd(ra, "notify-events-supported");
       cupsArrayAdd(ra, "notify-pull-method-supported");
       cupsArrayAdd(ra, "notify-schemes-supported");
       cupsArrayAdd(ra, "operations-supported");
@@ -4662,10 +4679,9 @@ create_subscription(
   http_status_t        status;                 /* Policy status */
   int                  i;              /* Looping var */
   ipp_attribute_t      *attr;          /* Current attribute */
-  const char           *dest;          /* Destination */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method portion of URI */
+  char                 scheme[HTTP_MAX_URI],
+                                       /* Scheme portion of URI */
                        userpass[HTTP_MAX_URI],
                                        /* Username portion of URI */
                        host[HTTP_MAX_URI],
@@ -4705,29 +4721,26 @@ create_subscription(
                   "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
                   con, con->http.fd, uri->values[0].string.text);
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), userpass, sizeof(userpass), host,
+  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
+                  sizeof(scheme), userpass, sizeof(userpass), host,
                  sizeof(host), &port, resource, sizeof(resource));
 
   if (!strcmp(resource, "/"))
   {
-    dest    = NULL;
     dtype   = (cups_ptype_t)0;
     printer = NULL;
   }
   else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
   {
-    dest    = NULL;
     dtype   = (cups_ptype_t)0;
     printer = NULL;
   }
   else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
   {
-    dest    = NULL;
     dtype   = CUPS_PRINTER_CLASS;
     printer = NULL;
   }
-  else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  else if (!cupsdValidateDest(host, resource, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -4798,10 +4811,55 @@ create_subscription(
     {
       if (!strcmp(attr->name, "notify-recipient") &&
           attr->value_tag == IPP_TAG_URI)
+      {
+       /*
+        * Validate the recipient scheme against the ServerBin/notifier
+       * directory...
+       */
+
+       char    notifier[1024];         /* Notifier filename */
+
+
         recipient = attr->values[0].string.text;
+
+       if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
+                           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-recipient URI \"%s\"!"), recipient);
+         ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
+                       "notify-status-code", IPP_URI_SCHEME);
+         return;
+       }
+
+        snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
+                scheme);
+        if (access(notifier, X_OK))
+       {
+          send_ipp_status(con, IPP_NOT_POSSIBLE,
+                         _("notify-recipient URI \"%s\" uses unknown scheme!"),
+                         recipient);
+         ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
+                       "notify-status-code", IPP_URI_SCHEME);
+         return;
+       }
+      }
       else if (!strcmp(attr->name, "notify-pull-method") &&
                attr->value_tag == IPP_TAG_KEYWORD)
+      {
         pullmethod = attr->values[0].string.text;
+
+        if (strcmp(pullmethod, "ippget"))
+       {
+          send_ipp_status(con, IPP_NOT_POSSIBLE,
+                         _("Bad notify-pull-method \"%s\"!"), pullmethod);
+         ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
+                       "notify-status-code", IPP_ATTRIBUTES);
+         return;
+       }
+      }
       else if (!strcmp(attr->name, "notify-charset") &&
                attr->value_tag == IPP_TAG_CHARSET &&
               strcmp(attr->values[0].string.text, "us-ascii") &&
@@ -5091,14 +5149,12 @@ static void
 get_devices(cupsd_client_t *con)       /* I - Client connection */
 {
   http_status_t                status;         /* Policy status */
-  int                  i;              /* Looping var */
   ipp_attribute_t      *limit,         /* Limit attribute */
                        *requested;     /* requested-attributes attribute */
   char                 command[1024],  /* cups-deviced command */
                        options[1024],  /* Options to pass to command */
-                       attrs[1024],    /* String for requested attributes */
-                       *aptr;          /* Pointer into string */
-  int                  alen;           /* Length of attribute value */
+                       requested_str[256];
+                                       /* String for requested attributes */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
@@ -5122,48 +5178,16 @@ get_devices(cupsd_client_t *con)        /* I - Client connection */
                                IPP_TAG_KEYWORD);
 
   if (requested)
-  {
-    for (i = 0, aptr = attrs; i < requested->num_values; i ++)
-    {
-     /*
-      * Check that we have enough room...
-      */
-
-      alen = strlen(requested->values[i].string.text);
-      if (alen > (sizeof(attrs) - (aptr - attrs) - 2))
-        break;
-
-     /*
-      * Put commas between values...
-      */
-
-      if (i)
-        *aptr++ = ',';
-
-     /*
-      * Add the value to the end of the string...
-      */
-
-      strcpy(aptr, requested->values[i].string.text);
-      aptr += alen;
-    }
-
-   /*
-    * If we have more attribute names than will fit, default to "all"...
-    */
-
-    if (i < requested->num_values)
-      strcpy(attrs, "all");
-  }
+    url_encode_attr(requested, requested_str, sizeof(requested_str));
   else
-    strcpy(attrs, "all");
+    strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
 
   snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
   snprintf(options, sizeof(options),
-           "%d+%d+%d+requested-attributes=%s",
+           "%d+%d+%d+%s",
            con->request->request.op.request_id,
            limit ? limit->values[0].integer : 0, (int)User,
-          attrs);
+          requested_str);
 
   if (cupsdSendCommand(con, command, options, 1))
   {
@@ -5631,15 +5655,14 @@ static void
 get_ppds(cupsd_client_t *con)          /* I - Client connection */
 {
   http_status_t                status;         /* Policy status */
-  int                  i;              /* Looping var */
   ipp_attribute_t      *limit,         /* Limit attribute */
                        *make,          /* ppd-make attribute */
                        *requested;     /* requested-attributes attribute */
   char                 command[1024],  /* cups-deviced command */
                        options[1024],  /* Options to pass to command */
-                       attrs[1024],    /* String for requested attributes */
-                       *aptr;          /* Pointer into string */
-  int                  alen;           /* Length of attribute value */
+                       requested_str[256],
+                                       /* String for requested attributes */
+                       make_str[256];  /* Escaped ppd-make string */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
@@ -5664,50 +5687,20 @@ get_ppds(cupsd_client_t *con)           /* I - Client connection */
                                IPP_TAG_KEYWORD);
 
   if (requested)
-  {
-    for (i = 0, aptr = attrs; i < requested->num_values; i ++)
-    {
-     /*
-      * Check that we have enough room...
-      */
-
-      alen = strlen(requested->values[i].string.text);
-      if (alen > (sizeof(attrs) - (aptr - attrs) - 2))
-        break;
-
-     /*
-      * Put commas between values...
-      */
-
-      if (i)
-        *aptr++ = ',';
-
-     /*
-      * Add the value to the end of the string...
-      */
-
-      strcpy(aptr, requested->values[i].string.text);
-      aptr += alen;
-    }
-
-   /*
-    * If we have more attribute names than will fit, default to "all"...
-    */
+    url_encode_attr(requested, requested_str, sizeof(requested_str));
+  else
+    strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
 
-    if (i < requested->num_values)
-      strcpy(attrs, "all");
-  }
+  if (make)
+    url_encode_attr(make, make_str, sizeof(make_str));
   else
-    strcpy(attrs, "all");
+    make_str[0] = '\0';
 
   snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
-  snprintf(options, sizeof(options),
-           "list+%d+%d+requested-attributes=%s%s%s",
+  snprintf(options, sizeof(options), "list+%d+%d+%s%s%s",
            con->request->request.op.request_id,
            limit ? limit->values[0].integer : 0,
-          attrs,
-          make ? "%20ppd-make=" : "",
-          make ? make->values[0].string.text : "");
+          requested_str, make ? "%20" : "", make_str);
 
   if (cupsdSendCommand(con, command, options, 0))
   {
@@ -5740,7 +5733,6 @@ get_printer_attrs(cupsd_client_t  *con,   /* I - Client connection */
                  ipp_attribute_t *uri) /* I - Printer URI */
 {
   http_status_t                status;         /* Policy status */
-  const char           *dest;          /* Destination */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
   char                 method[HTTP_MAX_URI],
                                        /* Method portion of URI */
@@ -5766,7 +5758,7 @@ get_printer_attrs(cupsd_client_t  *con,   /* I - Client connection */
                   sizeof(method), username, sizeof(username), host,
                  sizeof(host), &port, resource, sizeof(resource));
 
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(host, resource, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -6275,6 +6267,9 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
 
   cupsdHoldJob(job);
 
+  cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
+                "Job held by user.");
+
   if ((newattr = ippFindAttribute(con->request, "job-hold-until",
                                   IPP_TAG_KEYWORD)) == NULL)
     newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
@@ -6308,6 +6303,9 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
     */
 
     cupsdSetJobHoldUntil(job, attr->values[0].string.text);
+
+    cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
+                  "Job job-hold-until value changed by user.");
   }
 
   cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was held by \"%s\".", jobid,
@@ -6329,8 +6327,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
   ipp_attribute_t *attr;               /* Current attribute */
   int          jobid;                  /* Job ID */
   cupsd_job_t  *job;                   /* Current job */
-  const char   *src,                   /* Source printer/class */
-               *dest;                  /* Destination */
+  const char   *src;                   /* Source printer/class */
   cups_ptype_t stype,                  /* Source type (printer or class) */
                dtype;                  /* Destination type (printer or class) */
   char         method[HTTP_MAX_URI],   /* Method portion of URI */
@@ -6365,7 +6362,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
                   sizeof(method), username, sizeof(username), host,
                  sizeof(host), &port, resource, sizeof(resource));
 
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &dprinter)) == NULL)
+  if (!cupsdValidateDest(host, resource, &dtype, &dprinter))
   {
    /*
     * Bad URI...
@@ -6894,8 +6891,10 @@ print_job(cupsd_client_t  *con,          /* I - Client connection */
   * See if we need to add the ending sheet...
   */
 
+  attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
+
   if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
-      attr->num_values > 1)
+      attr && attr->num_values > 1)
   {
    /*
     * Yes...
@@ -7321,6 +7320,9 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
 
     attr->value_tag = IPP_TAG_KEYWORD;
     attr->values[0].string.text = _cupsStrAlloc("no-hold");
+
+    cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
+                  "Job job-hold-until value changed by user.");
   }
 
  /*
@@ -7329,6 +7331,9 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
 
   cupsdReleaseJob(job);
 
+  cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
+                "Job released by user.");
+
   cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was released by \"%s\".", jobid,
                   username);
 
@@ -7519,7 +7524,7 @@ restart_job(cupsd_client_t  *con, /* I - Client connection */
 
   cupsdLoadJob(job);
 
-  if (!job->attrs ||job->num_files == 0)
+  if (!job->attrs || job->num_files == 0)
   {
    /*
     * Nope - return a "not possible" error...
@@ -7737,6 +7742,8 @@ send_document(cupsd_client_t  *con,       /* I - Client connection */
     return;
   }
 
+  printer = cupsdFindDest(job->dest);
+
  /*
   * See if the job is owned by the requesting user...
   */
@@ -7868,8 +7875,6 @@ send_document(cupsd_client_t  *con,       /* I - Client connection */
     return;
   }
 
-  printer = cupsdFindDest(job->dest);
-
   if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
   {
     snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
@@ -8008,7 +8013,7 @@ send_document(cupsd_client_t  *con,       /* I - Client connection */
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
 
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
-                job ? job->state_value : IPP_JOB_CANCELLED);
+                job ? job->state_value : IPP_JOB_CANCELED);
   add_job_state_reasons(con, job);
 
   con->response->request.status.status_code = IPP_OK;
@@ -8057,14 +8062,12 @@ send_ipp_status(cupsd_client_t *con,    /* I - Client connection */
               _cupsLangString(con->language, message), ap);
     va_end(ap);
 
-    cupsdLogMessage(status >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_INFO,
-                    "%s %s: %s",
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
                    ippOpString(con->request->request.op.operation_id),
                    ippErrorString(status), formatted);
   }
   else
-    cupsdLogMessage(status >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_INFO,
-                    "%s %s",
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
                    ippOpString(con->request->request.op.operation_id),
                    ippErrorString(status));
 
@@ -8185,6 +8188,7 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
                        resource[HTTP_MAX_URI];
                                        /* Resource portion of URI */
   int                  port;           /* Port portion of URI */
+  int                  event;          /* Events? */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
@@ -8286,6 +8290,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
 
   cupsdLoadJob(job);
 
+  event = 0;
+
   for (attr = con->request->attrs; attr; attr = attr->next)
   {
     if (attr->group_tag != IPP_TAG_JOB || !attr->name)
@@ -8349,7 +8355,10 @@ set_job_attrs(cupsd_client_t  *con,      /* I - Client connection */
        return;
       }
       else if (con->response->request.status.status_code == IPP_OK)
+      {
         cupsdSetJobPriority(job, attr->values[0].integer);
+        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
+      }
     }
     else if (!strcmp(attr->name, "job-state"))
     {
@@ -8379,7 +8388,9 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
               else if (con->response->request.status.status_code == IPP_OK)
              {
                job->state->values[0].integer = attr->values[0].integer;
-               job->state_value              = attr->values[0].integer;
+               job->state_value              = (ipp_jstate_t)attr->values[0].integer;
+
+                event |= CUPSD_EVENT_JOB_STATE;
              }
              break;
 
@@ -8393,7 +8404,7 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
              }
              break;
 
-         case IPP_JOB_CANCELLED :
+         case IPP_JOB_CANCELED :
          case IPP_JOB_ABORTED :
          case IPP_JOB_COMPLETED :
              if (job->state_value > IPP_JOB_PROCESSING)
@@ -8403,16 +8414,7 @@ set_job_attrs(cupsd_client_t  *con,      /* I - Client connection */
                return;
              }
               else if (con->response->request.status.status_code == IPP_OK)
-             {
-                cupsdCancelJob(job, 0);
-
-               if (JobHistory)
-               {
-                  job->state->values[0].integer = attr->values[0].integer;
-                  job->state_value              = attr->values[0].integer;
-                 cupsdSaveJob(job);
-               }
-             }
+                cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer);
              break;
        }
       }
@@ -8454,6 +8456,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
          cupsdReleaseJob(job);
        else
          cupsdHoldJob(job);
+
+        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
       }
     }
     else if (attr->value_tag == IPP_TAG_DELETEATTR)
@@ -8474,6 +8478,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
          job->attrs->last = job->attrs->prev;
 
         _ippFreeAttr(attr2);
+
+        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
       }
     }
     else
@@ -8483,6 +8489,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
       */
 
       copy_attribute(job->attrs, attr, 0);
+
+      event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
     }
   }
 
@@ -8492,6 +8500,19 @@ set_job_attrs(cupsd_client_t  *con,      /* I - Client connection */
 
   cupsdSaveJob(job);
 
+ /*
+  * Send events as needed...
+  */
+
+  if (event & CUPSD_EVENT_JOB_STATE)
+    cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
+                  job->state_value == IPP_JOB_HELD ?
+                     "Job held by user." : "Job restarted by user.");
+
+  if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
+    cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
+                  "Job options changed by user.");
+
  /*
   * Start jobs if possible...
   */
@@ -8658,7 +8679,7 @@ set_printer_defaults(
     }
     else if (!strcmp(attr->name, "document-format-default") ||
              !strcmp(attr->name, "notify-lease-duration-default") ||
-             !strcmp(attr->name, "notify-notify-events-default"))
+             !strcmp(attr->name, "notify-events-default"))
       continue;
 
    /*
@@ -8814,19 +8835,11 @@ start_printer(cupsd_client_t  *con,     /* I - Client connection */
   cupsdStartPrinter(printer, 1);
 
   if (dtype & CUPS_PRINTER_CLASS)
-  {
     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", name,
                     get_username(con));
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
-                  "Class \"%s\" started by \"%s\".", name, get_username(con));
-  }
   else
-  {
     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", name,
                     get_username(con));
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
-                  "Printer \"%s\" started by \"%s\".", name, get_username(con));
-  }
 
   cupsdCheckJobs();
 
@@ -8910,19 +8923,11 @@ stop_printer(cupsd_client_t  *con,      /* I - Client connection */
   cupsdStopPrinter(printer, 1);
 
   if (dtype & CUPS_PRINTER_CLASS)
-  {
     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", name,
                     get_username(con));
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
-                  "Class \"%s\" stopped by \"%s\".", name, get_username(con));
-  }
   else
-  {
     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", name,
                     get_username(con));
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
-                  "Printer \"%s\" stopped by \"%s\".", name, get_username(con));
-  }
 
  /*
   * Everything was ok, so return OK status...
@@ -8932,6 +8937,70 @@ stop_printer(cupsd_client_t  *con,       /* I - Client connection */
 }
 
 
+/*
+ * 'url_encode_attr()' - URL-encode a string attribute.
+ */
+
+static void
+url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
+                char            *buffer,/* I - String buffer */
+               int             bufsize)/* I - Size of buffer */
+{
+  int  i;                              /* Looping var */
+  char *bufptr,                        /* Pointer into buffer */
+       *bufend,                        /* End of buffer */
+       *valptr;                        /* Pointer into value */
+
+
+  strlcpy(buffer, attr->name, bufsize);
+  bufptr = buffer + strlen(buffer);
+  bufend = buffer + bufsize - 1;
+
+  for (i = 0; i < attr->num_values; i ++)
+  {
+    if (bufptr >= bufend)
+      break;
+
+    if (i)
+      *bufptr++ = ',';
+    else
+      *bufptr++ = '=';
+
+    if (bufptr >= bufend)
+      break;
+
+    *bufptr++ = '\'';
+
+    for (valptr = attr->values[i].string.text;
+         *valptr && bufptr < bufend;
+        valptr ++)
+      if (*valptr == ' ')
+      {
+        if (bufptr >= (bufend - 2))
+         break;
+
+        *bufptr++ = '%';
+       *bufptr++ = '2';
+       *bufptr++ = '0';
+      }
+      else if (*valptr == '\'' || *valptr == '\\')
+      {
+        *bufptr++ = '\\';
+        *bufptr++ = *valptr;
+      }
+      else
+        *bufptr++ = *valptr;
+
+    if (bufptr >= bufend)
+      break;
+
+    *bufptr++ = '\'';
+  }
+
+  *bufptr = '\0';
+}
+
+
 /*
  * 'user_allowed()' - See if a user is allowed to print to a queue.
  */
@@ -9156,5 +9225,5 @@ validate_user(cupsd_job_t    *job,        /* I - Job */
 
 
 /*
- * End of "$Id: ipp.c 5383 2006-04-07 15:36:10Z mike $".
+ * End of "$Id: ipp.c 5970 2006-09-19 20:11:08Z mike $".
  */