1 /*
2  * Printer class routines for the CUPS scheduler.
3  *
4  * Copyright 2007-2017 by Apple Inc.
5  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
8  */
9 
10 /*
11  * Include necessary headers...
12  */
13 
14 #include "cupsd.h"
15 
16 
17 /*
18  * 'cupsdAddClass()' - Add a class to the system.
19  */
20 
21 cupsd_printer_t *			/* O - New class */
cupsdAddClass(const char * name)22 cupsdAddClass(const char *name)		/* I - Name of class */
23 {
24   cupsd_printer_t	*c;		/* New class */
25   char			uri[1024];	/* Class URI */
26 
27 
28  /*
29   * Add the printer and set the type to "class"...
30   */
31 
32   if ((c = cupsdAddPrinter(name)) != NULL)
33   {
34    /*
35     * Change from a printer to a class...
36     */
37 
38     c->type = CUPS_PRINTER_CLASS;
39 
40     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
41 		     ServerName, RemotePort, "/classes/%s", name);
42     cupsdSetString(&c->uri, uri);
43 
44     cupsdSetString(&c->error_policy, "retry-current-job");
45   }
46 
47   return (c);
48 }
49 
50 
51 /*
52  * 'cupsdAddPrinterToClass()' - Add a printer to a class...
53  */
54 
55 void
cupsdAddPrinterToClass(cupsd_printer_t * c,cupsd_printer_t * p)56 cupsdAddPrinterToClass(
57     cupsd_printer_t *c,			/* I - Class to add to */
58     cupsd_printer_t *p)			/* I - Printer to add */
59 {
60   int			i;		/* Looping var */
61   cupsd_printer_t	**temp;		/* Pointer to printer array */
62 
63 
64  /*
65   * See if this printer is already a member of the class...
66   */
67 
68   for (i = 0; i < c->num_printers; i ++)
69     if (c->printers[i] == p)
70       return;
71 
72  /*
73   * Allocate memory as needed...
74   */
75 
76   if (c->num_printers == 0)
77     temp = malloc(sizeof(cupsd_printer_t *));
78   else
79     temp = realloc(c->printers, sizeof(cupsd_printer_t *) * (size_t)(c->num_printers + 1));
80 
81   if (temp == NULL)
82   {
83     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to add printer %s to class %s!",
84                     p->name, c->name);
85     return;
86   }
87 
88  /*
89   * Add the printer to the end of the array and update the number of printers.
90   */
91 
92   c->printers = temp;
93   temp        += c->num_printers;
94   c->num_printers ++;
95 
96   *temp = p;
97 }
98 
99 
100 /*
101  * 'cupsdDeletePrinterFromClass()' - Delete a printer from a class.
102  */
103 
104 int					/* O - 1 if class changed, 0 otherwise */
cupsdDeletePrinterFromClass(cupsd_printer_t * c,cupsd_printer_t * p)105 cupsdDeletePrinterFromClass(
106     cupsd_printer_t *c,			/* I - Class to delete from */
107     cupsd_printer_t *p)			/* I - Printer to delete */
108 {
109   int	i;				/* Looping var */
110 
111 
112  /*
113   * See if the printer is in the class...
114   */
115 
116   for (i = 0; i < c->num_printers; i ++)
117     if (p == c->printers[i])
118       break;
119 
120  /*
121   * If it is, remove it from the list...
122   */
123 
124   if (i < c->num_printers)
125   {
126    /*
127     * Yes, remove the printer...
128     */
129 
130     c->num_printers --;
131     if (i < c->num_printers)
132       memmove(c->printers + i, c->printers + i + 1,
133               (size_t)(c->num_printers - i) * sizeof(cupsd_printer_t *));
134   }
135   else
136     return (0);
137 
138  /*
139   * Update the IPP attributes (have to do this for member-names)...
140   */
141 
142   cupsdSetPrinterAttrs(c);
143 
144   return (1);
145 }
146 
147 
148 /*
149  * 'cupsdDeletePrinterFromClasses()' - Delete a printer from all classes.
150  */
151 
152 int					/* O - 1 if class changed, 0 otherwise */
cupsdDeletePrinterFromClasses(cupsd_printer_t * p)153 cupsdDeletePrinterFromClasses(
154     cupsd_printer_t *p)			/* I - Printer to delete */
155 {
156   int			changed = 0;	/* Any class changed? */
157   cupsd_printer_t	*c;		/* Pointer to current class */
158 
159 
160  /*
161   * Loop through the printer/class list and remove the printer
162   * from each class listed...
163   */
164 
165   for (c = (cupsd_printer_t *)cupsArrayFirst(Printers);
166        c;
167        c = (cupsd_printer_t *)cupsArrayNext(Printers))
168     if (c->type & CUPS_PRINTER_CLASS)
169       changed |= cupsdDeletePrinterFromClass(c, p);
170 
171   return (changed);
172 }
173 
174 
175 /*
176  * 'cupsdFindAvailablePrinter()' - Find an available printer in a class.
177  */
178 
179 cupsd_printer_t *			/* O - Available printer or NULL */
cupsdFindAvailablePrinter(const char * name)180 cupsdFindAvailablePrinter(
181     const char *name)			/* I - Class to check */
182 {
183   int			i;		/* Looping var */
184   cupsd_printer_t	*c;		/* Printer class */
185 
186 
187  /*
188   * Find the class...
189   */
190 
191   if ((c = cupsdFindClass(name)) == NULL)
192   {
193     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to find class \"%s\"!", name);
194     return (NULL);
195   }
196 
197   if (c->num_printers == 0)
198     return (NULL);
199 
200  /*
201   * Make sure that the last printer is also a valid index into the printer
202   * array.  If not, reset the last printer to 0...
203   */
204 
205   if (c->last_printer >= c->num_printers)
206     c->last_printer = 0;
207 
208  /*
209   * Loop through the printers in the class and return the first idle
210   * printer...  We keep track of the last printer that we used so that
211   * a "round robin" type of scheduling is realized (otherwise the first
212   * server might be saturated with print jobs...)
213   *
214   * Thanks to Joel Fredrikson for helping us get this right!
215   */
216 
217   for (i = c->last_printer + 1; ; i ++)
218   {
219     if (i >= c->num_printers)
220       i = 0;
221 
222     if (c->printers[i]->accepting &&
223         (c->printers[i]->state == IPP_PRINTER_IDLE ||
224          ((c->printers[i]->type & CUPS_PRINTER_REMOTE) && !c->printers[i]->job)))
225     {
226       c->last_printer = i;
227       return (c->printers[i]);
228     }
229 
230     if (i == c->last_printer)
231       break;
232   }
233 
234   return (NULL);
235 }
236 
237 
238 /*
239  * 'cupsdFindClass()' - Find the named class.
240  */
241 
242 cupsd_printer_t *			/* O - Matching class or NULL */
cupsdFindClass(const char * name)243 cupsdFindClass(const char *name)	/* I - Name of class */
244 {
245   cupsd_printer_t	*c;		/* Current class/printer */
246 
247 
248   if ((c = cupsdFindDest(name)) != NULL && (c->type & CUPS_PRINTER_CLASS))
249     return (c);
250   else
251     return (NULL);
252 }
253 
254 
255 /*
256  * 'cupsdLoadAllClasses()' - Load classes from the classes.conf file.
257  */
258 
259 void
cupsdLoadAllClasses(void)260 cupsdLoadAllClasses(void)
261 {
262   int			i;		/* Looping var */
263   cups_file_t		*fp;		/* classes.conf file */
264   int			linenum;	/* Current line number */
265   char			line[4096],	/* Line from file */
266 			*value,		/* Pointer to value */
267 			*valueptr;	/* Pointer into value */
268   cupsd_printer_t	*p,		/* Current printer class */
269 			*temp;		/* Temporary pointer to printer */
270 
271 
272  /*
273   * Open the classes.conf file...
274   */
275 
276   snprintf(line, sizeof(line), "%s/classes.conf", ServerRoot);
277   if ((fp = cupsdOpenConfFile(line)) == NULL)
278     return;
279 
280  /*
281   * Read class configurations until we hit EOF...
282   */
283 
284   linenum = 0;
285   p       = NULL;
286 
287   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
288   {
289    /*
290     * Decode the directive...
291     */
292 
293     if (!_cups_strcasecmp(line, "<Class") ||
294         !_cups_strcasecmp(line, "<DefaultClass"))
295     {
296      /*
297       * <Class name> or <DefaultClass name>
298       */
299 
300       if (p == NULL && value)
301       {
302         cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value);
303 
304        /*
305         * Since prior classes may have implicitly defined this class,
306 	* see if it already exists...
307 	*/
308 
309         if ((p = cupsdFindDest(value)) != NULL)
310 	{
311 	  p->type = CUPS_PRINTER_CLASS;
312 	  cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName,
313 	                  LocalPort, value);
314 	  cupsdSetString(&p->error_policy, "retry-job");
315 	}
316 	else
317           p = cupsdAddClass(value);
318 
319 	p->accepting = 1;
320 	p->state     = IPP_PRINTER_IDLE;
321 
322         if (!_cups_strcasecmp(line, "<DefaultClass"))
323 	  DefaultPrinter = p;
324       }
325       else
326         cupsdLogMessage(CUPSD_LOG_ERROR,
327 	                "Syntax error on line %d of classes.conf.", linenum);
328     }
329     else if (!_cups_strcasecmp(line, "</Class>") || !_cups_strcasecmp(line, "</DefaultClass>"))
330     {
331       if (p != NULL)
332       {
333         cupsdSetPrinterAttrs(p);
334         p = NULL;
335       }
336       else
337         cupsdLogMessage(CUPSD_LOG_ERROR,
338 	                "Syntax error on line %d of classes.conf.", linenum);
339     }
340     else if (!p)
341     {
342       cupsdLogMessage(CUPSD_LOG_ERROR,
343                       "Syntax error on line %d of classes.conf.", linenum);
344     }
345     else if (!_cups_strcasecmp(line, "PrinterId"))
346     {
347       if (value && (i = atoi(value)) > 0)
348         p->printer_id = i;
349       else
350         cupsdLogMessage(CUPSD_LOG_ERROR, "Bad PrinterId on line %d of classes.conf.", linenum);
351     }
352     else if (!_cups_strcasecmp(line, "UUID"))
353     {
354       if (value && !strncmp(value, "urn:uuid:", 9))
355         cupsdSetString(&(p->uuid), value);
356       else
357         cupsdLogMessage(CUPSD_LOG_ERROR,
358 	                "Bad UUID on line %d of classes.conf.", linenum);
359     }
360     else if (!_cups_strcasecmp(line, "AuthInfoRequired"))
361     {
362       if (!cupsdSetAuthInfoRequired(p, value, NULL))
363 	cupsdLogMessage(CUPSD_LOG_ERROR,
364 			"Bad AuthInfoRequired on line %d of classes.conf.",
365 			linenum);
366     }
367     else if (!_cups_strcasecmp(line, "Info"))
368     {
369       if (value)
370         cupsdSetString(&p->info, value);
371     }
372     else if (!_cups_strcasecmp(line, "Location"))
373     {
374       if (value)
375         cupsdSetString(&p->location, value);
376     }
377     else if (!_cups_strcasecmp(line, "Option") && value)
378     {
379      /*
380       * Option name value
381       */
382 
383       for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
384 
385       if (!*valueptr)
386         cupsdLogMessage(CUPSD_LOG_ERROR,
387 	                "Syntax error on line %d of classes.conf.", linenum);
388       else
389       {
390         for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
391 
392         p->num_options = cupsAddOption(value, valueptr, p->num_options,
393 	                               &(p->options));
394       }
395     }
396     else if (!_cups_strcasecmp(line, "Printer"))
397     {
398       if (!value)
399       {
400 	cupsdLogMessage(CUPSD_LOG_ERROR,
401 	                "Syntax error on line %d of classes.conf.", linenum);
402         continue;
403       }
404       else if ((temp = cupsdFindPrinter(value)) == NULL)
405       {
406 	cupsdLogMessage(CUPSD_LOG_WARN,
407 	                "Unknown printer %s on line %d of classes.conf.",
408 	                value, linenum);
409 
410        /*
411 	* Add the missing remote printer...
412 	*/
413 
414 	if ((temp = cupsdAddPrinter(value)) != NULL)
415 	{
416 	  cupsdSetString(&temp->make_model, "Remote Printer on unknown");
417 
418           temp->state = IPP_PRINTER_STOPPED;
419 	  temp->type  |= CUPS_PRINTER_REMOTE;
420 
421 	  cupsdSetString(&temp->location, "Location Unknown");
422 	  cupsdSetString(&temp->info, "No Information Available");
423 	  temp->hostname[0] = '\0';
424 
425 	  cupsdSetPrinterAttrs(temp);
426 	}
427       }
428 
429       if (temp)
430         cupsdAddPrinterToClass(p, temp);
431     }
432     else if (!_cups_strcasecmp(line, "State"))
433     {
434      /*
435       * Set the initial queue state...
436       */
437 
438       if (!_cups_strcasecmp(value, "idle"))
439         p->state = IPP_PRINTER_IDLE;
440       else if (!_cups_strcasecmp(value, "stopped"))
441       {
442         p->state = IPP_PRINTER_STOPPED;
443 
444         for (i = 0 ; i < p->num_reasons; i ++)
445 	  if (!strcmp("paused", p->reasons[i]))
446 	    break;
447 
448         if (i >= p->num_reasons &&
449 	    p->num_reasons < (int)(sizeof(p->reasons) / sizeof(p->reasons[0])))
450 	{
451 	  p->reasons[p->num_reasons] = _cupsStrAlloc("paused");
452 	  p->num_reasons ++;
453 	}
454       }
455       else
456 	cupsdLogMessage(CUPSD_LOG_ERROR,
457 	                "Syntax error on line %d of classes.conf.",
458 	                linenum);
459     }
460     else if (!_cups_strcasecmp(line, "StateMessage"))
461     {
462      /*
463       * Set the initial queue state message...
464       */
465 
466       if (value)
467 	strlcpy(p->state_message, value, sizeof(p->state_message));
468     }
469     else if (!_cups_strcasecmp(line, "StateTime"))
470     {
471      /*
472       * Set the state time...
473       */
474 
475       if (value)
476         p->state_time = atoi(value);
477     }
478     else if (!_cups_strcasecmp(line, "Accepting"))
479     {
480      /*
481       * Set the initial accepting state...
482       */
483 
484       if (value &&
485           (!_cups_strcasecmp(value, "yes") ||
486            !_cups_strcasecmp(value, "on") ||
487            !_cups_strcasecmp(value, "true")))
488         p->accepting = 1;
489       else if (value &&
490                (!_cups_strcasecmp(value, "no") ||
491         	!_cups_strcasecmp(value, "off") ||
492         	!_cups_strcasecmp(value, "false")))
493         p->accepting = 0;
494       else
495 	cupsdLogMessage(CUPSD_LOG_ERROR,
496 	                "Syntax error on line %d of classes.conf.",
497 	                linenum);
498     }
499     else if (!_cups_strcasecmp(line, "Shared"))
500     {
501      /*
502       * Set the initial shared state...
503       */
504 
505       if (value &&
506           (!_cups_strcasecmp(value, "yes") ||
507            !_cups_strcasecmp(value, "on") ||
508            !_cups_strcasecmp(value, "true")))
509         p->shared = 1;
510       else if (value &&
511                (!_cups_strcasecmp(value, "no") ||
512         	!_cups_strcasecmp(value, "off") ||
513         	!_cups_strcasecmp(value, "false")))
514         p->shared = 0;
515       else
516 	cupsdLogMessage(CUPSD_LOG_ERROR,
517 	                "Syntax error on line %d of classes.conf.",
518 	                linenum);
519     }
520     else if (!_cups_strcasecmp(line, "JobSheets"))
521     {
522      /*
523       * Set the initial job sheets...
524       */
525 
526       if (value)
527       {
528 	for (valueptr = value;
529 	     *valueptr && !isspace(*valueptr & 255);
530 	     valueptr ++);
531 
532 	if (*valueptr)
533           *valueptr++ = '\0';
534 
535 	cupsdSetString(&p->job_sheets[0], value);
536 
537 	while (isspace(*valueptr & 255))
538           valueptr ++;
539 
540 	if (*valueptr)
541 	{
542           for (value = valueptr;
543 	       *valueptr && !isspace(*valueptr & 255);
544 	       valueptr ++);
545 
546 	  if (*valueptr)
547             *valueptr = '\0';
548 
549 	  cupsdSetString(&p->job_sheets[1], value);
550 	}
551       }
552       else
553 	cupsdLogMessage(CUPSD_LOG_ERROR,
554 	                "Syntax error on line %d of classes.conf.", linenum);
555     }
556     else if (!_cups_strcasecmp(line, "AllowUser"))
557     {
558       if (value)
559       {
560         p->deny_users = 0;
561         cupsdAddString(&(p->users), value);
562       }
563       else
564 	cupsdLogMessage(CUPSD_LOG_ERROR,
565 	                "Syntax error on line %d of classes.conf.", linenum);
566     }
567     else if (!_cups_strcasecmp(line, "DenyUser"))
568     {
569       if (value)
570       {
571         p->deny_users = 1;
572         cupsdAddString(&(p->users), value);
573       }
574       else
575 	cupsdLogMessage(CUPSD_LOG_ERROR,
576 	                "Syntax error on line %d of classes.conf.", linenum);
577     }
578     else if (!_cups_strcasecmp(line, "QuotaPeriod"))
579     {
580       if (value)
581         p->quota_period = atoi(value);
582       else
583 	cupsdLogMessage(CUPSD_LOG_ERROR,
584 	                "Syntax error on line %d of classes.conf.", linenum);
585     }
586     else if (!_cups_strcasecmp(line, "PageLimit"))
587     {
588       if (value)
589         p->page_limit = atoi(value);
590       else
591 	cupsdLogMessage(CUPSD_LOG_ERROR,
592 	                "Syntax error on line %d of classes.conf.", linenum);
593     }
594     else if (!_cups_strcasecmp(line, "KLimit"))
595     {
596       if (value)
597         p->k_limit = atoi(value);
598       else
599 	cupsdLogMessage(CUPSD_LOG_ERROR,
600 	                "Syntax error on line %d of classes.conf.", linenum);
601     }
602     else if (!_cups_strcasecmp(line, "OpPolicy"))
603     {
604       if (value)
605       {
606         cupsd_policy_t *pol;		/* Policy */
607 
608 
609         if ((pol = cupsdFindPolicy(value)) != NULL)
610 	{
611           cupsdSetString(&p->op_policy, value);
612 	  p->op_policy_ptr = pol;
613 	}
614 	else
615 	  cupsdLogMessage(CUPSD_LOG_ERROR,
616 	                  "Bad policy \"%s\" on line %d of classes.conf",
617 			  value, linenum);
618       }
619       else
620 	cupsdLogMessage(CUPSD_LOG_ERROR,
621 	                "Syntax error on line %d of classes.conf.", linenum);
622     }
623     else if (!_cups_strcasecmp(line, "ErrorPolicy"))
624     {
625       if (value)
626       {
627         if (strcmp(value, "retry-current-job") && strcmp(value, "retry-job"))
628 	  cupsdLogMessage(CUPSD_LOG_WARN,
629 	                  "ErrorPolicy %s ignored on line %d of classes.conf",
630 			  value, linenum);
631       }
632       else
633 	cupsdLogMessage(CUPSD_LOG_ERROR,
634 	                "Syntax error on line %d of classes.conf.", linenum);
635     }
636     else
637     {
638      /*
639       * Something else we don't understand...
640       */
641 
642       cupsdLogMessage(CUPSD_LOG_ERROR,
643                       "Unknown configuration directive %s on line %d of classes.conf.",
644 	              line, linenum);
645     }
646   }
647 
648   cupsFileClose(fp);
649 }
650 
651 
652 /*
653  * 'cupsdSaveAllClasses()' - Save classes to the classes.conf file.
654  */
655 
656 void
cupsdSaveAllClasses(void)657 cupsdSaveAllClasses(void)
658 {
659   cups_file_t		*fp;		/* classes.conf file */
660   char			filename[1024],	/* classes.conf filename */
661 			temp[1024],	/* Temporary string */
662 			value[2048],	/* Value string */
663 			*name;		/* Current user name */
664   cupsd_printer_t	*pclass;	/* Current printer class */
665   int			i;		/* Looping var */
666   time_t		curtime;	/* Current time */
667   struct tm		curdate;	/* Current date */
668   cups_option_t		*option;	/* Current option */
669 
670 
671  /*
672   * Create the classes.conf file...
673   */
674 
675   snprintf(filename, sizeof(filename), "%s/classes.conf", ServerRoot);
676 
677   if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
678     return;
679 
680   cupsdLogMessage(CUPSD_LOG_INFO, "Saving classes.conf...");
681 
682  /*
683   * Write a small header to the file...
684   */
685 
686   time(&curtime);
687   localtime_r(&curtime, &curdate);
688   strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", &curdate);
689 
690   cupsFilePuts(fp, "# Class configuration file for " CUPS_SVERSION "\n");
691   cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
692   cupsFilePuts(fp, "# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING\n");
693 
694  /*
695   * Write each local class known to the system...
696   */
697 
698   for (pclass = (cupsd_printer_t *)cupsArrayFirst(Printers);
699        pclass;
700        pclass = (cupsd_printer_t *)cupsArrayNext(Printers))
701   {
702    /*
703     * Skip remote destinations and regular printers...
704     */
705 
706     if ((pclass->type & CUPS_PRINTER_REMOTE) ||
707         !(pclass->type & CUPS_PRINTER_CLASS))
708       continue;
709 
710    /*
711     * Write printers as needed...
712     */
713 
714     if (pclass == DefaultPrinter)
715       cupsFilePrintf(fp, "<DefaultClass %s>\n", pclass->name);
716     else
717       cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
718 
719     if (pclass->printer_id)
720       cupsFilePrintf(fp, "PrinterId %d\n", pclass->printer_id);
721 
722     cupsFilePrintf(fp, "UUID %s\n", pclass->uuid);
723 
724     if (pclass->num_auth_info_required > 0)
725     {
726       switch (pclass->num_auth_info_required)
727       {
728         case 1 :
729             strlcpy(value, pclass->auth_info_required[0], sizeof(value));
730 	    break;
731 
732         case 2 :
733             snprintf(value, sizeof(value), "%s,%s",
734 	             pclass->auth_info_required[0],
735 		     pclass->auth_info_required[1]);
736 	    break;
737 
738         case 3 :
739 	default :
740             snprintf(value, sizeof(value), "%s,%s,%s",
741 	             pclass->auth_info_required[0],
742 		     pclass->auth_info_required[1],
743 		     pclass->auth_info_required[2]);
744 	    break;
745       }
746 
747       cupsFilePutConf(fp, "AuthInfoRequired", value);
748     }
749 
750     if (pclass->info)
751       cupsFilePutConf(fp, "Info", pclass->info);
752 
753     if (pclass->location)
754       cupsFilePutConf(fp, "Location", pclass->location);
755 
756     if (pclass->state == IPP_PRINTER_STOPPED)
757       cupsFilePuts(fp, "State Stopped\n");
758     else
759       cupsFilePuts(fp, "State Idle\n");
760 
761     cupsFilePrintf(fp, "StateTime %d\n", (int)pclass->state_time);
762 
763     if (pclass->accepting)
764       cupsFilePuts(fp, "Accepting Yes\n");
765     else
766       cupsFilePuts(fp, "Accepting No\n");
767 
768     if (pclass->shared)
769       cupsFilePuts(fp, "Shared Yes\n");
770     else
771       cupsFilePuts(fp, "Shared No\n");
772 
773     snprintf(value, sizeof(value), "%s %s", pclass->job_sheets[0],
774              pclass->job_sheets[1]);
775     cupsFilePutConf(fp, "JobSheets", value);
776 
777     for (i = 0; i < pclass->num_printers; i ++)
778       cupsFilePrintf(fp, "Printer %s\n", pclass->printers[i]->name);
779 
780     cupsFilePrintf(fp, "QuotaPeriod %d\n", pclass->quota_period);
781     cupsFilePrintf(fp, "PageLimit %d\n", pclass->page_limit);
782     cupsFilePrintf(fp, "KLimit %d\n", pclass->k_limit);
783 
784     for (name = (char *)cupsArrayFirst(pclass->users);
785          name;
786 	 name = (char *)cupsArrayNext(pclass->users))
787       cupsFilePutConf(fp, pclass->deny_users ? "DenyUser" : "AllowUser", name);
788 
789      if (pclass->op_policy)
790       cupsFilePutConf(fp, "OpPolicy", pclass->op_policy);
791     if (pclass->error_policy)
792       cupsFilePutConf(fp, "ErrorPolicy", pclass->error_policy);
793 
794     for (i = pclass->num_options, option = pclass->options;
795          i > 0;
796 	 i --, option ++)
797     {
798       snprintf(value, sizeof(value), "%s %s", option->name, option->value);
799       cupsFilePutConf(fp, "Option", value);
800     }
801 
802     if (pclass == DefaultPrinter)
803       cupsFilePuts(fp, "</DefaultClass>\n");
804     else
805       cupsFilePuts(fp, "</Class>\n");
806   }
807 
808   cupsdCloseCreatedConfFile(fp, filename);
809 }
810