]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/printers.c
Merge changes from CUPS 1.4svn-r7614.
[thirdparty/cups.git] / scheduler / printers.c
index 3571454f5d6e0ea821cb9eca8e24c5e7761b88c8..3a63fc2e668b80f792abfb5989521fec35af40d6 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: printers.c 6970 2007-09-17 23:58:28Z mike $"
+ * "$Id: printers.c 7608 2008-05-21 01:37:21Z mike $"
  *
  *   Printer routines for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 2007 by Apple Inc.
+ *   Copyright 2007-2008 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
@@ -27,6 +27,7 @@
  *   cupsdSaveAllPrinters()      - Save all printer definitions to the
  *                                 printers.conf file.
  *   cupsdSetAuthInfoRequired()  - Set the required authentication info.
+ *   cupsdSetPrinterAttr()       - Set a printer attribute.
  *   cupsdSetPrinterAttrs()      - Set printer attributes based upon the PPD
  *                                 file.
  *   cupsdSetPrinterReasons()    - Set/update the reasons strings.
@@ -909,7 +910,7 @@ cupsdLoadAllPrinters(void)
       {
         cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-        return;
+       break;
       }
     }
     else if (!strcasecmp(line, "</Printer>"))
@@ -954,14 +955,14 @@ cupsdLoadAllPrinters(void)
       {
         cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-        return;
+       break;
       }
     }
     else if (!p)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "Syntax error on line %d of printers.conf.", linenum);
-      return;
+      break;
     }
     else if (!strcasecmp(line, "AuthInfoRequired"))
     {
@@ -988,7 +989,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "Option") && value)
@@ -1020,7 +1021,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "State"))
@@ -1037,7 +1038,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "StateMessage"))
@@ -1078,7 +1079,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "Shared"))
@@ -1101,7 +1102,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "JobSheets"))
@@ -1136,7 +1137,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "AllowUser"))
@@ -1150,7 +1151,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "DenyUser"))
@@ -1164,7 +1165,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "QuotaPeriod"))
@@ -1175,7 +1176,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "PageLimit"))
@@ -1186,7 +1187,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "KLimit"))
@@ -1197,7 +1198,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "OpPolicy"))
@@ -1221,7 +1222,7 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
       }
     }
     else if (!strcasecmp(line, "ErrorPolicy"))
@@ -1232,7 +1233,27 @@ cupsdLoadAllPrinters(void)
       {
        cupsdLogMessage(CUPSD_LOG_ERROR,
                        "Syntax error on line %d of printers.conf.", linenum);
-       return;
+       break;
+      }
+    }
+    else if (!strcasecmp(line, "Attribute") && value)
+    {
+      for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
+
+      if (!*valueptr)
+        cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Syntax error on line %d of printers.conf.", linenum);
+      else
+      {
+        for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
+
+        if (!p->attrs)
+         cupsdSetPrinterAttrs(p);
+
+        cupsdSetPrinterAttr(p, value, valueptr);
+
+       if (!strncmp(value, "marker-", 7))
+         p->marker_time = time(NULL);
       }
     }
     else
@@ -1319,6 +1340,7 @@ cupsdSaveAllPrinters(void)
   struct tm            *curdate;       /* Current date */
   cups_option_t                *option;        /* Current option */
   const char           *ptr;           /* Pointer into info/location */
+  ipp_attribute_t      *marker;        /* Current marker attribute */
 
 
  /*
@@ -1469,8 +1491,24 @@ cupsdSaveAllPrinters(void)
     cupsFilePrintf(fp, "KLimit %d\n", printer->k_limit);
 
     for (i = 0; i < printer->num_users; i ++)
-      cupsFilePrintf(fp, "%sUser %s\n", printer->deny_users ? "Deny" : "Allow",
-              printer->users[i]);
+    {
+      if ((ptr = strchr(printer->users[i], '#')) != NULL)
+      {
+       /*
+        * Need to quote the first # in the user string...
+       */
+
+        cupsFilePrintf(fp, "%sUser ", printer->deny_users ? "Deny" : "Allow");
+       cupsFileWrite(fp, printer->users[i], ptr - printer->users[i]);
+       cupsFilePutChar(fp, '\\');
+       cupsFilePuts(fp, ptr);
+       cupsFilePutChar(fp, '\n');
+      }
+      else
+        cupsFilePrintf(fp, "%sUser %s\n",
+                      printer->deny_users ? "Deny" : "Allow",
+                       printer->users[i]);
+    }
 
     if (printer->op_policy)
       cupsFilePrintf(fp, "OpPolicy %s\n", printer->op_policy);
