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