2 * "$Id: admin.c 4921 2006-01-12 21:26:26Z mike $"
4 * Administration CGI for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 by Easy Software Products.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * main() - Main entry for CGI.
27 * compare_printer_devices() - Compare two printer devices.
28 * do_am_class() - Add or modify a class.
29 * do_am_printer() - Add or modify a printer.
30 * do_config_printer() - Configure the default options for a printer.
31 * do_config_server() - Configure server settings.
32 * do_delete_class() - Delete a class...
33 * do_delete_printer() - Delete a printer...
34 * do_menu() - Show the main menu...
35 * do_printer_op() - Do a printer operation.
36 * do_set_allowed_users() - Set the allowed/denied users for a queue.
37 * do_set_sharing() - Set printer-is-shared value...
38 * match_string() - Return the number of matching characters.
42 * Include necessary headers...
45 #include "cgi-private.h"
46 #include <cups/file.h>
54 static void do_am_class(http_t
*http
, cups_lang_t
*language
, int modify
);
55 static void do_am_printer(http_t
*http
, cups_lang_t
*language
, int modify
);
56 static void do_config_printer(http_t
*http
, cups_lang_t
*language
);
57 static void do_config_server(http_t
*http
, cups_lang_t
*language
);
58 static void do_delete_class(http_t
*http
, cups_lang_t
*language
);
59 static void do_delete_printer(http_t
*http
, cups_lang_t
*language
);
60 static void do_menu(http_t
*http
, cups_lang_t
*language
);
61 static void do_printer_op(http_t
*http
, cups_lang_t
*language
,
62 ipp_op_t op
, const char *title
);
63 static void do_set_allowed_users(http_t
*http
, cups_lang_t
*language
);
64 static void do_set_sharing(http_t
*http
, cups_lang_t
*language
);
65 static int match_string(const char *a
, const char *b
);
69 * 'main()' - Main entry for CGI.
72 int /* O - Exit status */
73 main(int argc
, /* I - Number of command-line arguments */
74 char *argv
[]) /* I - Command-line arguments */
76 cups_lang_t
*language
; /* Language information */
77 http_t
*http
; /* Connection to the server */
78 const char *op
; /* Operation name */
82 * Get the request language...
85 language
= cupsLangDefault();
88 * Connect to the HTTP server...
91 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
94 * Set the web interface section...
97 cgiSetVariable("SECTION", "admin");
100 * See if we have form data...
103 if (!cgiInitialize())
106 * Nope, send the administration menu...
109 do_menu(http
, language
);
111 else if ((op
= cgiGetVariable("OP")) != NULL
)
114 * Do the operation...
117 if (!strcmp(op
, "redirect"))
119 const char *url
; /* Redirection URL... */
122 if ((url
= cgiGetVariable("URL")) != NULL
)
123 printf("Location: %s\n\n", url
);
125 puts("Location: /admin\n");
127 else if (!strcmp(op
, "start-printer"))
128 do_printer_op(http
, language
, IPP_RESUME_PRINTER
, "Start Printer");
129 else if (!strcmp(op
, "stop-printer"))
130 do_printer_op(http
, language
, IPP_PAUSE_PRINTER
, "Stop Printer");
131 else if (!strcmp(op
, "start-class"))
132 do_printer_op(http
, language
, IPP_RESUME_PRINTER
, "Start Class");
133 else if (!strcmp(op
, "stop-class"))
134 do_printer_op(http
, language
, IPP_PAUSE_PRINTER
, "Stop Class");
135 else if (!strcmp(op
, "accept-jobs"))
136 do_printer_op(http
, language
, CUPS_ACCEPT_JOBS
, "Accept Jobs");
137 else if (!strcmp(op
, "reject-jobs"))
138 do_printer_op(http
, language
, CUPS_REJECT_JOBS
, "Reject Jobs");
139 else if (!strcmp(op
, "purge-jobs"))
140 do_printer_op(http
, language
, IPP_PURGE_JOBS
, "Purge Jobs");
141 else if (!strcmp(op
, "set-allowed-users"))
142 do_set_allowed_users(http
, language
);
143 else if (!strcmp(op
, "set-as-default"))
144 do_printer_op(http
, language
, CUPS_SET_DEFAULT
, "Set As Default");
145 else if (!strcmp(op
, "set-sharing"))
146 do_set_sharing(http
, language
);
147 else if (!strcmp(op
, "add-class"))
148 do_am_class(http
, language
, 0);
149 else if (!strcmp(op
, "add-printer"))
150 do_am_printer(http
, language
, 0);
151 else if (!strcmp(op
, "modify-class"))
152 do_am_class(http
, language
, 1);
153 else if (!strcmp(op
, "modify-printer"))
154 do_am_printer(http
, language
, 1);
155 else if (!strcmp(op
, "delete-class"))
156 do_delete_class(http
, language
);
157 else if (!strcmp(op
, "delete-printer"))
158 do_delete_printer(http
, language
);
159 else if (!strcmp(op
, "set-printer-options"))
160 do_config_printer(http
, language
);
161 else if (!strcmp(op
, "config-server"))
162 do_config_server(http
, language
);
166 * Bad operation code... Display an error...
169 cgiStartHTML("Error");
170 cgiCopyTemplateLang("admin-op.tmpl");
175 * Close the HTTP server connection...
183 * Form data but no operation code... Display an error...
186 cgiStartHTML("Error");
187 cgiCopyTemplateLang("admin-op.tmpl");
192 * Free the request language...
195 cupsLangFree(language
);
198 * Return with no errors...
206 * 'compare_printer_devices()' - Compare two printer devices.
209 static int /* O - Result of comparison */
210 compare_printer_devices(const void *a
, /* I - First device */
211 const void *b
) /* I - Second device */
213 return (strcmp(*((char **)a
), *((char **)b
)));
218 * 'do_am_class()' - Add or modify a class.
222 do_am_class(http_t
*http
, /* I - HTTP connection */
223 cups_lang_t
*language
, /* I - Client's language */
224 int modify
) /* I - Modify the printer? */
226 int i
, j
; /* Looping vars */
227 int element
; /* Element number */
228 int num_printers
; /* Number of printers */
229 ipp_t
*request
, /* IPP request */
230 *response
; /* IPP response */
231 ipp_attribute_t
*attr
; /* member-uris attribute */
232 ipp_status_t status
; /* Request status */
233 char uri
[HTTP_MAX_URI
]; /* Device or printer URI */
234 const char *name
, /* Pointer to class name */
235 *ptr
; /* Pointer to CGI variable */
236 const char *title
; /* Title of page */
237 static const char * const pattrs
[] = /* Requested printer attributes */
245 title
= modify
? "Modify Class" : "Add Class";
246 name
= cgiGetVariable("PRINTER_NAME");
248 if (cgiGetVariable("PRINTER_LOCATION") == NULL
)
251 * Build a CUPS_GET_PRINTERS request, which requires the
252 * following attributes:
255 * attributes-natural-language
261 request
->request
.op
.operation_id
= CUPS_GET_PRINTERS
;
262 request
->request
.op
.request_id
= 1;
264 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
265 "attributes-charset", NULL
, cupsLangEncoding(language
));
267 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
268 "attributes-natural-language", NULL
, language
->language
);
270 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
271 NULL
, "ipp://localhost/printers");
274 * Do the request and get back a response...
277 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
280 * Create MEMBER_URIS and MEMBER_NAMES arrays...
283 for (element
= 0, attr
= response
->attrs
;
286 if (attr
->name
&& !strcmp(attr
->name
, "printer-uri-supported"))
288 if ((ptr
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
&&
289 (!name
|| strcasecmp(name
, ptr
+ 1)))
292 * Don't show the current class...
295 cgiSetArray("MEMBER_URIS", element
, attr
->values
[0].string
.text
);
300 for (element
= 0, attr
= response
->attrs
;
303 if (attr
->name
&& !strcmp(attr
->name
, "printer-name"))
305 if (!name
|| strcasecmp(name
, attr
->values
[0].string
.text
))
308 * Don't show the current class...
311 cgiSetArray("MEMBER_NAMES", element
, attr
->values
[0].string
.text
);
316 num_printers
= cgiGetSize("MEMBER_URIS");
326 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
327 * following attributes:
330 * attributes-natural-language
336 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
337 request
->request
.op
.request_id
= 1;
339 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
340 "attributes-charset", NULL
, cupsLangEncoding(language
));
342 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
343 "attributes-natural-language", NULL
, language
->language
);
345 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
346 "/classes/%s", name
);
347 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
350 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
351 "requested-attributes",
352 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])),
356 * Do the request and get back a response...
359 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
361 if ((attr
= ippFindAttribute(response
, "member-names", IPP_TAG_NAME
)) != NULL
)
364 * Mark any current members in the class...
367 for (j
= 0; j
< num_printers
; j
++)
368 cgiSetArray("MEMBER_SELECTED", j
, "");
370 for (i
= 0; i
< attr
->num_values
; i
++)
372 for (j
= 0; j
< num_printers
; j
++)
374 if (!strcasecmp(attr
->values
[i
].string
.text
,
375 cgiGetArray("MEMBER_NAMES", j
)))
377 cgiSetArray("MEMBER_SELECTED", j
, "SELECTED");
384 if ((attr
= ippFindAttribute(response
, "printer-info",
385 IPP_TAG_TEXT
)) != NULL
)
386 cgiSetVariable("PRINTER_INFO", attr
->values
[0].string
.text
);
388 if ((attr
= ippFindAttribute(response
, "printer-location",
389 IPP_TAG_TEXT
)) != NULL
)
390 cgiSetVariable("PRINTER_LOCATION", attr
->values
[0].string
.text
);
396 * Update the location and description of an existing printer...
400 cgiCopyTemplateLang("modify-class.tmpl");
405 * Get the name, location, and description for a new printer...
409 cgiCopyTemplateLang("add-class.tmpl");
417 for (ptr
= name
; *ptr
; ptr
++)
418 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
421 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
423 cgiSetVariable("ERROR", "The class name may only contain up to 127 printable "
424 "characters and may not contain spaces, slashes (/), "
425 "or the pound sign (#).");
427 cgiCopyTemplateLang("error.tmpl");
433 * Build a CUPS_ADD_CLASS request, which requires the following
437 * attributes-natural-language
441 * printer-is-accepting-jobs
448 request
->request
.op
.operation_id
= CUPS_ADD_CLASS
;
449 request
->request
.op
.request_id
= 1;
451 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
452 "attributes-charset", NULL
, cupsLangEncoding(language
));
454 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
455 "attributes-natural-language", NULL
, language
->language
);
457 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
458 "/classes/%s", cgiGetVariable("PRINTER_NAME"));
459 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
462 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
463 NULL
, cgiGetVariable("PRINTER_LOCATION"));
465 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
466 NULL
, cgiGetVariable("PRINTER_INFO"));
468 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
470 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
473 if ((num_printers
= cgiGetSize("MEMBER_URIS")) > 0)
475 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "member-uris",
476 num_printers
, NULL
, NULL
);
477 for (i
= 0; i
< num_printers
; i
++)
478 attr
->values
[i
].string
.text
= strdup(cgiGetArray("MEMBER_URIS", i
));
482 * Do the request and get back a response...
485 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
487 status
= response
->request
.status
.status_code
;
491 status
= cupsLastError();
493 if (status
> IPP_OK_CONFLICT
)
496 cgiSetVariable("ERROR", ippErrorString(status
));
497 cgiCopyTemplateLang("error.tmpl");
502 * Redirect successful updates back to the class page...
505 char refresh
[1024]; /* Refresh URL */
507 cgiFormEncode(uri
, name
, sizeof(uri
));
508 snprintf(refresh
, sizeof(refresh
), "5;/admin?OP=redirect&URL=/classes/%s",
510 cgiSetVariable("refresh_page", refresh
);
515 cgiCopyTemplateLang("class-modified.tmpl");
517 cgiCopyTemplateLang("class-added.tmpl");
525 * 'do_am_printer()' - Add or modify a printer.
529 do_am_printer(http_t
*http
, /* I - HTTP connection */
530 cups_lang_t
*language
, /* I - Client's language */
531 int modify
) /* I - Modify the printer? */
533 int i
; /* Looping var */
534 int element
; /* Element number */
535 ipp_attribute_t
*attr
, /* Current attribute */
536 *last
; /* Last attribute */
537 ipp_t
*request
, /* IPP request */
538 *response
, /* IPP response */
539 *oldinfo
; /* Old printer information */
540 ipp_status_t status
; /* Request status */
541 const cgi_file_t
*file
; /* Uploaded file, if any */
542 const char *var
; /* CGI variable */
543 char uri
[HTTP_MAX_URI
], /* Device or printer URI */
544 *uriptr
; /* Pointer into URI */
545 int maxrate
; /* Maximum baud rate */
546 char baudrate
[255]; /* Baud rate string */
547 const char *name
, /* Pointer to class name */
548 *ptr
; /* Pointer to CGI variable */
549 const char *title
; /* Title of page */
550 static int baudrates
[] = /* Baud rates */
565 title
= modify
? "Modify Printer" : "Add Printer";
570 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
571 * following attributes:
574 * attributes-natural-language
580 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
581 request
->request
.op
.request_id
= 1;
583 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
584 "attributes-charset", NULL
, cupsLangEncoding(language
));
586 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
587 "attributes-natural-language", NULL
, language
->language
);
589 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
590 "/printers/%s", cgiGetVariable("PRINTER_NAME"));
591 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
595 * Do the request and get back a response...
598 oldinfo
= cupsDoRequest(http
, request
, "/");
603 if ((name
= cgiGetVariable("PRINTER_NAME")) == NULL
||
604 cgiGetVariable("PRINTER_LOCATION") == NULL
)
611 * Update the location and description of an existing printer...
615 cgiSetIPPVars(oldinfo
, NULL
, NULL
, NULL
, 0);
617 cgiCopyTemplateLang("modify-printer.tmpl");
622 * Get the name, location, and description for a new printer...
625 cgiCopyTemplateLang("add-printer.tmpl");
636 for (ptr
= name
; *ptr
; ptr
++)
637 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
640 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
642 cgiSetVariable("ERROR", "The printer name may only contain up to 127 printable "
643 "characters and may not contain spaces, slashes (/), "
644 "or the pound sign (#).");
646 cgiCopyTemplateLang("error.tmpl");
655 fprintf(stderr
, "DEBUG: file->tempfile=%s\n", file
->tempfile
);
656 fprintf(stderr
, "DEBUG: file->name=%s\n", file
->name
);
657 fprintf(stderr
, "DEBUG: file->filename=%s\n", file
->filename
);
658 fprintf(stderr
, "DEBUG: file->mimetype=%s\n", file
->mimetype
);
661 if ((var
= cgiGetVariable("DEVICE_URI")) == NULL
)
664 * Build a CUPS_GET_DEVICES request, which requires the following
668 * attributes-natural-language
674 request
->request
.op
.operation_id
= CUPS_GET_DEVICES
;
675 request
->request
.op
.request_id
= 1;
677 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
678 "attributes-charset", NULL
, cupsLangEncoding(language
));
680 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
681 "attributes-natural-language", NULL
, language
->language
);
683 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
684 NULL
, "ipp://localhost/printers/");
687 * Do the request and get back a response...
690 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
692 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
697 * Let the user choose...
700 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
702 strlcpy(uri
, attr
->values
[0].string
.text
, sizeof(uri
));
703 if ((uriptr
= strchr(uri
, ':')) != NULL
&& strncmp(uriptr
, "://", 3) == 0)
706 cgiSetVariable("CURRENT_DEVICE_URI", attr
->values
[0].string
.text
);
707 cgiSetVariable("CURRENT_DEVICE_SCHEME", uri
);
711 cgiCopyTemplateLang("choose-device.tmpl");
714 else if (strchr(var
, '/') == NULL
)
716 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
719 * Set the current device URI for the form to the old one...
722 if (strncmp(attr
->values
[0].string
.text
, var
, strlen(var
)) == 0)
723 cgiSetVariable("DEVICE_URI", attr
->values
[0].string
.text
);
727 * User needs to set the full URI...
731 cgiCopyTemplateLang("choose-uri.tmpl");
734 else if (!strncmp(var
, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
737 * Need baud rate, parity, etc.
740 if ((var
= strchr(var
, '?')) != NULL
&&
741 strncmp(var
, "?baud=", 6) == 0)
742 maxrate
= atoi(var
+ 6);
746 for (i
= 0; i
< 10; i
++)
747 if (baudrates
[i
] > maxrate
)
751 sprintf(baudrate
, "%d", baudrates
[i
]);
752 cgiSetArray("BAUDRATES", i
, baudrate
);
756 cgiCopyTemplateLang("choose-serial.tmpl");
759 else if (!file
&& (var
= cgiGetVariable("PPD_NAME")) == NULL
)
764 * Get the PPD file...
767 int fd
; /* PPD file */
768 char filename
[1024]; /* PPD filename */
769 ppd_file_t
*ppd
; /* PPD information */
770 char buffer
[1024]; /* Buffer */
771 int bytes
; /* Number of bytes */
772 http_status_t get_status
; /* Status of GET */
775 snprintf(uri
, sizeof(uri
), "/printers/%s.ppd", name
);
777 if (httpGet(http
, uri
))
780 while ((get_status
= httpUpdate(http
)) == HTTP_CONTINUE
);
782 if (get_status
!= HTTP_OK
)
784 fprintf(stderr
, "ERROR: Unable to get PPD file %s: %d - %s\n",
785 uri
, get_status
, httpStatus(get_status
));
787 else if ((fd
= cupsTempFd(filename
, sizeof(filename
))) >= 0)
789 while ((bytes
= httpRead(http
, buffer
, sizeof(buffer
))) > 0)
790 write(fd
, buffer
, bytes
);
794 if ((ppd
= ppdOpenFile(filename
)) != NULL
)
796 if (ppd
->manufacturer
)
797 cgiSetVariable("CURRENT_MAKE", ppd
->manufacturer
);
800 cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd
->nickname
);
807 fprintf(stderr
, "ERROR: Unable to open PPD file %s: %s\n",
808 filename
, ppdErrorString(ppdLastError(&bytes
)));
816 "ERROR: Unable to create temporary file for PPD file: %s\n",
820 else if ((uriptr
= strrchr(cgiGetVariable("DEVICE_URI"), '|')) != NULL
)
823 * Extract make and make/model from device URI string...
826 char make
[1024], /* Make string */
827 *makeptr
; /* Pointer into make */
832 strlcpy(make
, uriptr
, sizeof(make
));
834 if ((makeptr
= strchr(make
, ' ')) != NULL
)
836 else if ((makeptr
= strchr(make
, '-')) != NULL
)
838 else if (!strncasecmp(make
, "laserjet", 8) ||
839 !strncasecmp(make
, "deskjet", 7) ||
840 !strncasecmp(make
, "designjet", 9))
842 else if (!strncasecmp(make
, "phaser", 6))
843 strcpy(make
, "Xerox");
844 else if (!strncasecmp(make
, "stylus", 6))
845 strcpy(make
, "Epson");
847 strcpy(make
, "Generic");
849 cgiSetVariable("CURRENT_MAKE", make
);
850 cgiSetVariable("PPD_MAKE", make
);
851 cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr
);
855 * Build a CUPS_GET_PPDS request, which requires the following
859 * attributes-natural-language
865 request
->request
.op
.operation_id
= CUPS_GET_PPDS
;
866 request
->request
.op
.request_id
= 1;
868 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
869 "attributes-charset", NULL
, cupsLangEncoding(language
));
871 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
872 "attributes-natural-language", NULL
, language
->language
);
874 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
875 NULL
, "ipp://localhost/printers/");
878 if ((var
= cgiGetVariable("PPD_MAKE")) != NULL
)
879 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
880 "ppd-make", NULL
, var
);
882 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
883 "requested-attributes", NULL
, "ppd-make");
886 * Do the request and get back a response...
889 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
892 * Got the list of PPDs, see if the user has selected a make...
895 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
900 * Let the user choose a make...
903 for (element
= 0, attr
= response
->attrs
, last
= NULL
;
906 if (attr
->name
&& strcmp(attr
->name
, "ppd-make") == 0)
908 strcasecmp(last
->values
[0].string
.text
,
909 attr
->values
[0].string
.text
) != 0)
911 cgiSetArray("PPD_MAKE", element
, attr
->values
[0].string
.text
);
917 cgiCopyTemplateLang("choose-make.tmpl");
923 * Let the user choose a model...
926 const char *make_model
; /* Current make/model string */
929 if ((make_model
= cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL
)
932 * Scan for "close" matches...
935 int match
, /* Current match */
936 best_match
, /* Best match so far */
937 count
; /* Number of drivers */
938 const char *best
, /* Best matching string */
939 *current
; /* Current string */
942 count
= cgiGetSize("PPD_MAKE_AND_MODEL");
944 for (i
= 0, best_match
= 0, best
= NULL
; i
< count
; i
++)
946 current
= cgiGetArray("PPD_MAKE_AND_MODEL", i
);
947 match
= match_string(make_model
, current
);
949 if (match
> best_match
)
956 if (best_match
> strlen(var
))
959 * Found a match longer than the make...
962 cgiSetVariable("CURRENT_MAKE_AND_MODEL", best
);
967 cgiCopyTemplateLang("choose-model.tmpl");
979 snprintf(message
, sizeof(message
), "Unable to get list of printer drivers: %s",
980 ippErrorString(cupsLastError()));
981 cgiSetVariable("ERROR", message
);
983 cgiCopyTemplateLang("error.tmpl");
990 * Build a CUPS_ADD_PRINTER request, which requires the following
994 * attributes-natural-language
1000 * printer-is-accepting-jobs
1006 request
->request
.op
.operation_id
= CUPS_ADD_PRINTER
;
1007 request
->request
.op
.request_id
= 1;
1009 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1010 "attributes-charset", NULL
, cupsLangEncoding(language
));
1012 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1013 "attributes-natural-language", NULL
, language
->language
);
1015 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
1016 "/printers/%s", cgiGetVariable("PRINTER_NAME"));
1017 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1020 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
1021 NULL
, cgiGetVariable("PRINTER_LOCATION"));
1023 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1024 NULL
, cgiGetVariable("PRINTER_INFO"));
1027 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "ppd-name",
1028 NULL
, cgiGetVariable("PPD_NAME"));
1030 strlcpy(uri
, cgiGetVariable("DEVICE_URI"), sizeof(uri
));
1033 * Strip make and model from URI...
1036 if ((uriptr
= strrchr(uri
, '|')) != NULL
)
1039 if (strncmp(uri
, "serial:", 7) == 0)
1042 * Update serial port URI to include baud rate, etc.
1045 if ((uriptr
= strchr(uri
, '?')) == NULL
)
1046 uriptr
= uri
+ strlen(uri
);
1048 snprintf(uriptr
, sizeof(uri
) - (uriptr
- uri
),
1049 "?baud=%s+bits=%s+parity=%s+flow=%s",
1050 cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
1051 cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
1054 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "device-uri",
1057 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1059 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
1063 * Do the request and get back a response...
1067 response
= cupsDoFileRequest(http
, request
, "/admin/", file
->tempfile
);
1069 response
= cupsDoRequest(http
, request
, "/admin/");
1073 status
= response
->request
.status
.status_code
;
1074 ippDelete(response
);
1077 status
= cupsLastError();
1079 if (status
> IPP_OK_CONFLICT
)
1081 cgiStartHTML(title
);
1082 cgiSetVariable("ERROR", ippErrorString(status
));
1083 cgiCopyTemplateLang("error.tmpl");
1088 * Redirect successful updates back to the printer or set-options pages...
1091 char refresh
[1024]; /* Refresh URL */
1094 cgiFormEncode(uri
, name
, sizeof(uri
));
1097 snprintf(refresh
, sizeof(refresh
),
1098 "5;/admin?OP=redirect&URL=/printers/%s", uri
);
1100 snprintf(refresh
, sizeof(refresh
),
1101 "5;/admin?OP=set-printer-options&PRINTER_NAME=%s", uri
);
1103 cgiSetVariable("refresh_page", refresh
);
1105 cgiStartHTML(title
);
1108 cgiCopyTemplateLang("printer-modified.tmpl");
1110 cgiCopyTemplateLang("printer-added.tmpl");
1122 * 'do_config_printer()' - Configure the default options for a printer.
1126 do_config_printer(http_t
*http
, /* I - HTTP connection */
1127 cups_lang_t
*language
)/* I - Client's language */
1129 int i
, j
, k
, m
; /* Looping vars */
1130 int have_options
; /* Have options? */
1131 ipp_t
*request
, /* IPP request */
1132 *response
; /* IPP response */
1133 ipp_attribute_t
*attr
; /* IPP attribute */
1134 char uri
[HTTP_MAX_URI
]; /* Job URI */
1135 const char *var
; /* Variable value */
1136 const char *printer
; /* Printer printer name */
1137 ipp_status_t status
; /* Operation status... */
1138 const char *filename
; /* PPD filename */
1139 char tempfile
[1024]; /* Temporary filename */
1140 cups_file_t
*in
, /* Input file */
1141 *out
; /* Output file */
1142 char line
[1024]; /* Line from PPD file */
1143 char keyword
[1024], /* Keyword from Default line */
1144 *keyptr
; /* Pointer into keyword... */
1145 ppd_file_t
*ppd
; /* PPD file */
1146 ppd_group_t
*group
; /* Option group */
1147 ppd_option_t
*option
; /* Option */
1148 ppd_attr_t
*protocol
; /* cupsProtocol attribute */
1152 * Get the printer name...
1155 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
1156 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
1157 "/printers/%s", printer
);
1160 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND
));
1161 cgiStartHTML("Set Printer Options");
1162 cgiCopyTemplateLang("error.tmpl");
1168 * Get the PPD file...
1171 if ((filename
= cupsGetPPD(printer
)) == NULL
)
1173 if (cupsLastError() == IPP_NOT_FOUND
)
1176 * No PPD file for this printer, so we can't configure it!
1179 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_POSSIBLE
));
1180 cgiStartHTML("Set Printer Options");
1181 cgiCopyTemplateLang("error.tmpl");
1187 * Unable to access the PPD file for some reason...
1190 cgiSetVariable("ERROR", ippErrorString(cupsLastError()));
1191 cgiStartHTML("Set Printer Options");
1192 cgiCopyTemplateLang("error.tmpl");
1198 if ((ppd
= ppdOpenFile(filename
)) == NULL
)
1200 cgiSetVariable("ERROR", ippErrorString(IPP_DEVICE_ERROR
));
1201 cgiStartHTML("Set Printer Options");
1202 cgiCopyTemplateLang("error.tmpl");
1207 if (cgiGetVariable("job_sheets_start") != NULL
||
1208 cgiGetVariable("job_sheets_end") != NULL
)
1213 ppdMarkDefaults(ppd
);
1215 DEBUG_printf(("<P>ppd->num_groups = %d\n"
1216 "<UL>\n", ppd
->num_groups
));
1218 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
1220 DEBUG_printf(("<LI>%s<UL>\n", group
->text
));
1222 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
1223 if ((var
= cgiGetVariable(option
->keyword
)) != NULL
)
1225 DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option
->keyword
, var
));
1227 ppdMarkOption(ppd
, option
->keyword
, var
);
1231 printf("<LI>%s not defined!</LI>\n", option
->keyword
);
1234 DEBUG_puts("</UL></LI>");
1237 DEBUG_printf(("</UL>\n"
1238 "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd
)));
1240 if (!have_options
|| ppdConflicts(ppd
))
1243 * Show the options to the user...
1246 cgiStartHTML("Set Printer Options");
1247 cgiCopyTemplateLang("set-printer-options-header.tmpl");
1249 if (ppdConflicts(ppd
))
1251 for (i
= ppd
->num_groups
, k
= 0, group
= ppd
->groups
; i
> 0; i
--, group
++)
1252 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
1253 if (option
->conflicted
)
1255 cgiSetArray("ckeyword", k
, option
->keyword
);
1256 cgiSetArray("ckeytext", k
, option
->text
);
1260 cgiCopyTemplateLang("option-conflict.tmpl");
1263 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
1267 if (!strcmp(group
->name
, "InstallableOptions"))
1268 cgiSetVariable("GROUP",
1269 _cupsLangString(language
, _("Options Installed")));
1271 cgiSetVariable("GROUP", group
->text
);
1273 cgiCopyTemplateLang("option-header.tmpl");
1275 for (j
= group
->num_options
, option
= group
->options
;
1279 if (!strcmp(option
->keyword
, "PageRegion"))
1282 cgiSetVariable("KEYWORD", option
->keyword
);
1283 cgiSetVariable("KEYTEXT", option
->text
);
1285 if (option
->conflicted
)
1286 cgiSetVariable("CONFLICTED", "1");
1288 cgiSetVariable("CONFLICTED", "0");
1290 cgiSetSize("CHOICES", 0);
1291 cgiSetSize("TEXT", 0);
1292 for (k
= 0, m
= 0; k
< option
->num_choices
; k
++)
1295 * Hide custom option values...
1298 if (!strcmp(option
->choices
[k
].choice
, "Custom"))
1301 cgiSetArray("CHOICES", m
, option
->choices
[k
].choice
);
1302 cgiSetArray("TEXT", m
, option
->choices
[k
].text
);
1306 if (option
->choices
[k
].marked
)
1307 cgiSetVariable("DEFCHOICE", option
->choices
[k
].choice
);
1312 case PPD_UI_BOOLEAN
:
1313 cgiCopyTemplateLang("option-boolean.tmpl");
1315 case PPD_UI_PICKONE
:
1316 cgiCopyTemplateLang("option-pickone.tmpl");
1318 case PPD_UI_PICKMANY
:
1319 cgiCopyTemplateLang("option-pickmany.tmpl");
1324 cgiCopyTemplateLang("option-trailer.tmpl");
1328 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
1329 * following attributes:
1331 * attributes-charset
1332 * attributes-natural-language
1338 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
1339 request
->request
.op
.request_id
= 1;
1341 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1342 "attributes-charset", NULL
, cupsLangEncoding(language
));
1344 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1345 "attributes-natural-language", NULL
, language
->language
);
1347 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
1348 "/printers/%s", printer
);
1349 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1353 * Do the request and get back a response...
1356 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1358 if ((attr
= ippFindAttribute(response
, "job-sheets-supported", IPP_TAG_ZERO
)) != NULL
)
1361 * Add the job sheets options...
1364 cgiSetVariable("GROUP", "Banners");
1365 cgiCopyTemplateLang("option-header.tmpl");
1367 cgiSetSize("CHOICES", attr
->num_values
);
1368 cgiSetSize("TEXT", attr
->num_values
);
1369 for (k
= 0; k
< attr
->num_values
; k
++)
1371 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1372 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1375 attr
= ippFindAttribute(response
, "job-sheets-default", IPP_TAG_ZERO
);
1377 cgiSetVariable("KEYWORD", "job_sheets_start");
1378 cgiSetVariable("KEYTEXT", "Starting Banner");
1379 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1380 "" : attr
->values
[0].string
.text
);
1382 cgiCopyTemplateLang("option-pickone.tmpl");
1384 cgiSetVariable("KEYWORD", "job_sheets_end");
1385 cgiSetVariable("KEYTEXT", "Ending Banner");
1386 cgiSetVariable("DEFCHOICE", attr
== NULL
&& attr
->num_values
> 1 ?
1387 "" : attr
->values
[1].string
.text
);
1389 cgiCopyTemplateLang("option-pickone.tmpl");
1391 cgiCopyTemplateLang("option-trailer.tmpl");
1394 if (ippFindAttribute(response
, "printer-error-policy-supported",
1396 ippFindAttribute(response
, "printer-op-policy-supported",
1400 * Add the error and operation policy options...
1403 cgiSetVariable("GROUP", "Policies");
1404 cgiCopyTemplateLang("option-header.tmpl");
1410 attr
= ippFindAttribute(response
, "printer-error-policy-supported",
1415 cgiSetSize("CHOICES", attr
->num_values
);
1416 cgiSetSize("TEXT", attr
->num_values
);
1417 for (k
= 0; k
< attr
->num_values
; k
++)
1419 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1420 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1423 attr
= ippFindAttribute(response
, "printer-error-policy",
1426 cgiSetVariable("KEYWORD", "printer_error_policy");
1427 cgiSetVariable("KEYTEXT", "Error Policy");
1428 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1429 "" : attr
->values
[0].string
.text
);
1432 cgiCopyTemplateLang("option-pickone.tmpl");
1435 * Operation policy...
1438 attr
= ippFindAttribute(response
, "printer-op-policy-supported",
1443 cgiSetSize("CHOICES", attr
->num_values
);
1444 cgiSetSize("TEXT", attr
->num_values
);
1445 for (k
= 0; k
< attr
->num_values
; k
++)
1447 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1448 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1451 attr
= ippFindAttribute(response
, "printer-op-policy", IPP_TAG_ZERO
);
1453 cgiSetVariable("KEYWORD", "printer_op_policy");
1454 cgiSetVariable("KEYTEXT", "Operation Policy");
1455 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1456 "" : attr
->values
[0].string
.text
);
1458 cgiCopyTemplateLang("option-pickone.tmpl");
1461 cgiCopyTemplateLang("option-trailer.tmpl");
1464 ippDelete(response
);
1468 * Binary protocol support...
1471 if (ppd
->protocols
&& strstr(ppd
->protocols
, "BCP"))
1473 protocol
= ppdFindAttr(ppd
, "cupsProtocol", NULL
);
1475 cgiSetVariable("GROUP", "PS Binary Protocol");
1476 cgiCopyTemplateLang("option-header.tmpl");
1478 cgiSetSize("CHOICES", 2);
1479 cgiSetSize("TEXT", 2);
1480 cgiSetArray("CHOICES", 0, "None");
1481 cgiSetArray("TEXT", 0, "None");
1483 if (strstr(ppd
->protocols
, "TBCP"))
1485 cgiSetArray("CHOICES", 1, "TBCP");
1486 cgiSetArray("TEXT", 1, "TBCP");
1490 cgiSetArray("CHOICES", 1, "BCP");
1491 cgiSetArray("TEXT", 1, "BCP");
1494 cgiSetVariable("KEYWORD", "protocol");
1495 cgiSetVariable("KEYTEXT", "PS Binary Protocol");
1496 cgiSetVariable("DEFCHOICE", protocol
? protocol
->value
: "None");
1498 cgiCopyTemplateLang("option-pickone.tmpl");
1500 cgiCopyTemplateLang("option-trailer.tmpl");
1503 cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
1509 * Set default options...
1512 out
= cupsTempFile2(tempfile
, sizeof(tempfile
));
1513 in
= cupsFileOpen(filename
, "r");
1517 cgiSetVariable("ERROR", strerror(errno
));
1518 cgiStartHTML("Set Printer Options");
1519 cgiCopyTemplateLang("error.tmpl");
1535 while (cupsFileGets(in
, line
, sizeof(line
)))
1537 if (!strncmp(line
, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
1539 else if (strncmp(line
, "*Default", 8))
1540 cupsFilePrintf(out
, "%s\n", line
);
1544 * Get default option name...
1547 strlcpy(keyword
, line
+ 8, sizeof(keyword
));
1549 for (keyptr
= keyword
; *keyptr
; keyptr
++)
1550 if (*keyptr
== ':' || isspace(*keyptr
& 255))
1555 if (!strcmp(keyword
, "PageRegion"))
1556 var
= cgiGetVariable("PageSize");
1558 var
= cgiGetVariable(keyword
);
1561 cupsFilePrintf(out
, "*Default%s: %s\n", keyword
, var
);
1563 cupsFilePrintf(out
, "%s\n", line
);
1567 if ((var
= cgiGetVariable("protocol")) != NULL
)
1568 cupsFilePrintf(out
, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
1574 * Build a CUPS_ADD_PRINTER request, which requires the following
1577 * attributes-charset
1578 * attributes-natural-language
1580 * job-sheets-default
1586 request
->request
.op
.operation_id
= CUPS_ADD_PRINTER
;
1587 request
->request
.op
.request_id
= 1;
1589 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1590 "attributes-charset", NULL
, cupsLangEncoding(language
));
1592 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1593 "attributes-natural-language", NULL
, language
->language
);
1595 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
1596 "/printers/%s", cgiGetVariable("PRINTER_NAME"));
1597 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1600 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1601 "job-sheets-default", 2, NULL
, NULL
);
1602 attr
->values
[0].string
.text
= strdup(cgiGetVariable("job_sheets_start"));
1603 attr
->values
[1].string
.text
= strdup(cgiGetVariable("job_sheets_end"));
1605 if ((var
= cgiGetVariable("printer_error_policy")) != NULL
)
1606 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1607 "printer-error-policy", NULL
, var
);
1609 if ((var
= cgiGetVariable("printer_op_policy")) != NULL
)
1610 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1611 "printer-op-policy", NULL
, var
);
1614 * Do the request and get back a response...
1617 if ((response
= cupsDoFileRequest(http
, request
, "/admin/", tempfile
)) != NULL
)
1619 status
= response
->request
.status
.status_code
;
1620 ippDelete(response
);
1623 status
= cupsLastError();
1625 if (status
> IPP_OK_CONFLICT
)
1627 cgiStartHTML("Set Printer Options");
1628 cgiSetVariable("ERROR", ippErrorString(status
));
1629 cgiCopyTemplateLang("error.tmpl");
1634 * Redirect successful updates back to the printer page...
1637 char refresh
[1024]; /* Refresh URL */
1639 cgiFormEncode(uri
, printer
, sizeof(uri
));
1640 snprintf(refresh
, sizeof(refresh
), "5;/admin?OP=redirect&URL=/printers/%s",
1642 cgiSetVariable("refresh_page", refresh
);
1644 cgiStartHTML("Set Printer Options");
1646 cgiCopyTemplateLang("printer-configured.tmpl");
1659 * 'do_config_server()' - Configure server settings.
1663 do_config_server(http_t
*http
, /* I - HTTP connection */
1664 cups_lang_t
*language
) /* I - Client's language */
1666 if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
1669 * Save basic setting changes...
1672 http_status_t status
; /* PUT status */
1673 cups_file_t
*cupsd
; /* cupsd.conf file */
1674 char tempfile
[1024]; /* Temporary new cupsd.conf */
1675 int tempfd
; /* Temporary file descriptor */
1676 cups_file_t
*temp
; /* Temporary file */
1677 char line
[1024], /* Line from cupsd.conf file */
1678 *value
; /* Value on line */
1679 const char *server_root
; /* Location of config files */
1680 int linenum
, /* Line number in file */
1681 in_policy
, /* In a policy section? */
1682 in_cancel_job
, /* In a cancel-job section? */
1683 in_admin_location
, /* In the /admin location? */
1684 in_conf_location
, /* In the /admin/conf location? */
1685 in_root_location
; /* In the / location? */
1686 int remote_printers
, /* Show remote printers */
1687 share_printers
, /* Share local printers */
1688 remote_admin
, /* Remote administration allowed? */
1689 user_cancel_any
, /* Cancel-job policy set? */
1690 debug_logging
; /* LogLevel debug set? */
1691 int wrote_port_listen
, /* Wrote the port/listen lines? */
1692 wrote_browsing
, /* Wrote the browsing lines? */
1693 wrote_policy
, /* Wrote the policy? */
1694 wrote_loglevel
, /* Wrote the LogLevel line? */
1695 wrote_admin_location
, /* Wrote the /admin location? */
1696 wrote_conf_location
, /* Wrote the /admin/conf location? */
1697 wrote_root_location
; /* Wrote the / location? */
1698 int indent
; /* Indentation */
1702 * Get form variables...
1705 remote_printers
= cgiGetVariable("REMOTE_PRINTERS") != NULL
;
1706 share_printers
= cgiGetVariable("SHARE_PRINTERS") != NULL
;
1707 remote_admin
= cgiGetVariable("REMOTE_ADMIN") != NULL
;
1708 user_cancel_any
= cgiGetVariable("USER_CANCEL_ANY") != NULL
;
1709 debug_logging
= cgiGetVariable("DEBUG_LOGGING") != NULL
;
1712 * Locate the cupsd.conf file...
1715 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
1716 server_root
= CUPS_SERVERROOT
;
1718 snprintf(line
, sizeof(line
), "%s/cupsd.conf", server_root
);
1721 * Open the cupsd.conf file...
1724 if ((cupsd
= cupsFileOpen(line
, "r")) == NULL
)
1727 * Unable to open - log an error...
1730 cgiStartHTML("Change Settings");
1731 cgiSetVariable("ERROR", strerror(errno
));
1732 cgiCopyTemplateLang("error.tmpl");
1740 * Create a temporary file for the new cupsd.conf file...
1743 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
1745 cgiStartHTML("Change Settings");
1746 cgiSetVariable("ERROR", strerror(errno
));
1747 cgiCopyTemplateLang("error.tmpl");
1751 cupsFileClose(cupsd
);
1755 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
1757 cgiStartHTML("Change Settings");
1758 cgiSetVariable("ERROR", strerror(errno
));
1759 cgiCopyTemplateLang("error.tmpl");
1765 cupsFileClose(cupsd
);
1770 * Copy the old file to the new, making changes along the way...
1773 in_admin_location
= 0;
1775 in_conf_location
= 0;
1777 in_root_location
= 0;
1779 wrote_admin_location
= 0;
1781 wrote_conf_location
= 0;
1784 wrote_port_listen
= 0;
1785 wrote_root_location
= 0;
1788 while (cupsFileGetConf(cupsd
, line
, sizeof(line
), &value
, &linenum
))
1790 if (!strcasecmp(line
, "Port") || !strcasecmp(line
, "Listen"))
1792 if (!wrote_port_listen
)
1794 wrote_port_listen
= 1;
1796 if (share_printers
|| remote_admin
)
1798 cupsFilePuts(temp
, "# Allow remote access\n");
1799 cupsFilePrintf(temp
, "Listen *:%d\n", ippPort());
1803 cupsFilePuts(temp
, "# Only listen for connections from the local machine.\n");
1804 cupsFilePrintf(temp
, "Listen localhost:%d\n", ippPort());
1807 #ifdef CUPS_DEFAULT_DOMAINSOCKET
1808 cupsFilePuts(temp
, "Listen " CUPS_DEFAULT_DOMAINSOCKET
"\n");
1809 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
1812 else if (!strcasecmp(line
, "Browsing") ||
1813 !strcasecmp(line
, "BrowseAddress") ||
1814 !strcasecmp(line
, "BrowseAllow") ||
1815 !strcasecmp(line
, "BrowseDeny") ||
1816 !strcasecmp(line
, "BrowseOrder"))
1818 if (!wrote_browsing
)
1822 if (remote_printers
|| share_printers
)
1824 if (remote_printers
&& share_printers
)
1825 cupsFilePuts(temp
, "# Enable printer sharing and shared printers.\n");
1826 else if (remote_printers
)
1827 cupsFilePuts(temp
, "# Show shared printers on the local network.\n");
1829 cupsFilePuts(temp
, "# Share local printers on the local network.\n");
1831 cupsFilePuts(temp
, "Browsing On\n");
1832 cupsFilePuts(temp
, "BrowseOrder allow,deny\n");
1834 if (remote_printers
)
1835 cupsFilePuts(temp
, "BrowseAllow @LOCAL\n");
1838 cupsFilePuts(temp
, "BrowseAddress @LOCAL\n");
1842 cupsFilePuts(temp
, "# Disable printer sharing and shared printers.\n");
1843 cupsFilePuts(temp
, "Browsing Off\n");
1847 else if (!strcasecmp(line
, "LogLevel"))
1853 cupsFilePuts(temp
, "# Show troubleshooting information in error_log.\n");
1854 cupsFilePuts(temp
, "LogLevel debug\n");
1858 cupsFilePuts(temp
, "# Show general information in error_log.\n");
1859 cupsFilePuts(temp
, "LogLevel info\n");
1862 else if (!strcasecmp(line
, "<Policy") && !strcasecmp(value
, "default"))
1866 cupsFilePrintf(temp
, "%s %s>\n", line
, value
);
1869 else if (!strcasecmp(line
, "</Policy>"))
1876 if (!user_cancel_any
)
1877 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
1878 " <Limit Cancel-Job>\n"
1879 " Order deny,allow\n"
1887 cupsFilePuts(temp
, "</Policy>\n");
1889 else if (!strcasecmp(line
, "<Location"))
1892 if (!strcmp(value
, "/admin"))
1893 in_admin_location
= 1;
1894 if (!strcmp(value
, "/admin/conf"))
1895 in_conf_location
= 1;
1896 else if (!strcmp(value
, "/"))
1897 in_root_location
= 1;
1899 cupsFilePrintf(temp
, "%s %s>\n", line
, value
);
1901 else if (!strcasecmp(line
, "</Location>"))
1904 if (in_admin_location
)
1906 wrote_admin_location
= 1;
1909 cupsFilePuts(temp
, " # Allow remote administration...\n");
1911 cupsFilePuts(temp
, " # Restrict access to the admin pages...\n");
1913 cupsFilePuts(temp
, " Order allow,deny\n");
1916 cupsFilePuts(temp
, " Allow @LOCAL\n");
1918 cupsFilePuts(temp
, " Allow localhost\n");
1920 else if (in_conf_location
)
1922 wrote_conf_location
= 1;
1925 cupsFilePuts(temp
, " # Allow remote access to the configuration files...\n");
1927 cupsFilePuts(temp
, " # Restrict access to the configuration files...\n");
1929 cupsFilePuts(temp
, " Order allow,deny\n");
1932 cupsFilePuts(temp
, " Allow @LOCAL\n");
1934 cupsFilePuts(temp
, " Allow localhost\n");
1936 else if (in_root_location
)
1938 wrote_root_location
= 1;
1940 if (remote_admin
&& share_printers
)
1941 cupsFilePuts(temp
, " # Allow shared printing and remote administration...\n");
1942 else if (remote_admin
)
1943 cupsFilePuts(temp
, " # Allow remote administration...\n");
1944 else if (share_printers
)
1945 cupsFilePuts(temp
, " # Allow shared printing...\n");
1947 cupsFilePuts(temp
, " # Restrict access to the server...\n");
1949 cupsFilePuts(temp
, " Order allow,deny\n");
1951 if (remote_admin
|| share_printers
)
1952 cupsFilePuts(temp
, " Allow @LOCAL\n");
1954 cupsFilePuts(temp
, " Allow localhost\n");
1957 in_admin_location
= 0;
1958 in_conf_location
= 0;
1959 in_root_location
= 0;
1961 cupsFilePuts(temp
, "</Location>\n");
1963 else if (!strcasecmp(line
, "<Limit") && in_policy
)
1966 * See if the policy limit is for the Cancel-Job operation...
1969 char *valptr
; /* Pointer into value */
1974 if (!strcasecmp(value
, "cancel-job"))
1977 * Don't write anything for this limit section...
1984 cupsFilePrintf(temp
, " %s", line
);
1988 for (valptr
= value
; !isspace(*valptr
& 255) && *valptr
; valptr
++);
1993 if (!strcasecmp(value
, "cancel-job"))
1996 * Write everything except for this definition...
2002 cupsFilePrintf(temp
, " %s", value
);
2004 for (value
= valptr
; isspace(*value
& 255); value
++);
2007 cupsFilePuts(temp
, ">\n");
2010 else if (!strcasecmp(line
, "</Limit>") && in_cancel_job
)
2014 if (in_cancel_job
== 1)
2015 cupsFilePuts(temp
, " </Limit>\n");
2019 if (!user_cancel_any
)
2020 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
2021 " <Limit Cancel-Job>\n"
2022 " Order deny,allow\n"
2023 " Require user @OWNER @SYSTEM\n"
2028 else if ((in_admin_location
|| in_conf_location
|| in_root_location
) &&
2029 (!strcasecmp(line
, "Allow") || !strcasecmp(line
, "Deny") ||
2030 !strcasecmp(line
, "Order")))
2032 else if (in_cancel_job
== 2)
2034 else if (!strcasecmp(line
, "<Limit") && value
)
2035 cupsFilePrintf(temp
, " %s %s>\n", line
, value
);
2036 else if (line
[0] == '<')
2040 cupsFilePrintf(temp
, "%*s%s %s>\n", indent
, "", line
, value
);
2048 cupsFilePrintf(temp
, "%*s%s\n", indent
, "", line
);
2052 cupsFilePrintf(temp
, "%*s%s %s\n", indent
, "", line
, value
);
2054 cupsFilePrintf(temp
, "%*s%s\n", indent
, "", line
);
2058 * Write any missing info...
2061 if (!wrote_browsing
)
2063 if (remote_printers
|| share_printers
)
2065 if (remote_printers
&& share_printers
)
2066 cupsFilePuts(temp
, "# Enable printer sharing and shared printers.\n");
2067 else if (remote_printers
)
2068 cupsFilePuts(temp
, "# Show shared printers on the local network.\n");
2070 cupsFilePuts(temp
, "# Share local printers on the local network.\n");
2072 cupsFilePuts(temp
, "Browsing On\n");
2073 cupsFilePuts(temp
, "BrowseOrder allow,deny\n");
2075 if (remote_printers
)
2076 cupsFilePuts(temp
, "BrowseAllow @LOCAL\n");
2079 cupsFilePuts(temp
, "BrowseAddress @LOCAL\n");
2083 cupsFilePuts(temp
, "# Disable printer sharing and shared printers.\n");
2084 cupsFilePuts(temp
, "Browsing Off\n");
2088 if (!wrote_loglevel
)
2092 cupsFilePuts(temp
, "# Show troubleshooting information in error_log.\n");
2093 cupsFilePuts(temp
, "LogLevel debug\n");
2097 cupsFilePuts(temp
, "# Show general information in error_log.\n");
2098 cupsFilePuts(temp
, "LogLevel info\n");
2102 if (!wrote_port_listen
)
2104 if (share_printers
|| remote_admin
)
2106 cupsFilePuts(temp
, "# Allow remote access\n");
2107 cupsFilePrintf(temp
, "Listen *:%d\n", ippPort());
2111 cupsFilePuts(temp
, "# Only listen for connections from the local machine.\n");
2112 cupsFilePrintf(temp
, "Listen localhost:%d\n", ippPort());
2115 #ifdef CUPS_DEFAULT_DOMAINSOCKET
2116 cupsFilePuts(temp
, "Listen " CUPS_DEFAULT_DOMAINSOCKET
"\n");
2117 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
2120 if (!wrote_root_location
)
2122 if (remote_admin
&& share_printers
)
2123 cupsFilePuts(temp
, "# Allow shared printing and remote administration...\n");
2124 else if (remote_admin
)
2125 cupsFilePuts(temp
, "# Allow remote administration...\n");
2126 else if (share_printers
)
2127 cupsFilePuts(temp
, "# Allow shared printing...\n");
2129 cupsFilePuts(temp
, "# Restrict access to the server...\n");
2131 cupsFilePuts(temp
, "<Location />\n"
2132 " Order allow,deny\n");
2134 if (remote_admin
|| share_printers
)
2135 cupsFilePuts(temp
, " Allow @LOCAL\n");
2137 cupsFilePuts(temp
, " Allow localhost\n");
2139 cupsFilePuts(temp
, "</Location>\n");
2142 if (!wrote_admin_location
)
2145 cupsFilePuts(temp
, "# Allow remote administration...\n");
2147 cupsFilePuts(temp
, "# Restrict access to the admin pages...\n");
2149 cupsFilePuts(temp
, "<Location /admin>\n"
2150 " Order allow,deny\n");
2153 cupsFilePuts(temp
, " Allow @LOCAL\n");
2155 cupsFilePuts(temp
, " Allow localhost\n");
2157 cupsFilePuts(temp
, "</Location>\n");
2160 if (!wrote_conf_location
)
2163 cupsFilePuts(temp
, "# Allow remote access to the configuration files...\n");
2165 cupsFilePuts(temp
, "# Restrict access to the configuration files...\n");
2167 cupsFilePuts(temp
, "<Location /admin/conf>\n"
2169 " Require user @SYSTEM\n"
2170 " Order allow,deny\n");
2173 cupsFilePuts(temp
, " Allow @LOCAL\n");
2175 cupsFilePuts(temp
, " Allow localhost\n");
2177 cupsFilePuts(temp
, "</Location>\n");
2182 cupsFilePuts(temp
, "<Policy default>\n"
2183 " # Job-related operations must be done by the owner or an adminstrator...\n"
2184 " <Limit Send-Document Send-URI Hold-Job Release-Job "
2185 "Restart-Job Purge-Jobs Set-Job-Attributes "
2186 "Create-Job-Subscription Renew-Subscription "
2187 "Cancel-Subscription Get-Notifications Reprocess-Job "
2188 "Cancel-Current-Job Suspend-Current-Job Resume-Job "
2190 " Require user @OWNER @SYSTEM\n"
2191 " Order deny,allow\n"
2193 " # All administration operations require an adminstrator to authenticate...\n"
2194 " <Limit Pause-Printer Resume-Printer "
2195 "Set-Printer-Attributes Enable-Printer "
2196 "Disable-Printer Pause-Printer-After-Current-Job "
2197 "Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer "
2198 "Activate-Printer Restart-Printer Shutdown-Printer "
2199 "Startup-Printer Promote-Job Schedule-Job-After "
2200 "CUPS-Add-Printer CUPS-Delete-Printer "
2201 "CUPS-Add-Class CUPS-Delete-Class "
2202 "CUPS-Accept-Jobs CUPS-Reject-Jobs "
2203 "CUPS-Set-Default CUPS-Add-Device CUPS-Delete-Device>\n"
2205 " Require user @SYSTEM\n"
2206 " Order deny,allow\n"
2209 if (!user_cancel_any
)
2210 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
2211 " <Limit Cancel-Job>\n"
2212 " Require user @OWNER @SYSTEM\n"
2213 " Order deny,allow\n"
2216 cupsFilePuts(temp
, " <Limit All>\n"
2217 " Order deny,allow\n"
2222 cupsFileClose(cupsd
);
2223 cupsFileClose(temp
);
2226 * Upload the configuration file to the server...
2229 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
2231 if (status
!= HTTP_CREATED
)
2233 cgiSetVariable("ERROR", httpStatus(status
));
2234 cgiStartHTML("Change Settings");
2235 cgiCopyTemplateLang("error.tmpl");
2239 cgiSetVariable("refresh_page", "5;/admin?OP=redirect");
2241 cgiStartHTML("Change Settings");
2242 cgiCopyTemplateLang("restart.tmpl");
2249 else if (cgiIsPOST())
2252 * Save hand-edited config file...
2255 http_status_t status
; /* PUT status */
2256 char tempfile
[1024]; /* Temporary new cupsd.conf */
2257 int tempfd
; /* Temporary file descriptor */
2258 cups_file_t
*temp
; /* Temporary file */
2259 const char *start
, /* Start of line */
2260 *end
; /* End of line */
2264 * Create a temporary file for the new cupsd.conf file...
2267 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
2269 cgiStartHTML("Edit Configuration File");
2270 cgiSetVariable("ERROR", strerror(errno
));
2271 cgiCopyTemplateLang("error.tmpl");
2278 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
2280 cgiStartHTML("Edit Configuration File");
2281 cgiSetVariable("ERROR", strerror(errno
));
2282 cgiCopyTemplateLang("error.tmpl");
2292 * Copy the cupsd.conf text from the form variable...
2295 start
= cgiGetVariable("CUPSDCONF");
2298 if ((end
= strstr(start
, "\r\n")) == NULL
)
2299 if ((end
= strstr(start
, "\n")) == NULL
)
2300 end
= start
+ strlen(start
);
2302 cupsFileWrite(temp
, start
, end
- start
);
2303 cupsFilePutChar(temp
, '\n');
2307 else if (*end
== '\n')
2313 cupsFileClose(temp
);
2316 * Upload the configuration file to the server...
2319 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
2321 if (status
!= HTTP_CREATED
)
2323 cgiSetVariable("ERROR", httpStatus(status
));
2324 cgiStartHTML("Edit Configuration File");
2325 cgiCopyTemplateLang("error.tmpl");
2329 cgiSetVariable("refresh_page", "5;/admin?OP=redirect");
2331 cgiStartHTML("Edit Configuration File");
2332 cgiCopyTemplateLang("restart.tmpl");
2341 struct stat info
; /* cupsd.conf information */
2342 cups_file_t
*cupsd
; /* cupsd.conf file */
2343 char *buffer
; /* Buffer for entire file */
2344 char filename
[1024]; /* Filename */
2345 const char *server_root
; /* Location of config files */
2349 * Locate the cupsd.conf file...
2352 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
2353 server_root
= CUPS_SERVERROOT
;
2355 snprintf(filename
, sizeof(filename
), "%s/cupsd.conf", server_root
);
2358 * Figure out the size...
2361 if (stat(filename
, &info
))
2363 cgiStartHTML("Edit Configuration File");
2364 cgiSetVariable("ERROR", strerror(errno
));
2365 cgiCopyTemplateLang("error.tmpl");
2372 if (info
.st_size
> (1024 * 1024))
2374 cgiStartHTML("Edit Configuration File");
2375 cgiSetVariable("ERROR", "Unable to edit cupsd.conf files larger than 1MB!");
2376 cgiCopyTemplateLang("error.tmpl");
2379 fprintf(stderr
, "ERROR: \"%s\" too large (%ld) to edit!\n", filename
,
2380 (long)info
.st_size
);
2385 * Open the cupsd.conf file...
2388 if ((cupsd
= cupsFileOpen(filename
, "r")) == NULL
)
2391 * Unable to open - log an error...
2394 cgiStartHTML("Edit Configuration File");
2395 cgiSetVariable("ERROR", strerror(errno
));
2396 cgiCopyTemplateLang("error.tmpl");
2404 * Allocate memory and load the file into a string buffer...
2407 buffer
= calloc(1, info
.st_size
+ 1);
2409 cupsFileRead(cupsd
, buffer
, info
.st_size
);
2410 cupsFileClose(cupsd
);
2412 cgiSetVariable("CUPSDCONF", buffer
);
2416 * Show the current config file...
2419 cgiStartHTML("Edit Configuration File");
2421 printf("<!-- \"%s\" -->\n", filename
);
2423 cgiCopyTemplateLang("edit-config.tmpl");
2431 * 'do_delete_class()' - Delete a class...
2435 do_delete_class(http_t
*http
, /* I - HTTP connection */
2436 cups_lang_t
*language
) /* I - Client's language */
2438 ipp_t
*request
, /* IPP request */
2439 *response
; /* IPP response */
2440 char uri
[HTTP_MAX_URI
]; /* Job URI */
2441 const char *pclass
; /* Printer class name */
2442 ipp_status_t status
; /* Operation status... */
2445 if (cgiGetVariable("CONFIRM") == NULL
)
2447 cgiStartHTML("Delete Class");
2448 cgiCopyTemplateLang("class-confirm.tmpl");
2453 if ((pclass
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2454 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
2455 "/classes/%s", pclass
);
2458 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND
));
2459 cgiStartHTML("Delete Class");
2460 cgiCopyTemplateLang("error.tmpl");
2466 * Build a CUPS_DELETE_CLASS request, which requires the following
2469 * attributes-charset
2470 * attributes-natural-language
2476 request
->request
.op
.operation_id
= CUPS_DELETE_CLASS
;
2477 request
->request
.op
.request_id
= 1;
2479 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
2480 "attributes-charset", NULL
, cupsLangEncoding(language
));
2482 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
2483 "attributes-natural-language", NULL
, language
->language
);
2485 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2489 * Do the request and get back a response...
2492 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
2494 status
= response
->request
.status
.status_code
;
2496 ippDelete(response
);
2499 status
= cupsLastError();
2501 cgiStartHTML("Delete Class");
2503 if (status
> IPP_OK_CONFLICT
)
2505 cgiSetVariable("ERROR", ippErrorString(status
));
2506 cgiCopyTemplateLang("error.tmpl");
2509 cgiCopyTemplateLang("class-deleted.tmpl");
2516 * 'do_delete_printer()' - Delete a printer...
2520 do_delete_printer(http_t
*http
, /* I - HTTP connection */
2521 cups_lang_t
*language
)/* I - Client's language */
2523 ipp_t
*request
, /* IPP request */
2524 *response
; /* IPP response */
2525 char uri
[HTTP_MAX_URI
]; /* Job URI */
2526 const char *printer
; /* Printer printer name */
2527 ipp_status_t status
; /* Operation status... */
2530 if (cgiGetVariable("CONFIRM") == NULL
)
2532 cgiStartHTML("Delete Printer");
2533 cgiCopyTemplateLang("printer-confirm.tmpl");
2538 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2539 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
2540 "/printers/%s", printer
);
2543 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND
));
2544 cgiStartHTML("Delete Printer");
2545 cgiCopyTemplateLang("error.tmpl");
2551 * Build a CUPS_DELETE_PRINTER request, which requires the following
2554 * attributes-charset
2555 * attributes-natural-language
2561 request
->request
.op
.operation_id
= CUPS_DELETE_PRINTER
;
2562 request
->request
.op
.request_id
= 1;
2564 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
2565 "attributes-charset", NULL
, cupsLangEncoding(language
));
2567 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
2568 "attributes-natural-language", NULL
, language
->language
);
2570 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2574 * Do the request and get back a response...
2577 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
2579 status
= response
->request
.status
.status_code
;
2581 ippDelete(response
);
2584 status
= cupsLastError();
2586 cgiStartHTML("Delete Printer");
2588 if (status
> IPP_OK_CONFLICT
)
2590 cgiSetVariable("ERROR", ippErrorString(status
));
2591 cgiCopyTemplateLang("error.tmpl");
2594 cgiCopyTemplateLang("printer-deleted.tmpl");
2601 * 'do_menu()' - Show the main menu...
2605 do_menu(http_t
*http
, /* I - HTTP connection */
2606 cups_lang_t
*language
) /* I - Client's language */
2608 cups_file_t
*cupsd
; /* cupsd.conf file */
2609 char line
[1024], /* Line from cupsd.conf file */
2610 *value
; /* Value on line */
2611 const char *server_root
; /* Location of config files */
2612 ipp_t
*request
, /* IPP request */
2613 *response
; /* IPP response */
2614 ipp_attribute_t
*attr
; /* IPP attribute */
2618 * Locate the cupsd.conf file...
2621 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
2622 server_root
= CUPS_SERVERROOT
;
2624 snprintf(line
, sizeof(line
), "%s/cupsd.conf", server_root
);
2626 cgiStartHTML("Administration");
2628 printf("<!-- \"%s\" -->\n", line
);
2631 * Open the cupsd.conf file...
2634 if ((cupsd
= cupsFileOpen(line
, "r")) == NULL
)
2637 * Unable to open - log an error...
2640 cgiStartHTML("Administration");
2641 cgiSetVariable("ERROR", strerror(errno
));
2642 cgiCopyTemplateLang("error.tmpl");
2650 * Read the file, keeping track of what settings are enabled...
2653 int remote_access
= 0, /* Remote access allowed? */
2654 remote_admin
= 0, /* Remote administration allowed? */
2655 browsing
= 1, /* Browsing enabled? */
2656 browse_allow
= 1, /* Browse address set? */
2657 browse_address
= 0, /* Browse address set? */
2658 cancel_policy
= 1, /* Cancel-job policy set? */
2659 debug_logging
= 0; /* LogLevel debug set? */
2660 int linenum
= 0, /* Line number in file */
2661 in_policy
= 0, /* In a policy section? */
2662 in_cancel_job
= 0, /* In a cancel-job section? */
2663 in_admin_location
= 0; /* In the /admin location? */
2666 while (cupsFileGetConf(cupsd
, line
, sizeof(line
), &value
, &linenum
))
2668 if (!strcasecmp(line
, "Port"))
2672 else if (!strcasecmp(line
, "Listen"))
2674 char *port
; /* Pointer to port number, if any */
2677 if ((port
= strrchr(value
, ':')) != NULL
)
2680 if (strcasecmp(value
, "localhost") && strcmp(value
, "127.0.0.1"))
2683 else if (!strcasecmp(line
, "Browsing"))
2685 browsing
= !strcasecmp(value
, "yes") || !strcasecmp(value
, "on") ||
2686 !strcasecmp(value
, "true");
2688 else if (!strcasecmp(line
, "BrowseAddress"))
2692 else if (!strcasecmp(line
, "BrowseAllow"))
2696 else if (!strcasecmp(line
, "BrowseOrder"))
2698 browse_allow
= !strncasecmp(value
, "deny,", 5);
2700 else if (!strcasecmp(line
, "LogLevel"))
2702 debug_logging
= !strncasecmp(value
, "debug", 5);
2704 else if (!strcasecmp(line
, "<Policy") && !strcasecmp(value
, "default"))
2708 else if (!strcasecmp(line
, "</Policy>"))
2712 else if (!strcasecmp(line
, "<Limit") && in_policy
)
2715 * See if the policy limit is for the Cancel-Job operation...
2718 char *valptr
; /* Pointer into value */
2723 for (valptr
= value
; !isspace(*valptr
& 255) && *valptr
; valptr
++);
2728 if (!strcasecmp(value
, "cancel-job") || !strcasecmp(value
, "all"))
2734 for (value
= valptr
; isspace(*value
& 255); value
++);
2737 else if (!strcasecmp(line
, "</Limit>"))
2741 else if (!strcasecmp(line
, "Require") && in_cancel_job
)
2745 else if (!strcasecmp(line
, "<Location") && !strcasecmp(value
, "/admin"))
2747 in_admin_location
= 1;
2749 else if (!strcasecmp(line
, "</Location>"))
2751 in_admin_location
= 0;
2753 else if (!strcasecmp(line
, "Allow") && in_admin_location
&&
2754 strcasecmp(value
, "localhost") && strcasecmp(value
, "127.0.0.1"))
2760 cupsFileClose(cupsd
);
2762 if (browsing
&& browse_allow
)
2763 cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
2765 if (remote_access
&& browsing
&& browse_address
)
2766 cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2768 if (remote_access
&& remote_admin
)
2769 cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2772 cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2775 cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2779 * Get the list of printers and their devices...
2783 request
->request
.op
.operation_id
= CUPS_GET_PRINTERS
;
2784 request
->request
.op
.request_id
= 1;
2786 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
2787 "attributes-charset", NULL
, cupsLangEncoding(language
));
2789 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
2790 "attributes-natural-language", NULL
, language
->language
);
2792 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2793 "requested-attributes", NULL
, "device-uri");
2795 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type",
2796 CUPS_PRINTER_LOCAL
);
2797 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type-mask",
2798 CUPS_PRINTER_LOCAL
);
2800 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2803 * Got the printer list, now load the devices...
2806 int i
; /* Looping var */
2807 int num_printer_devices
; /* Number of devices for local printers */
2808 char **printer_devices
; /* Printer devices for local printers */
2812 * Count the number of printers we have...
2815 for (num_printer_devices
= 0,
2816 attr
= ippFindAttribute(response
, "device-uri", IPP_TAG_URI
);
2818 num_printer_devices
++,
2819 attr
= ippFindNextAttribute(response
, "device-uri", IPP_TAG_URI
));
2821 if (num_printer_devices
> 0)
2824 * Allocate an array and copy the device strings...
2827 printer_devices
= calloc(num_printer_devices
, sizeof(char *));
2829 for (i
= 0, attr
= ippFindAttribute(response
, "device-uri", IPP_TAG_URI
);
2831 i
++, attr
= ippFindNextAttribute(response
, "device-uri", IPP_TAG_URI
))
2833 printer_devices
[i
] = strdup(attr
->values
[0].string
.text
);
2837 * Sort the printer devices as needed...
2840 if (num_printer_devices
> 1)
2841 qsort(printer_devices
, num_printer_devices
, sizeof(char *),
2842 compare_printer_devices
);
2845 printer_devices
= NULL
;
2848 * Free the printer list and get the device list...
2851 ippDelete(response
);
2854 request
->request
.op
.operation_id
= CUPS_GET_DEVICES
;
2855 request
->request
.op
.request_id
= 1;
2857 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
2858 "attributes-charset", NULL
, cupsLangEncoding(language
));
2860 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
2861 "attributes-natural-language", NULL
, language
->language
);
2863 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2866 * Got the device list, let's parse it...
2869 const char *device_uri
, /* device-uri attribute value */
2870 *device_make_and_model
, /* device-make-and-model value */
2871 *device_info
; /* device-info value */
2874 for (i
= 0, attr
= response
->attrs
; attr
; attr
= attr
->next
)
2877 * Skip leading attributes until we hit a device...
2880 while (attr
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
2887 * Pull the needed attributes from this device...
2891 device_make_and_model
= NULL
;
2894 while (attr
&& attr
->group_tag
== IPP_TAG_PRINTER
)
2896 if (strcmp(attr
->name
, "device-info") == 0 &&
2897 attr
->value_tag
== IPP_TAG_TEXT
)
2898 device_info
= attr
->values
[0].string
.text
;
2900 if (strcmp(attr
->name
, "device-make-and-model") == 0 &&
2901 attr
->value_tag
== IPP_TAG_TEXT
)
2902 device_make_and_model
= attr
->values
[0].string
.text
;
2904 if (strcmp(attr
->name
, "device-uri") == 0 &&
2905 attr
->value_tag
== IPP_TAG_URI
)
2906 device_uri
= attr
->values
[0].string
.text
;
2912 * See if we have everything needed...
2915 if (device_info
&& device_make_and_model
&& device_uri
&&
2916 strcasecmp(device_make_and_model
, "unknown") &&
2917 strchr(device_uri
, ':'))
2920 * Yes, now see if there is already a printer for this
2924 if (!bsearch(&device_uri
, printer_devices
, num_printer_devices
,
2925 sizeof(char *), compare_printer_devices
))
2928 * Not found, so it must be a new printer...
2931 char options
[1024], /* Form variables for this device */
2932 *options_ptr
; /* Pointer into string */
2933 const char *ptr
; /* Pointer into device string */
2937 * Format the printer name variable for this device...
2939 * We use the device-info string first, then device-uri,
2940 * and finally device-make-and-model to come up with a
2944 strcpy(options
, "PRINTER_NAME=");
2945 options_ptr
= options
+ strlen(options
);
2947 if (strncasecmp(device_info
, "unknown", 7))
2949 else if ((ptr
= strstr(device_uri
, "://")) != NULL
)
2952 ptr
= device_make_and_model
;
2955 options_ptr
< (options
+ sizeof(options
) - 1) && *ptr
;
2957 if (isalnum(*ptr
& 255) || *ptr
== '_' || *ptr
== '-' || *ptr
== '.')
2958 *options_ptr
++ = *ptr
;
2959 else if ((*ptr
== ' ' || *ptr
== '/') && options_ptr
[-1] != '_')
2960 *options_ptr
++ = '_';
2961 else if (*ptr
== '?' || *ptr
== '(')
2965 * Then add the make and model in the printer info, so
2966 * that MacOS clients see something reasonable...
2969 strlcpy(options_ptr
, "&PRINTER_LOCATION=Local+Printer"
2971 sizeof(options
) - (options_ptr
- options
));
2972 options_ptr
+= strlen(options_ptr
);
2974 cgiFormEncode(options_ptr
, device_make_and_model
,
2975 sizeof(options
) - (options_ptr
- options
));
2976 options_ptr
+= strlen(options_ptr
);
2979 * Then copy the device URI...
2982 strlcpy(options_ptr
, "&DEVICE_URI=",
2983 sizeof(options
) - (options_ptr
- options
));
2984 options_ptr
+= strlen(options_ptr
);
2986 cgiFormEncode(options_ptr
, device_uri
,
2987 sizeof(options
) - (options_ptr
- options
));
2988 options_ptr
+= strlen(options_ptr
);
2990 if (options_ptr
< (options
+ sizeof(options
) - 1))
2992 *options_ptr
++ = '|';
2993 cgiFormEncode(options_ptr
, device_make_and_model
,
2994 sizeof(options
) - (options_ptr
- options
));
2998 * Finally, set the form variables for this printer...
3001 cgiSetArray("device_info", i
, device_info
);
3002 cgiSetArray("device_make_and_model", i
, device_make_and_model
);
3003 cgiSetArray("device_options", i
, options
);
3004 cgiSetArray("device_uri", i
, device_uri
);
3014 * Free the device list...
3017 ippDelete(response
);
3019 if (num_printer_devices
)
3020 free(printer_devices
);
3025 * Finally, show the main menu template...
3028 cgiCopyTemplateLang("admin.tmpl");
3035 * 'do_printer_op()' - Do a printer operation.
3039 do_printer_op(http_t
*http
, /* I - HTTP connection */
3040 cups_lang_t
*language
, /* I - Client's language */
3041 ipp_op_t op
, /* I - Operation to perform */
3042 const char *title
) /* I - Title of page */
3044 ipp_t
*request
, /* IPP request */
3045 *response
; /* IPP response */
3046 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3047 const char *printer
; /* Printer name (purge-jobs) */
3048 ipp_status_t status
; /* Operation status... */
3051 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
3052 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
3053 "/printers/%s", printer
);
3056 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND
));
3057 cgiStartHTML(title
);
3058 cgiCopyTemplateLang("error.tmpl");
3064 * Build a printer request, which requires the following
3067 * attributes-charset
3068 * attributes-natural-language
3074 request
->request
.op
.operation_id
= op
;
3075 request
->request
.op
.request_id
= 1;
3077 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
3078 "attributes-charset", NULL
, cupsLangEncoding(language
));
3080 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
3081 "attributes-natural-language", NULL
, language
->language
);
3083 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3087 * Do the request and get back a response...
3090 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3092 status
= response
->request
.status
.status_code
;
3094 ippDelete(response
);
3097 status
= cupsLastError();
3099 if (status
> IPP_OK_CONFLICT
)
3101 cgiStartHTML(title
);
3102 cgiSetVariable("ERROR", ippErrorString(status
));
3103 cgiCopyTemplateLang("error.tmpl");
3108 * Redirect successful updates back to the printer page...
3111 char refresh
[1024]; /* Refresh URL */
3113 cgiFormEncode(uri
, printer
, sizeof(uri
));
3114 snprintf(refresh
, sizeof(refresh
), "5;/admin?OP=redirect&URL=/printers/%s",
3116 cgiSetVariable("refresh_page", refresh
);
3118 cgiStartHTML(title
);
3120 if (op
== IPP_PAUSE_PRINTER
)
3121 cgiCopyTemplateLang("printer-stop.tmpl");
3122 else if (op
== IPP_RESUME_PRINTER
)
3123 cgiCopyTemplateLang("printer-start.tmpl");
3124 else if (op
== CUPS_ACCEPT_JOBS
)
3125 cgiCopyTemplateLang("printer-accept.tmpl");
3126 else if (op
== CUPS_REJECT_JOBS
)
3127 cgiCopyTemplateLang("printer-reject.tmpl");
3128 else if (op
== IPP_PURGE_JOBS
)
3129 cgiCopyTemplateLang("printer-purge.tmpl");
3130 else if (op
== CUPS_SET_DEFAULT
)
3131 cgiCopyTemplateLang("printer-default.tmpl");
3139 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
3143 do_set_allowed_users(
3144 http_t
*http
, /* I - HTTP connection */
3145 cups_lang_t
*language
) /* I - Language */
3147 int i
; /* Looping var */
3148 ipp_t
*request
, /* IPP request */
3149 *response
; /* IPP response */
3150 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3151 const char *printer
, /* Printer name (purge-jobs) */
3152 *users
, /* List of users or groups */
3153 *type
; /* Allow/deny type */
3154 int num_users
; /* Number of users */
3155 char *ptr
, /* Pointer into users string */
3156 *end
, /* Pointer to end of users string */
3157 quote
; /* Quote character */
3158 ipp_attribute_t
*attr
; /* Attribute */
3159 ipp_status_t status
; /* Operation status... */
3160 static const char * const attrs
[] = /* Requested attributes */
3162 "requesting-user-name-allowed",
3163 "requesting-user-name-denied"
3167 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
3168 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
3169 "/printers/%s", printer
);
3172 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND
));
3173 cgiStartHTML("Set Allowed Users");
3174 cgiCopyTemplateLang("error.tmpl");
3179 users
= cgiGetVariable("users");
3180 type
= cgiGetVariable("type");
3182 if (!users
|| !type
||
3183 (strcmp(type
, "requesting-user-name-allowed") &&
3184 strcmp(type
, "requesting-user-name-denied")))
3187 * Build a Get-Printer-Attributes request, which requires the following
3190 * attributes-charset
3191 * attributes-natural-language
3193 * requested-attributes
3198 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
3199 request
->request
.op
.request_id
= 1;
3201 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
3202 "attributes-charset", NULL
, cupsLangEncoding(language
));
3204 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
3205 "attributes-natural-language", NULL
, language
->language
);
3207 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3210 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
3211 "requested-attributes",
3212 (int)(sizeof(attrs
) / sizeof(attrs
[0])), NULL
, attrs
);
3215 * Do the request and get back a response...
3218 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3220 status
= response
->request
.status
.status_code
;
3222 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3224 ippDelete(response
);
3227 status
= cupsLastError();
3229 cgiStartHTML("Set Allowed Users");
3231 if (status
> IPP_OK_CONFLICT
)
3233 cgiSetVariable("ERROR", ippErrorString(status
));
3234 cgiCopyTemplateLang("error.tmpl");
3237 cgiCopyTemplateLang("users.tmpl");
3244 * Save the changes...
3247 for (num_users
= 0, ptr
= (char *)users
; *ptr
; num_users
++)
3250 * Skip whitespace and commas...
3253 while (*ptr
== ',' || isspace(*ptr
& 255))
3256 if (*ptr
== '\'' || *ptr
== '\"')
3259 * Scan quoted name...
3264 for (end
= ptr
; *end
; end
++)
3271 * Scan space or comma-delimited name...
3274 for (end
= ptr
; *end
; end
++)
3275 if (isspace(*end
& 255) || *end
== ',')
3280 * Advance to the next name...
3287 * Build a CUPS-Add-Printer request, which requires the following
3290 * attributes-charset
3291 * attributes-natural-language
3293 * requesting-user-name-{allowed,denied}
3298 request
->request
.op
.operation_id
= CUPS_ADD_PRINTER
;
3299 request
->request
.op
.request_id
= 1;
3301 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
3302 "attributes-charset", NULL
, cupsLangEncoding(language
));
3304 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
3305 "attributes-natural-language", NULL
, language
->language
);
3307 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3311 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
3312 "requesting-user-name-allowed", NULL
, "all");
3315 attr
= ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
3316 type
, num_users
, NULL
, NULL
);
3318 for (i
= 0, ptr
= (char *)users
; *ptr
; i
++)
3321 * Skip whitespace and commas...
3324 while (*ptr
== ',' || isspace(*ptr
& 255))
3327 if (*ptr
== '\'' || *ptr
== '\"')
3330 * Scan quoted name...
3335 for (end
= ptr
; *end
; end
++)
3342 * Scan space or comma-delimited name...
3345 for (end
= ptr
; *end
; end
++)
3346 if (isspace(*end
& 255) || *end
== ',')
3351 * Terminate the name...
3361 attr
->values
[i
].string
.text
= strdup(ptr
);
3364 * Advance to the next name...
3372 * Do the request and get back a response...
3375 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3377 status
= response
->request
.status
.status_code
;
3379 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3381 ippDelete(response
);
3384 status
= cupsLastError();
3386 if (status
> IPP_OK_CONFLICT
)
3388 cgiStartHTML("Set Allowed Users");
3389 cgiSetVariable("ERROR", ippErrorString(status
));
3390 cgiCopyTemplateLang("error.tmpl");
3395 * Redirect successful updates back to the printer page...
3398 char refresh
[1024]; /* Refresh URL */
3400 cgiFormEncode(uri
, printer
, sizeof(uri
));
3401 snprintf(refresh
, sizeof(refresh
), "5;/admin?OP=redirect&URL=/printers/%s",
3403 cgiSetVariable("refresh_page", refresh
);
3405 cgiStartHTML("Set Allowed Users");
3407 cgiCopyTemplateLang("printer-modified.tmpl");
3416 * 'do_set_sharing()' - Set printer-is-shared value...
3420 do_set_sharing(http_t
*http
, /* I - HTTP connection */
3421 cups_lang_t
*language
) /* I - Language */
3423 ipp_t
*request
, /* IPP request */
3424 *response
; /* IPP response */
3425 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3426 const char *printer
, /* Printer name */
3427 *shared
; /* Sharing value */
3428 ipp_status_t status
; /* Operation status... */
3431 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
3432 httpAssembleURIf(uri
, sizeof(uri
), "ipp", NULL
, "localhost", 0,
3433 "/printers/%s", printer
);
3436 cgiSetVariable("ERROR", ippErrorString(IPP_NOT_FOUND
));
3437 cgiStartHTML("Set Publishing");
3438 cgiCopyTemplateLang("error.tmpl");
3443 if ((shared
= cgiGetVariable("SHARED")) == NULL
)
3445 cgiSetVariable("ERROR", "Missing SHARED parameter");
3446 cgiStartHTML("Set Publishing");
3447 cgiCopyTemplateLang("error.tmpl");
3453 * Build a CUPS-Add-Printer request, which requires the following
3456 * attributes-charset
3457 * attributes-natural-language
3464 request
->request
.op
.operation_id
= CUPS_ADD_PRINTER
;
3465 request
->request
.op
.request_id
= 1;
3467 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
3468 "attributes-charset", NULL
, cupsLangEncoding(language
));
3470 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
3471 "attributes-natural-language", NULL
, language
->language
);
3473 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3476 ippAddBoolean(request
, IPP_TAG_OPERATION
, "printer-is-shared", atoi(shared
));
3479 * Do the request and get back a response...
3482 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3484 status
= response
->request
.status
.status_code
;
3486 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3488 ippDelete(response
);
3491 status
= cupsLastError();
3493 if (status
> IPP_OK_CONFLICT
)
3495 cgiStartHTML("Set Publishing");
3496 cgiSetVariable("ERROR", ippErrorString(status
));
3497 cgiCopyTemplateLang("error.tmpl");
3502 * Redirect successful updates back to the printer page...
3505 char refresh
[1024]; /* Refresh URL */
3507 cgiFormEncode(uri
, printer
, sizeof(uri
));
3508 snprintf(refresh
, sizeof(refresh
), "5;/admin?OP=redirect&URL=/printers/%s",
3510 cgiSetVariable("refresh_page", refresh
);
3512 cgiStartHTML("Set Publishing");
3514 cgiCopyTemplateLang("printer-modified.tmpl");
3522 * 'match_string()' - Return the number of matching characters.
3525 static int /* O - Number of matching characters */
3526 match_string(const char *a
, /* I - First string */
3527 const char *b
) /* I - Second string */
3529 int count
; /* Number of matching characters */
3533 * Loop through both strings until we hit the end of either or we find
3534 * a non-matching character. For the purposes of comparison, we ignore
3535 * whitespace and do a case-insensitive comparison so that we have a
3536 * better chance of finding a match...
3539 for (count
= 0; *a
&& *b
; a
++, b
++, count
++)
3542 * Skip leading whitespace characters...
3545 while (isspace(*a
& 255))
3548 while (isspace(*b
& 255))
3552 * Break out if we run out of characters...
3559 * Do a case-insensitive comparison of the next two chars...
3562 if (tolower(*a
& 255) != tolower(*b
& 255))
3571 * End of "$Id: admin.c 4921 2006-01-12 21:26:26Z mike $".