@@ -1480,7 +1518,104 @@ cupsdSaveAllPrinters(void)
     for (i = printer->num_options, option = printer->options;
          i > 0;
         i --, option ++)
-      cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
+    {
+      if ((ptr = strchr(option->value, '#')) != NULL)
+      {
+       /*
+        * Need to quote the first # in the option string...
+       */
+
+        cupsFilePrintf(fp, "Option %s ", option->name);
+       cupsFileWrite(fp, option->value, ptr - option->value);
+       cupsFilePutChar(fp, '\\');
+       cupsFilePuts(fp, ptr);
+       cupsFilePutChar(fp, '\n');
+      }
+      else
+        cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-colors",
+                                   IPP_TAG_NAME)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s ", marker->name);
+
+      for (i = 0, ptr = NULL; i < marker->num_values; i ++)
+      {
+        if (i)
+         cupsFilePutChar(fp, ',');
+
+        if (!ptr && (ptr = strchr(marker->values[i].string.text, '#')) != NULL)
+       {
+         cupsFileWrite(fp, marker->values[i].string.text,
+                       ptr - marker->values[i].string.text);
+         cupsFilePutChar(fp, '\\');
+         cupsFilePuts(fp, ptr);
+       }
+       else
+          cupsFilePuts(fp, marker->values[i].string.text);
+      }
+
+      cupsFilePuts(fp, "\n");
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-levels",
+                                   IPP_TAG_INTEGER)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s %d", marker->name,
+                     marker->values[0].integer);
+      for (i = 1; i < marker->num_values; i ++)
+        cupsFilePrintf(fp, ",%d", marker->values[i].integer);
+      cupsFilePuts(fp, "\n");
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-names",
+                                   IPP_TAG_NAME)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s ", marker->name);
+
+      for (i = 0, ptr = NULL; i < marker->num_values; i ++)
+      {
+        if (i)
+         cupsFilePutChar(fp, ',');
+
+        if (!ptr && (ptr = strchr(marker->values[i].string.text, '#')) != NULL)
+       {
+         cupsFileWrite(fp, marker->values[i].string.text,
+                       ptr - marker->values[i].string.text);
+         cupsFilePutChar(fp, '\\');
+         cupsFilePuts(fp, ptr);
+       }
+       else
+          cupsFilePuts(fp, marker->values[i].string.text);
+      }
+
+      cupsFilePuts(fp, "\n");
+    }
+
+    if ((marker = ippFindAttribute(printer->attrs, "marker-types",
+                                   IPP_TAG_KEYWORD)) != NULL)
+    {
+      cupsFilePrintf(fp, "Attribute %s ", marker->name);
+
+      for (i = 0, ptr = NULL; i < marker->num_values; i ++)
+      {
+        if (i)
+         cupsFilePutChar(fp, ',');
+
+        if (!ptr && (ptr = strchr(marker->values[i].string.text, '#')) != NULL)
+       {
+         cupsFileWrite(fp, marker->values[i].string.text,
+                       ptr - marker->values[i].string.text);
+         cupsFilePutChar(fp, '\\');
+         cupsFilePuts(fp, ptr);
+       }
+       else
+          cupsFilePuts(fp, marker->values[i].string.text);
+      }
+
+      cupsFilePuts(fp, "\n");
+    }
 
     cupsFilePuts(fp, "</Printer>\n");
 
@@ -1650,6 +1785,122 @@ cupsdSetAuthInfoRequired(
 }
 
 
