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