+/*
+ * 'cupsdSetPrinterAttr()' - Set a printer attribute.
+ */
+
+void
+cupsdSetPrinterAttr(
+    cupsd_printer_t *p,                        /* I - Printer */
+    const char      *name,             /* I - Attribute name */
+    char            *value)            /* I - Attribute value string */
+{
+  ipp_attribute_t      *attr;          /* Attribute */
+  int                  i,              /* Looping var */
+                       count;          /* Number of values */
+  char                 *ptr;           /* Pointer into value */
+  ipp_tag_t            value_tag;      /* Value tag for this attribute */
+
+
+ /*
+  * Count the number of values...
+  */
+
+  for (count = 1, ptr = value;
+       (ptr = strchr(ptr, ',')) != NULL;
+       ptr ++, count ++);
+
+ /*
+  * Then add or update the attribute as needed...
+  */
+
+  if (!strcmp(name, "marker-levels"))
+  {
+   /*
+    * Integer values...
+    */
+
+    if ((attr = ippFindAttribute(p->attrs, name, IPP_TAG_INTEGER)) != NULL &&
+        attr->num_values < count)
+    {
+      ippDeleteAttribute(p->attrs, attr);
+      attr = NULL;
+    }
+
+    if (attr)
+      attr->num_values = count;
+    else
+      attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER, name,
+                            count, NULL);
+
+    if (!attr)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to allocate memory for printer attribute "
+                     "(%d values)", count);
+      return;
+    }
+
+    for (i = 0; i < count; i ++)
+    {
+      if ((ptr = strchr(value, ',')) != NULL)
+        *ptr++ = '\0';
+
+      attr->values[i].integer = strtol(value, NULL, 10);
+
+      if (ptr)
+        value = ptr;
+    }
+  }
+  else
+  {
+   /*
+    * Name or keyword values...
+    */
+
+    if (!strcmp(name, "marker-types"))
+      value_tag = IPP_TAG_KEYWORD;
+    else
+      value_tag = IPP_TAG_NAME;
+
+    if ((attr = ippFindAttribute(p->attrs, name, value_tag)) != NULL &&
+        attr->num_values < count)
+    {
+      ippDeleteAttribute(p->attrs, attr);
+      attr = NULL;
+    }
+
+    if (attr)
+      attr->num_values = count;
+    else
+      attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, value_tag, name,
+                           count, NULL, NULL);
+
+    if (!attr)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Unable to allocate memory for printer attribute "
+                     "(%d values)", count);
+      return;
+    }
+
+    for (i = 0; i < count; i ++)
+    {
+      if ((ptr = strchr(value, ',')) != NULL)
+        *ptr++ = '\0';
+
+      _cupsStrFree(attr->values[i].string.text);
+      attr->values[i].string.text = _cupsStrAlloc(value);
+
+      if (ptr)
+        value = ptr;
+    }
+  }
+
+  cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
+}
+
+
 /*
  * 'cupsdSetPrinterAttrs()' - Set printer attributes based upon the PPD file.
  */
@@ -1675,6 +1926,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                *media_quality,         /* EFMediaQualityMode options */
                *duplex;                /* Duplex options */
   ppd_attr_t   *ppdattr;               /* PPD attribute */
+  ipp_t                *oldattrs;              /* Old printer attributes */
   ipp_attribute_t *attr;               /* Attribute data */
   ipp_value_t  *val;                   /* Attribute value */
   int          num_finishings;         /* Number of finishings */
@@ -1691,14 +1943,23 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
                  "username",
                  "password"
                };
+#ifdef HAVE_GSSAPI
   static const char * const air_negotiate[] =
                {                       /* Kerberos authentication */
                  "negotiate"
                };
+#endif /* HAVE_GSSAPI */
   static const char * const air_none[] =
                {                       /* No authentication */
                  "none"
                };
+  static const char * const standard_commands[] =
+               {                       /* Standard CUPS commands */
+                 "AutoConfigure",
+                 "Clean",
+                 "PrintSelfTestPage",
+                 "ReportLevels"
+               };
 
 
   DEBUG_printf(("cupsdSetPrinterAttrs: entering name = %s, type = %x\n", p->name,
@@ -1743,25 +2004,25 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       snprintf(resource, sizeof(resource), "/printers/%s", p->name);
 
     if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
-        auth->type == AUTH_NONE)
+        auth->type == CUPSD_AUTH_NONE)
       auth = cupsdFindPolicyOp(p->op_policy_ptr, IPP_PRINT_JOB);
 
     if (auth)
     {
-      if (auth->type == AUTH_BASIC || auth->type == AUTH_BASICDIGEST)
+      if (auth->type == CUPSD_AUTH_BASIC || auth->type == CUPSD_AUTH_BASICDIGEST)
       {
        auth_supported = "basic";
        num_air        = 2;
        air            = air_userpass;
       }
-      else if (auth->type == AUTH_DIGEST)
+      else if (auth->type == CUPSD_AUTH_DIGEST)
       {
        auth_supported = "digest";
        num_air        = 2;
        air            = air_userpass;
       }
 #ifdef HAVE_GSSAPI
-      else if (auth->type == AUTH_NEGOTIATE)
+      else if (auth->type == CUPSD_AUTH_NEGOTIATE)
       {
        auth_supported = "negotiate";
        num_air        = 1;
@@ -1769,7 +2030,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
       }
 #endif /* HAVE_GSSAPI */
 
-      if (auth->type != AUTH_NONE)
+      if (auth->type != CUPSD_AUTH_NONE)
         p->type |= CUPS_PRINTER_AUTHENTICATED;
       else
         p->type &= ~CUPS_PRINTER_AUTHENTICATED;
@@ -1787,9 +2048,7 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
   * Create the required IPP attributes for a printer...
   */
 
-  if (p->attrs)
-    ippDelete(p->attrs);
-
+  oldattrs = p->attrs;
   p->attrs = ippNew();
 
   ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
@@ -2180,11 +2439,119 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
        if (ppd->num_filters == 0)
        {
         /*
-         * If there are no filters, add a PostScript printing filter.
+         * If there are no filters, add PostScript printing filters.
          */
 
+          add_printer_filter(p, p->filetype,
+                            "application/vnd.cups-command 0 commandtops");
           add_printer_filter(p, p->filetype,
                             "application/vnd.cups-postscript 0 -");
+
+          p->type |= CUPS_PRINTER_COMMANDS;
+        }
+       else if (!(p->type & CUPS_PRINTER_COMMANDS))
+       {
+        /*
+         * See if this is a PostScript device without a command filter...
+         */
+
+         for (i = 0; i < ppd->num_filters; i ++)
+           if (!strncasecmp(ppd->filters[i],
+                            "application/vnd.cups-postscript", 31))
+             break;
+
+          if (i < ppd->num_filters)
+         {
+          /*
+           * Add the generic PostScript command filter...
+           */
+
+           add_printer_filter(p, p->filetype,
+                              "application/vnd.cups-command 0 commandtops");
+           p->type |= CUPS_PRINTER_COMMANDS;
+         }
+       }
+
+        if (p->type & CUPS_PRINTER_COMMANDS)
+       {
+         char  *commands,              /* Copy of commands */
+               *start,                 /* Start of name */
+               *end;                   /* End of name */
+          int  count;                  /* Number of commands */
+
+
+          if ((ppdattr = ppdFindAttr(ppd, "cupsCommands", NULL)) != NULL &&
+             ppdattr->value && ppdattr->value[0])
+         {
+           for (count = 0, start = ppdattr->value; *start; count ++)
+           {
+             while (isspace(*start & 255))
+               start ++;
+
+             if (!*start)
+               break;
+
+             while (*start && !isspace(*start & 255))
+               start ++;
+           }
+         }
+         else
+           count = 0;
+
+          if (count > 0)
+         {
+          /*
+           * Make a copy of the commands string and count how many ...
+           */
+
+           attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                                "printer-commands", count, NULL, NULL);
+
+           commands = strdup(ppdattr->value);
+
+           for (count = 0, start = commands; *start; count ++)
+           {
+             while (isspace(*start & 255))
+               start ++;
+
+             if (!*start)
+               break;
+
+              end = start;
+             while (*end && !isspace(*end & 255))
+               end ++;
+
+              if (*end)
+               *end++ = '\0';
+
+              attr->values[count].string.text = _cupsStrAlloc(start);
+
+              start = end;
+           }
+
+           free(commands);
+          }
+         else
+         {
+          /*
+           * Add the standard list of commands...
+           */
+
+           ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                         "printer-commands",
+                         (int)(sizeof(standard_commands) /
+                               sizeof(standard_commands[0])), NULL,
+                         standard_commands);
+         }
+       }
+       else
+       {
+        /*
+         * No commands supported...
+         */
+
+         ippAddString(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                      "printer-commands", NULL, "none");
         }
 
        /*
@@ -2348,6 +2715,69 @@ cupsdSetPrinterAttrs(cupsd_printer_t *p)/* I - Printer to setup */
     }
   }
 
+ /*
+  * Copy marker attributes as needed...
+  */
+
+  if (oldattrs)
+  {
+    ipp_attribute_t *oldattr;          /* Old attribute */
+
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-colors",
+                                    IPP_TAG_NAME)) != NULL)
+    {
+      if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                                "marker-colors", oldattr->num_values, NULL,
+                               NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].string.text =
+             _cupsStrAlloc(oldattr->values[i].string.text);
+      }
+    }
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-levels",
+                                    IPP_TAG_INTEGER)) != NULL)
+    {
+      if ((attr = ippAddIntegers(p->attrs, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                                 "marker-levels", oldattr->num_values,
+                                NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].integer = oldattr->values[i].integer;
+      }
+    }
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-names",
+                                    IPP_TAG_NAME)) != NULL)
+    {
+      if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_NAME,
+                                "marker-names", oldattr->num_values, NULL,
+                               NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].string.text =
+             _cupsStrAlloc(oldattr->values[i].string.text);
+      }
+    }
+
+    if ((oldattr = ippFindAttribute(oldattrs, "marker-types",
+                                    IPP_TAG_KEYWORD)) != NULL)
+    {
+      if ((attr = ippAddStrings(p->attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                                "marker-types", oldattr->num_values, NULL,
+                               NULL)) != NULL)
+      {
+       for (i = 0; i < oldattr->num_values; i ++)
+         attr->values[i].string.text =
+             _cupsStrAlloc(oldattr->values[i].string.text);
+      }
+    }
+
+    ippDelete(oldattrs);
+  }
+
  /*
   * Force sharing off for remote queues...
   */
@@ -2641,7 +3071,7 @@ cupsdSetPrinterState(
   if (old_state != s)
   {
     cupsdAddEvent(s == IPP_PRINTER_STOPPED ? CUPSD_EVENT_PRINTER_STOPPED :
-                      CUPSD_EVENT_PRINTER_STATE_CHANGED, p, NULL,
+                      CUPSD_EVENT_PRINTER_STATE, p, NULL,
                  "%s \"%s\" state changed.",
                  (p->type & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
                  p->name);
@@ -2665,7 +3095,8 @@ cupsdSetPrinterState(
   * Let the browse protocols reflect the change...
   */
 
-  cupsdRegisterPrinter(p);
+  if (update)
+    cupsdRegisterPrinter(p);
 
  /*
   * Save the printer configuration if a printer goes from idle or processing
@@ -2676,9 +3107,9 @@ cupsdSetPrinterState(
       update)
   {
     if (p->type & CUPS_PRINTER_CLASS)
-      cupsdSaveAllClasses();
+      cupsdMarkDirty(CUPSD_DIRTY_CLASSES);
     else
-      cupsdSaveAllPrinters();
+      cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
   }
 }
 
@@ -2724,8 +3155,9 @@ cupsdStopPrinter(cupsd_printer_t *p,      /* I - Printer to stop */
 
     job->state->values[0].integer = IPP_JOB_PENDING;
     job->state_value              = IPP_JOB_PENDING;
+    job->dirty                    = 1;
 
-    cupsdSaveJob(job);
+    cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
     cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
                  "Job stopped due to printer being paused");
@@ -2733,6 +3165,118 @@ cupsdStopPrinter(cupsd_printer_t *p,    /* I - Printer to stop */
 }
 
 
+/*
+ * 'cupsdUpdatePrinterPPD()' - Update keywords in a printer's PPD file.
+ */
+
+int                                    /* O - 1 if successful, 0 otherwise */
+cupsdUpdatePrinterPPD(
+    cupsd_printer_t *p,                        /* I - Printer */
+    int             num_keywords,      /* I - Number of keywords */
+    cups_option_t   *keywords)         /* I - Keywords */
+{
+  int          i;                      /* Looping var */
+  cups_file_t  *src,                   /* Original file */
+               *dst;                   /* New file */
+  char         srcfile[1024],          /* Original filename */
+               dstfile[1024],          /* New filename */
+               line[1024],             /* Line from file */
+               keystring[41];          /* Keyword from line */
+  cups_option_t        *keyword;               /* Current keyword */
+
+
+  cupsdLogMessage(CUPSD_LOG_INFO, "Updating keywords in PPD file for %s...",
+                  p->name);
+
+ /*
+  * Get the old and new PPD filenames...
+  */
+
+  snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd.O", ServerRoot, p->name);
+  snprintf(dstfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
+
+ /*
+  * Rename the old file and open the old and new...
+  */
+
+  if (rename(dstfile, srcfile))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup PPD file for %s: %s",
+                    p->name, strerror(errno));
+    return (0);
+  }
+
+  if ((src = cupsFileOpen(srcfile, "r")) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open PPD file \"%s\": %s",
+                    srcfile, strerror(errno));
+    rename(srcfile, dstfile);
+    return (0);
+  }
+
+  if ((dst = cupsFileOpen(dstfile, "w")) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create PPD file \"%s\": %s",
+                    dstfile, strerror(errno));
+    cupsFileClose(src);
+    rename(srcfile, dstfile);
+    return (0);
+  }
+
+ /*
+  * Copy the first line and then write out all of the keywords...
+  */
+
+  if (!cupsFileGets(src, line, sizeof(line)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to read PPD file \"%s\": %s",
+                    srcfile, strerror(errno));
+    cupsFileClose(src);
+    cupsFileClose(dst);
+    rename(srcfile, dstfile);
+    return (0);
+  }
+
+  cupsFilePrintf(dst, "%s\n", line);
+
+  for (i = num_keywords, keyword = keywords; i > 0; i --, keyword ++)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "*%s: %s", keyword->name, keyword->value);
+    cupsFilePrintf(dst, "*%s: %s\n", keyword->name, keyword->value);
+  }
+
+ /*
+  * Then copy the rest of the PPD file, dropping any keywords we changed.
+  */
+
+  while (cupsFileGets(src, line, sizeof(line)))
+  {
+   /*
+    * Skip keywords we've already set...
+    */
+
+    if (sscanf(line, "*%40[^:]:", keystring) == 1 &&
+        cupsGetOption(keystring, num_keywords, keywords))
+      continue;
+
+   /*
+    * Otherwise write the line...
+    */
+
+    cupsFilePrintf(dst, "%s\n", line);
+  }
+
+ /*
+  * Close files and return...
+  */
+
+  cupsFileClose(src);
+  cupsFileClose(dst);
+
+  return (1);
+}
+
+
 /*
  * 'cupsdUpdatePrinters()' - Update printers after a partial reload.
  */
@@ -3776,5 +4320,5 @@ write_irix_state(cupsd_printer_t *p)      /* I - Printer to update */
 
 
 /*
- * End of "$Id: printers.c 6970 2007-09-17 23:58:28Z mike $".
+ * End of "$Id: printers.c 7608 2008-05-21 01:37:21Z mike $".
  */