2 * "$Id: admin.c 5107 2006-02-15 19:14:17Z 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 * do_am_class() - Add or modify a class.
28 * do_am_printer() - Add or modify a printer.
29 * do_config_printer() - Configure the default options for a printer.
30 * do_config_server() - Configure server settings.
31 * do_delete_class() - Delete a class...
32 * do_delete_printer() - Delete a printer...
33 * do_export() - Export printers to Samba...
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>
57 static void do_am_class(http_t
*http
, int modify
);
58 static void do_am_printer(http_t
*http
, int modify
);
59 static void do_config_printer(http_t
*http
);
60 static void do_config_server(http_t
*http
);
61 static void do_delete_class(http_t
*http
);
62 static void do_delete_printer(http_t
*http
);
63 static void do_export(http_t
*http
);
64 static void do_menu(http_t
*http
);
65 static void do_printer_op(http_t
*http
,
66 ipp_op_t op
, const char *title
);
67 static void do_set_allowed_users(http_t
*http
);
68 static void do_set_sharing(http_t
*http
);
69 static int match_string(const char *a
, const char *b
);
73 * 'main()' - Main entry for CGI.
76 int /* O - Exit status */
77 main(int argc
, /* I - Number of command-line arguments */
78 char *argv
[]) /* I - Command-line arguments */
80 http_t
*http
; /* Connection to the server */
81 const char *op
; /* Operation name */
85 * Connect to the HTTP server...
88 fputs("DEBUG: admin.cgi started...\n", stderr
);
90 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
94 perror("ERROR: Unable to connect to cupsd");
95 fprintf(stderr
, "DEBUG: cupsServer()=\"%s\"\n", cupsServer());
96 fprintf(stderr
, "DEBUG: ippPort()=%d\n", ippPort());
97 fprintf(stderr
, "DEBUG: cupsEncryption()=%d\n", cupsEncryption());
101 fprintf(stderr
, "DEBUG: http=%p\n", http
);
104 * Set the web interface section...
107 cgiSetVariable("SECTION", "admin");
110 * See if we have form data...
113 if (!cgiInitialize())
116 * Nope, send the administration menu...
119 fputs("DEBUG: No form data, showing main menu...\n", stderr
);
123 else if ((op
= cgiGetVariable("OP")) != NULL
)
126 * Do the operation...
129 fprintf(stderr
, "DEBUG: op=\"%s\"...\n", op
);
131 if (!strcmp(op
, "redirect"))
133 const char *url
; /* Redirection URL... */
136 if ((url
= cgiGetVariable("URL")) != NULL
)
137 printf("Location: %s\n\n", url
);
139 puts("Location: /admin\n");
141 else if (!strcmp(op
, "start-printer"))
142 do_printer_op(http
, IPP_RESUME_PRINTER
, cgiText(_("Start Printer")));
143 else if (!strcmp(op
, "stop-printer"))
144 do_printer_op(http
, IPP_PAUSE_PRINTER
, cgiText(_("Stop Printer")));
145 else if (!strcmp(op
, "start-class"))
146 do_printer_op(http
, IPP_RESUME_PRINTER
, cgiText(_("Start Class")));
147 else if (!strcmp(op
, "stop-class"))
148 do_printer_op(http
, IPP_PAUSE_PRINTER
, cgiText(_("Stop Class")));
149 else if (!strcmp(op
, "accept-jobs"))
150 do_printer_op(http
, CUPS_ACCEPT_JOBS
, cgiText(_("Accept Jobs")));
151 else if (!strcmp(op
, "reject-jobs"))
152 do_printer_op(http
, CUPS_REJECT_JOBS
, cgiText(_("Reject Jobs")));
153 else if (!strcmp(op
, "purge-jobs"))
154 do_printer_op(http
, IPP_PURGE_JOBS
, cgiText(_("Purge Jobs")));
155 else if (!strcmp(op
, "set-allowed-users"))
156 do_set_allowed_users(http
);
157 else if (!strcmp(op
, "set-as-default"))
158 do_printer_op(http
, CUPS_SET_DEFAULT
, cgiText(_("Set As Default")));
159 else if (!strcmp(op
, "set-sharing"))
160 do_set_sharing(http
);
161 else if (!strcmp(op
, "add-class"))
162 do_am_class(http
, 0);
163 else if (!strcmp(op
, "add-printer"))
164 do_am_printer(http
, 0);
165 else if (!strcmp(op
, "modify-class"))
166 do_am_class(http
, 1);
167 else if (!strcmp(op
, "modify-printer"))
168 do_am_printer(http
, 1);
169 else if (!strcmp(op
, "delete-class"))
170 do_delete_class(http
);
171 else if (!strcmp(op
, "delete-printer"))
172 do_delete_printer(http
);
173 else if (!strcmp(op
, "set-printer-options"))
174 do_config_printer(http
);
175 else if (!strcmp(op
, "config-server"))
176 do_config_server(http
);
177 else if (!strcmp(op
, "export-samba"))
182 * Bad operation code... Display an error...
185 cgiStartHTML(cgiText(_("Administration")));
186 cgiCopyTemplateLang("error-op.tmpl");
193 * Form data but no operation code... Display an error...
196 cgiStartHTML(cgiText(_("Administration")));
197 cgiCopyTemplateLang("error-op.tmpl");
202 * Close the HTTP server connection...
208 * Return with no errors...
216 * 'do_am_class()' - Add or modify a class.
220 do_am_class(http_t
*http
, /* I - HTTP connection */
221 int modify
) /* I - Modify the printer? */
223 int i
, j
; /* Looping vars */
224 int element
; /* Element number */
225 int num_printers
; /* Number of printers */
226 ipp_t
*request
, /* IPP request */
227 *response
; /* IPP response */
228 ipp_attribute_t
*attr
; /* member-uris attribute */
229 char uri
[HTTP_MAX_URI
]; /* Device or printer URI */
230 const char *name
, /* Pointer to class name */
231 *ptr
; /* Pointer to CGI variable */
232 const char *title
; /* Title of page */
233 static const char * const pattrs
[] = /* Requested printer attributes */
241 title
= cgiText(modify
? _("Modify Class") : _("Add Class"));
242 name
= cgiGetVariable("PRINTER_NAME");
244 if (cgiGetVariable("PRINTER_LOCATION") == NULL
)
247 * Build a CUPS_GET_PRINTERS request, which requires the
248 * following attributes:
251 * attributes-natural-language
255 request
= ippNewRequest(CUPS_GET_PRINTERS
);
257 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
258 NULL
, "ipp://localhost/printers");
261 * Do the request and get back a response...
264 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
267 * Create MEMBER_URIS and MEMBER_NAMES arrays...
270 for (element
= 0, attr
= response
->attrs
;
273 if (attr
->name
&& !strcmp(attr
->name
, "printer-uri-supported"))
275 if ((ptr
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
&&
276 (!name
|| strcasecmp(name
, ptr
+ 1)))
279 * Don't show the current class...
282 cgiSetArray("MEMBER_URIS", element
, attr
->values
[0].string
.text
);
287 for (element
= 0, attr
= response
->attrs
;
290 if (attr
->name
&& !strcmp(attr
->name
, "printer-name"))
292 if (!name
|| strcasecmp(name
, attr
->values
[0].string
.text
))
295 * Don't show the current class...
298 cgiSetArray("MEMBER_NAMES", element
, attr
->values
[0].string
.text
);
303 num_printers
= cgiGetSize("MEMBER_URIS");
313 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
314 * following attributes:
317 * attributes-natural-language
321 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
323 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
324 "localhost", 0, "/classes/%s", name
);
325 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
328 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
329 "requested-attributes",
330 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])),
334 * Do the request and get back a response...
337 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
339 if ((attr
= ippFindAttribute(response
, "member-names", IPP_TAG_NAME
)) != NULL
)
342 * Mark any current members in the class...
345 for (j
= 0; j
< num_printers
; j
++)
346 cgiSetArray("MEMBER_SELECTED", j
, "");
348 for (i
= 0; i
< attr
->num_values
; i
++)
350 for (j
= 0; j
< num_printers
; j
++)
352 if (!strcasecmp(attr
->values
[i
].string
.text
,
353 cgiGetArray("MEMBER_NAMES", j
)))
355 cgiSetArray("MEMBER_SELECTED", j
, "SELECTED");
362 if ((attr
= ippFindAttribute(response
, "printer-info",
363 IPP_TAG_TEXT
)) != NULL
)
364 cgiSetVariable("PRINTER_INFO", attr
->values
[0].string
.text
);
366 if ((attr
= ippFindAttribute(response
, "printer-location",
367 IPP_TAG_TEXT
)) != NULL
)
368 cgiSetVariable("PRINTER_LOCATION", attr
->values
[0].string
.text
);
374 * Update the location and description of an existing printer...
378 cgiCopyTemplateLang("modify-class.tmpl");
383 * Get the name, location, and description for a new printer...
387 cgiCopyTemplateLang("add-class.tmpl");
395 for (ptr
= name
; *ptr
; ptr
++)
396 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
399 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
401 cgiSetVariable("ERROR",
402 cgiText(_("The class name may only contain up to "
403 "127 printable characters and may not "
404 "contain spaces, slashes (/), or the "
405 "pound sign (#).")));
407 cgiCopyTemplateLang("error.tmpl");
413 * Build a CUPS_ADD_CLASS request, which requires the following
417 * attributes-natural-language
421 * printer-is-accepting-jobs
426 request
= ippNewRequest(CUPS_ADD_CLASS
);
428 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
429 "localhost", 0, "/classes/%s",
430 cgiGetVariable("PRINTER_NAME"));
431 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
434 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
435 NULL
, cgiGetVariable("PRINTER_LOCATION"));
437 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
438 NULL
, cgiGetVariable("PRINTER_INFO"));
440 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
442 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
445 if ((num_printers
= cgiGetSize("MEMBER_URIS")) > 0)
447 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "member-uris",
448 num_printers
, NULL
, NULL
);
449 for (i
= 0; i
< num_printers
; i
++)
450 attr
->values
[i
].string
.text
= strdup(cgiGetArray("MEMBER_URIS", i
));
454 * Do the request and get back a response...
457 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
459 if (cupsLastError() > IPP_OK_CONFLICT
)
462 cgiShowIPPError(modify
? _("Unable to modify class:") :
463 _("Unable to add class:"));
468 * Redirect successful updates back to the class page...
471 char refresh
[1024]; /* Refresh URL */
473 cgiFormEncode(uri
, name
, sizeof(uri
));
474 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=/classes/%s",
476 cgiSetVariable("refresh_page", refresh
);
481 cgiCopyTemplateLang("class-modified.tmpl");
483 cgiCopyTemplateLang("class-added.tmpl");
491 * 'do_am_printer()' - Add or modify a printer.
495 do_am_printer(http_t
*http
, /* I - HTTP connection */
496 int modify
) /* I - Modify the printer? */
498 int i
; /* Looping var */
499 int element
; /* Element number */
500 ipp_attribute_t
*attr
, /* Current attribute */
501 *last
; /* Last attribute */
502 ipp_t
*request
, /* IPP request */
503 *response
, /* IPP response */
504 *oldinfo
; /* Old printer information */
505 const cgi_file_t
*file
; /* Uploaded file, if any */
506 const char *var
; /* CGI variable */
507 char uri
[HTTP_MAX_URI
], /* Device or printer URI */
508 *uriptr
; /* Pointer into URI */
509 int maxrate
; /* Maximum baud rate */
510 char baudrate
[255]; /* Baud rate string */
511 const char *name
, /* Pointer to class name */
512 *ptr
; /* Pointer to CGI variable */
513 const char *title
; /* Title of page */
514 static int baudrates
[] = /* Baud rates */
529 fprintf(stderr
, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n",
530 cgiGetVariable("DEVICE_URI"));
532 title
= cgiText(modify
? _("Modify Printer") : _("Add Printer"));
537 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
538 * following attributes:
541 * attributes-natural-language
545 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
547 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
548 "localhost", 0, "/printers/%s",
549 cgiGetVariable("PRINTER_NAME"));
550 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
554 * Do the request and get back a response...
557 oldinfo
= cupsDoRequest(http
, request
, "/");
562 if ((name
= cgiGetVariable("PRINTER_NAME")) == NULL
||
563 cgiGetVariable("PRINTER_LOCATION") == NULL
)
570 * Update the location and description of an existing printer...
574 cgiSetIPPVars(oldinfo
, NULL
, NULL
, NULL
, 0);
576 cgiCopyTemplateLang("modify-printer.tmpl");
581 * Get the name, location, and description for a new printer...
584 cgiCopyTemplateLang("add-printer.tmpl");
595 for (ptr
= name
; *ptr
; ptr
++)
596 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
599 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
601 cgiSetVariable("ERROR",
602 cgiText(_("The printer name may only contain up to "
603 "127 printable characters and may not "
604 "contain spaces, slashes (/), or the "
605 "pound sign (#).")));
607 cgiCopyTemplateLang("error.tmpl");
616 fprintf(stderr
, "DEBUG: file->tempfile=%s\n", file
->tempfile
);
617 fprintf(stderr
, "DEBUG: file->name=%s\n", file
->name
);
618 fprintf(stderr
, "DEBUG: file->filename=%s\n", file
->filename
);
619 fprintf(stderr
, "DEBUG: file->mimetype=%s\n", file
->mimetype
);
622 if ((var
= cgiGetVariable("DEVICE_URI")) == NULL
)
625 * Build a CUPS_GET_DEVICES request, which requires the following
629 * attributes-natural-language
633 fputs("DEBUG: Getting list of devices...\n", stderr
);
635 request
= ippNewRequest(CUPS_GET_DEVICES
);
637 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
638 NULL
, "ipp://localhost/printers/");
641 * Do the request and get back a response...
644 fprintf(stderr
, "DEBUG: http=%p (%s)\n", http
, http
->hostname
);
646 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
648 fputs("DEBUG: Got device list!\n", stderr
);
650 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
655 "ERROR: CUPS-Get-Devices request failed with status %x: %s\n",
656 cupsLastError(), cupsLastErrorString());
659 * Let the user choose...
662 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
664 strlcpy(uri
, attr
->values
[0].string
.text
, sizeof(uri
));
665 if ((uriptr
= strchr(uri
, ':')) != NULL
&& strncmp(uriptr
, "://", 3) == 0)
668 cgiSetVariable("CURRENT_DEVICE_URI", attr
->values
[0].string
.text
);
669 cgiSetVariable("CURRENT_DEVICE_SCHEME", uri
);
673 cgiCopyTemplateLang("choose-device.tmpl");
676 else if (strchr(var
, '/') == NULL
)
678 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
681 * Set the current device URI for the form to the old one...
684 if (strncmp(attr
->values
[0].string
.text
, var
, strlen(var
)) == 0)
685 cgiSetVariable("DEVICE_URI", attr
->values
[0].string
.text
);
689 * User needs to set the full URI...
693 cgiCopyTemplateLang("choose-uri.tmpl");
696 else if (!strncmp(var
, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
699 * Need baud rate, parity, etc.
702 if ((var
= strchr(var
, '?')) != NULL
&&
703 strncmp(var
, "?baud=", 6) == 0)
704 maxrate
= atoi(var
+ 6);
708 for (i
= 0; i
< 10; i
++)
709 if (baudrates
[i
] > maxrate
)
713 sprintf(baudrate
, "%d", baudrates
[i
]);
714 cgiSetArray("BAUDRATES", i
, baudrate
);
718 cgiCopyTemplateLang("choose-serial.tmpl");
721 else if (!file
&& (var
= cgiGetVariable("PPD_NAME")) == NULL
)
726 * Get the PPD file...
729 int fd
; /* PPD file */
730 char filename
[1024]; /* PPD filename */
731 ppd_file_t
*ppd
; /* PPD information */
732 char buffer
[1024]; /* Buffer */
733 int bytes
; /* Number of bytes */
734 http_status_t get_status
; /* Status of GET */
737 /* TODO: Use cupsGetFile() API... */
738 snprintf(uri
, sizeof(uri
), "/printers/%s.ppd", name
);
740 if (httpGet(http
, uri
))
743 while ((get_status
= httpUpdate(http
)) == HTTP_CONTINUE
);
745 if (get_status
!= HTTP_OK
)
747 fprintf(stderr
, "ERROR: Unable to get PPD file %s: %d - %s\n",
748 uri
, get_status
, httpStatus(get_status
));
750 else if ((fd
= cupsTempFd(filename
, sizeof(filename
))) >= 0)
752 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
753 write(fd
, buffer
, bytes
);
757 if ((ppd
= ppdOpenFile(filename
)) != NULL
)
759 if (ppd
->manufacturer
)
760 cgiSetVariable("CURRENT_MAKE", ppd
->manufacturer
);
763 cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd
->nickname
);
770 fprintf(stderr
, "ERROR: Unable to open PPD file %s: %s\n",
771 filename
, ppdErrorString(ppdLastError(&bytes
)));
779 "ERROR: Unable to create temporary file for PPD file: %s\n",
783 else if ((uriptr
= strrchr(cgiGetVariable("DEVICE_URI"), '|')) != NULL
)
786 * Extract make and make/model from device URI string...
789 char make
[1024], /* Make string */
790 *makeptr
; /* Pointer into make */
795 strlcpy(make
, uriptr
, sizeof(make
));
797 if ((makeptr
= strchr(make
, ' ')) != NULL
)
799 else if ((makeptr
= strchr(make
, '-')) != NULL
)
801 else if (!strncasecmp(make
, "laserjet", 8) ||
802 !strncasecmp(make
, "deskjet", 7) ||
803 !strncasecmp(make
, "designjet", 9))
805 else if (!strncasecmp(make
, "phaser", 6))
806 strcpy(make
, "Xerox");
807 else if (!strncasecmp(make
, "stylus", 6))
808 strcpy(make
, "Epson");
810 strcpy(make
, "Generic");
812 cgiSetVariable("CURRENT_MAKE", make
);
813 cgiSetVariable("PPD_MAKE", make
);
814 cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr
);
818 * Build a CUPS_GET_PPDS request, which requires the following
822 * attributes-natural-language
826 request
= ippNewRequest(CUPS_GET_PPDS
);
828 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
829 NULL
, "ipp://localhost/printers/");
831 if ((var
= cgiGetVariable("PPD_MAKE")) != NULL
)
832 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
833 "ppd-make", NULL
, var
);
835 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
836 "requested-attributes", NULL
, "ppd-make");
839 * Do the request and get back a response...
842 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
845 * Got the list of PPDs, see if the user has selected a make...
848 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
853 * Let the user choose a make...
856 for (element
= 0, attr
= response
->attrs
, last
= NULL
;
859 if (attr
->name
&& strcmp(attr
->name
, "ppd-make") == 0)
861 strcasecmp(last
->values
[0].string
.text
,
862 attr
->values
[0].string
.text
) != 0)
864 cgiSetArray("PPD_MAKE", element
, attr
->values
[0].string
.text
);
870 cgiCopyTemplateLang("choose-make.tmpl");
876 * Let the user choose a model...
879 const char *make_model
; /* Current make/model string */
882 if ((make_model
= cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL
)
885 * Scan for "close" matches...
888 int match
, /* Current match */
889 best_match
, /* Best match so far */
890 count
; /* Number of drivers */
891 const char *best
, /* Best matching string */
892 *current
; /* Current string */
895 count
= cgiGetSize("PPD_MAKE_AND_MODEL");
897 for (i
= 0, best_match
= 0, best
= NULL
; i
< count
; i
++)
899 current
= cgiGetArray("PPD_MAKE_AND_MODEL", i
);
900 match
= match_string(make_model
, current
);
902 if (match
> best_match
)
909 if (best_match
> strlen(var
))
912 * Found a match longer than the make...
915 cgiSetVariable("CURRENT_MAKE_AND_MODEL", best
);
920 cgiCopyTemplateLang("choose-model.tmpl");
930 cgiShowIPPError(_("Unable to get list of printer drivers:"));
931 cgiCopyTemplateLang("error.tmpl");
938 * Build a CUPS_ADD_PRINTER request, which requires the following
942 * attributes-natural-language
948 * printer-is-accepting-jobs
952 request
= ippNewRequest(CUPS_ADD_PRINTER
);
954 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
955 "localhost", 0, "/printers/%s",
956 cgiGetVariable("PRINTER_NAME"));
957 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
960 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
961 NULL
, cgiGetVariable("PRINTER_LOCATION"));
963 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
964 NULL
, cgiGetVariable("PRINTER_INFO"));
967 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "ppd-name",
968 NULL
, cgiGetVariable("PPD_NAME"));
970 strlcpy(uri
, cgiGetVariable("DEVICE_URI"), sizeof(uri
));
973 * Strip make and model from URI...
976 if ((uriptr
= strrchr(uri
, '|')) != NULL
)
979 if (!strncmp(uri
, "serial:", 7))
982 * Update serial port URI to include baud rate, etc.
985 if ((uriptr
= strchr(uri
, '?')) == NULL
)
986 uriptr
= uri
+ strlen(uri
);
988 snprintf(uriptr
, sizeof(uri
) - (uriptr
- uri
),
989 "?baud=%s+bits=%s+parity=%s+flow=%s",
990 cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
991 cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
994 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "device-uri",
997 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
999 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
1003 * Do the request and get back a response...
1007 ippDelete(cupsDoFileRequest(http
, request
, "/admin/", file
->tempfile
));
1009 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
1011 if (cupsLastError() > IPP_OK_CONFLICT
)
1013 cgiStartHTML(title
);
1014 cgiShowIPPError(modify
? _("Unable to modify printer:") :
1015 _("Unable to add printer:"));
1020 * Redirect successful updates back to the printer or set-options pages...
1023 char refresh
[1024]; /* Refresh URL */
1026 cgiFormEncode(uri
, name
, sizeof(uri
));
1029 snprintf(refresh
, sizeof(refresh
),
1030 "5;/admin/?OP=redirect&URL=/printers/%s", uri
);
1032 snprintf(refresh
, sizeof(refresh
),
1033 "5;/admin/?OP=set-printer-options&PRINTER_NAME=%s", uri
);
1035 cgiSetVariable("refresh_page", refresh
);
1037 cgiStartHTML(title
);
1040 cgiCopyTemplateLang("printer-modified.tmpl");
1042 cgiCopyTemplateLang("printer-added.tmpl");
1054 * 'do_config_printer()' - Configure the default options for a printer.
1058 do_config_printer(http_t
*http
) /* I - HTTP connection */
1060 int i
, j
, k
, m
; /* Looping vars */
1061 int have_options
; /* Have options? */
1062 ipp_t
*request
, /* IPP request */
1063 *response
; /* IPP response */
1064 ipp_attribute_t
*attr
; /* IPP attribute */
1065 char uri
[HTTP_MAX_URI
]; /* Job URI */
1066 const char *var
; /* Variable value */
1067 const char *printer
; /* Printer printer name */
1068 const char *filename
; /* PPD filename */
1069 char tempfile
[1024]; /* Temporary filename */
1070 cups_file_t
*in
, /* Input file */
1071 *out
; /* Output file */
1072 char line
[1024]; /* Line from PPD file */
1073 char keyword
[1024], /* Keyword from Default line */
1074 *keyptr
; /* Pointer into keyword... */
1075 ppd_file_t
*ppd
; /* PPD file */
1076 ppd_group_t
*group
; /* Option group */
1077 ppd_option_t
*option
; /* Option */
1078 ppd_attr_t
*protocol
; /* cupsProtocol attribute */
1079 const char *title
; /* Page title */
1082 title
= cgiText(_("Set Printer Options"));
1084 fprintf(stderr
, "DEBUG: do_config_printer(http=%p)\n", http
);
1087 * Get the printer name...
1090 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
1091 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1092 "localhost", 0, "/printers/%s", printer
);
1095 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
1096 cgiStartHTML(title
);
1097 cgiCopyTemplateLang("error.tmpl");
1102 fprintf(stderr
, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer
, uri
);
1105 * Get the PPD file...
1108 if ((filename
= cupsGetPPD2(http
, printer
)) == NULL
)
1110 fputs("DEBUG: No PPD file!?!\n", stderr
);
1112 cgiStartHTML(title
);
1113 cgiShowIPPError(_("Unable to get PPD file!"));
1118 fprintf(stderr
, "DEBUG: Got PPD file: \"%s\"\n", filename
);
1120 if ((ppd
= ppdOpenFile(filename
)) == NULL
)
1122 cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i
)));
1123 cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:")));
1124 cgiStartHTML(title
);
1125 cgiCopyTemplateLang("error.tmpl");
1130 if (cgiGetVariable("job_sheets_start") != NULL
||
1131 cgiGetVariable("job_sheets_end") != NULL
)
1136 ppdMarkDefaults(ppd
);
1138 DEBUG_printf(("<P>ppd->num_groups = %d\n"
1139 "<UL>\n", ppd
->num_groups
));
1141 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
1143 DEBUG_printf(("<LI>%s<UL>\n", group
->text
));
1145 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
1146 if ((var
= cgiGetVariable(option
->keyword
)) != NULL
)
1148 DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option
->keyword
, var
));
1150 ppdMarkOption(ppd
, option
->keyword
, var
);
1154 printf("<LI>%s not defined!</LI>\n", option
->keyword
);
1157 DEBUG_puts("</UL></LI>");
1160 DEBUG_printf(("</UL>\n"
1161 "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd
)));
1163 if (!have_options
|| ppdConflicts(ppd
))
1166 * Show the options to the user...
1169 fputs("DEBUG: Showing options...\n", stderr
);
1173 cgiStartHTML("Set Printer Options");
1174 cgiCopyTemplateLang("set-printer-options-header.tmpl");
1176 if (ppdConflicts(ppd
))
1178 for (i
= ppd
->num_groups
, k
= 0, group
= ppd
->groups
; i
> 0; i
--, group
++)
1179 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
1180 if (option
->conflicted
)
1182 cgiSetArray("ckeyword", k
, option
->keyword
);
1183 cgiSetArray("ckeytext", k
, option
->text
);
1187 cgiCopyTemplateLang("option-conflict.tmpl");
1190 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
1194 if (!strcmp(group
->name
, "InstallableOptions"))
1195 cgiSetVariable("GROUP", cgiText(_("Options Installed")));
1197 cgiSetVariable("GROUP", group
->text
);
1199 cgiCopyTemplateLang("option-header.tmpl");
1201 for (j
= group
->num_options
, option
= group
->options
;
1205 if (!strcmp(option
->keyword
, "PageRegion"))
1208 cgiSetVariable("KEYWORD", option
->keyword
);
1209 cgiSetVariable("KEYTEXT", option
->text
);
1211 if (option
->conflicted
)
1212 cgiSetVariable("CONFLICTED", "1");
1214 cgiSetVariable("CONFLICTED", "0");
1216 cgiSetSize("CHOICES", 0);
1217 cgiSetSize("TEXT", 0);
1218 for (k
= 0, m
= 0; k
< option
->num_choices
; k
++)
1221 * Hide custom option values...
1224 if (!strcmp(option
->choices
[k
].choice
, "Custom"))
1227 cgiSetArray("CHOICES", m
, option
->choices
[k
].choice
);
1228 cgiSetArray("TEXT", m
, option
->choices
[k
].text
);
1232 if (option
->choices
[k
].marked
)
1233 cgiSetVariable("DEFCHOICE", option
->choices
[k
].choice
);
1238 case PPD_UI_BOOLEAN
:
1239 cgiCopyTemplateLang("option-boolean.tmpl");
1241 case PPD_UI_PICKONE
:
1242 cgiCopyTemplateLang("option-pickone.tmpl");
1244 case PPD_UI_PICKMANY
:
1245 cgiCopyTemplateLang("option-pickmany.tmpl");
1250 cgiCopyTemplateLang("option-trailer.tmpl");
1254 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
1255 * following attributes:
1257 * attributes-charset
1258 * attributes-natural-language
1262 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1264 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1265 "localhost", 0, "/printers/%s", printer
);
1266 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1270 * Do the request and get back a response...
1273 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1275 if ((attr
= ippFindAttribute(response
, "job-sheets-supported",
1276 IPP_TAG_ZERO
)) != NULL
)
1279 * Add the job sheets options...
1282 cgiSetVariable("GROUP", cgiText(_("Banners")));
1283 cgiCopyTemplateLang("option-header.tmpl");
1285 cgiSetSize("CHOICES", attr
->num_values
);
1286 cgiSetSize("TEXT", attr
->num_values
);
1287 for (k
= 0; k
< attr
->num_values
; k
++)
1289 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1290 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1293 attr
= ippFindAttribute(response
, "job-sheets-default", IPP_TAG_ZERO
);
1295 cgiSetVariable("KEYWORD", "job_sheets_start");
1296 cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
1297 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1298 "" : attr
->values
[0].string
.text
);
1300 cgiCopyTemplateLang("option-pickone.tmpl");
1302 cgiSetVariable("KEYWORD", "job_sheets_end");
1303 cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
1304 cgiSetVariable("DEFCHOICE", attr
== NULL
&& attr
->num_values
> 1 ?
1305 "" : attr
->values
[1].string
.text
);
1307 cgiCopyTemplateLang("option-pickone.tmpl");
1309 cgiCopyTemplateLang("option-trailer.tmpl");
1312 if (ippFindAttribute(response
, "printer-error-policy-supported",
1314 ippFindAttribute(response
, "printer-op-policy-supported",
1318 * Add the error and operation policy options...
1321 cgiSetVariable("GROUP", cgiText(_("Policies")));
1322 cgiCopyTemplateLang("option-header.tmpl");
1328 attr
= ippFindAttribute(response
, "printer-error-policy-supported",
1333 cgiSetSize("CHOICES", attr
->num_values
);
1334 cgiSetSize("TEXT", attr
->num_values
);
1335 for (k
= 0; k
< attr
->num_values
; k
++)
1337 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1338 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1341 attr
= ippFindAttribute(response
, "printer-error-policy",
1344 cgiSetVariable("KEYWORD", "printer_error_policy");
1345 cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
1346 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1347 "" : attr
->values
[0].string
.text
);
1350 cgiCopyTemplateLang("option-pickone.tmpl");
1353 * Operation policy...
1356 attr
= ippFindAttribute(response
, "printer-op-policy-supported",
1361 cgiSetSize("CHOICES", attr
->num_values
);
1362 cgiSetSize("TEXT", attr
->num_values
);
1363 for (k
= 0; k
< attr
->num_values
; k
++)
1365 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1366 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1369 attr
= ippFindAttribute(response
, "printer-op-policy", IPP_TAG_ZERO
);
1371 cgiSetVariable("KEYWORD", "printer_op_policy");
1372 cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
1373 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1374 "" : attr
->values
[0].string
.text
);
1376 cgiCopyTemplateLang("option-pickone.tmpl");
1379 cgiCopyTemplateLang("option-trailer.tmpl");
1382 ippDelete(response
);
1386 * Binary protocol support...
1389 if (ppd
->protocols
&& strstr(ppd
->protocols
, "BCP"))
1391 protocol
= ppdFindAttr(ppd
, "cupsProtocol", NULL
);
1393 cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol")));
1394 cgiCopyTemplateLang("option-header.tmpl");
1396 cgiSetSize("CHOICES", 2);
1397 cgiSetSize("TEXT", 2);
1398 cgiSetArray("CHOICES", 0, "None");
1399 cgiSetArray("TEXT", 0, cgiText(_("None")));
1401 if (strstr(ppd
->protocols
, "TBCP"))
1403 cgiSetArray("CHOICES", 1, "TBCP");
1404 cgiSetArray("TEXT", 1, "TBCP");
1408 cgiSetArray("CHOICES", 1, "BCP");
1409 cgiSetArray("TEXT", 1, "BCP");
1412 cgiSetVariable("KEYWORD", "protocol");
1413 cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol")));
1414 cgiSetVariable("DEFCHOICE", protocol
? protocol
->value
: "None");
1416 cgiCopyTemplateLang("option-pickone.tmpl");
1418 cgiCopyTemplateLang("option-trailer.tmpl");
1421 cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
1427 * Set default options...
1430 fputs("DEBUG: Setting options...\n", stderr
);
1432 out
= cupsTempFile2(tempfile
, sizeof(tempfile
));
1433 in
= cupsFileOpen(filename
, "r");
1437 cgiSetVariable("ERROR", strerror(errno
));
1438 cgiStartHTML("Set Printer Options");
1439 cgiCopyTemplateLang("error.tmpl");
1455 while (cupsFileGets(in
, line
, sizeof(line
)))
1457 if (!strncmp(line
, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
1459 else if (strncmp(line
, "*Default", 8))
1460 cupsFilePrintf(out
, "%s\n", line
);
1464 * Get default option name...
1467 strlcpy(keyword
, line
+ 8, sizeof(keyword
));
1469 for (keyptr
= keyword
; *keyptr
; keyptr
++)
1470 if (*keyptr
== ':' || isspace(*keyptr
& 255))
1475 if (!strcmp(keyword
, "PageRegion"))
1476 var
= cgiGetVariable("PageSize");
1478 var
= cgiGetVariable(keyword
);
1481 cupsFilePrintf(out
, "*Default%s: %s\n", keyword
, var
);
1483 cupsFilePrintf(out
, "%s\n", line
);
1487 if ((var
= cgiGetVariable("protocol")) != NULL
)
1488 cupsFilePrintf(out
, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
1494 * Build a CUPS_ADD_PRINTER request, which requires the following
1497 * attributes-charset
1498 * attributes-natural-language
1500 * job-sheets-default
1504 request
= ippNewRequest(CUPS_ADD_PRINTER
);
1506 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1507 "localhost", 0, "/printers/%s",
1508 cgiGetVariable("PRINTER_NAME"));
1509 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1512 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1513 "job-sheets-default", 2, NULL
, NULL
);
1514 attr
->values
[0].string
.text
= strdup(cgiGetVariable("job_sheets_start"));
1515 attr
->values
[1].string
.text
= strdup(cgiGetVariable("job_sheets_end"));
1517 if ((var
= cgiGetVariable("printer_error_policy")) != NULL
)
1518 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1519 "printer-error-policy", NULL
, var
);
1521 if ((var
= cgiGetVariable("printer_op_policy")) != NULL
)
1522 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1523 "printer-op-policy", NULL
, var
);
1526 * Do the request and get back a response...
1529 ippDelete(cupsDoFileRequest(http
, request
, "/admin/", tempfile
));
1531 if (cupsLastError() > IPP_OK_CONFLICT
)
1533 cgiStartHTML(title
);
1534 cgiShowIPPError(_("Unable to set options:"));
1539 * Redirect successful updates back to the printer page...
1542 char refresh
[1024]; /* Refresh URL */
1545 cgiFormEncode(uri
, printer
, sizeof(uri
));
1546 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=/printers/%s",
1548 cgiSetVariable("refresh_page", refresh
);
1550 cgiStartHTML(title
);
1552 cgiCopyTemplateLang("printer-configured.tmpl");
1565 * 'do_config_server()' - Configure server settings.
1569 do_config_server(http_t
*http
) /* I - HTTP connection */
1571 if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
1574 * Save basic setting changes...
1577 http_status_t status
; /* PUT status */
1578 cups_file_t
*cupsd
; /* cupsd.conf file */
1579 char tempfile
[1024]; /* Temporary new cupsd.conf */
1580 int tempfd
; /* Temporary file descriptor */
1581 cups_file_t
*temp
; /* Temporary file */
1582 char line
[1024], /* Line from cupsd.conf file */
1583 *value
; /* Value on line */
1584 const char *server_root
; /* Location of config files */
1585 int linenum
, /* Line number in file */
1586 in_policy
, /* In a policy section? */
1587 in_cancel_job
, /* In a cancel-job section? */
1588 in_admin_location
, /* In the /admin location? */
1589 in_conf_location
, /* In the /admin/conf location? */
1590 in_root_location
; /* In the / location? */
1591 int remote_printers
, /* Show remote printers */
1592 share_printers
, /* Share local printers */
1593 remote_admin
, /* Remote administration allowed? */
1594 user_cancel_any
, /* Cancel-job policy set? */
1595 debug_logging
; /* LogLevel debug set? */
1596 int wrote_port_listen
, /* Wrote the port/listen lines? */
1597 wrote_browsing
, /* Wrote the browsing lines? */
1598 wrote_policy
, /* Wrote the policy? */
1599 wrote_loglevel
, /* Wrote the LogLevel line? */
1600 wrote_admin_location
, /* Wrote the /admin location? */
1601 wrote_conf_location
, /* Wrote the /admin/conf location? */
1602 wrote_root_location
; /* Wrote the / location? */
1603 int indent
; /* Indentation */
1607 * Get form variables...
1610 remote_printers
= cgiGetVariable("REMOTE_PRINTERS") != NULL
;
1611 share_printers
= cgiGetVariable("SHARE_PRINTERS") != NULL
;
1612 remote_admin
= cgiGetVariable("REMOTE_ADMIN") != NULL
;
1613 user_cancel_any
= cgiGetVariable("USER_CANCEL_ANY") != NULL
;
1614 debug_logging
= cgiGetVariable("DEBUG_LOGGING") != NULL
;
1617 * Locate the cupsd.conf file...
1620 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
1621 server_root
= CUPS_SERVERROOT
;
1623 snprintf(line
, sizeof(line
), "%s/cupsd.conf", server_root
);
1626 * Open the cupsd.conf file...
1629 if ((cupsd
= cupsFileOpen(line
, "r")) == NULL
)
1632 * Unable to open - log an error...
1635 cgiStartHTML(cgiText(_("Change Settings")));
1636 cgiSetVariable("MESSAGE", cgiText(_("Unable to change server settings:")));
1637 cgiSetVariable("ERROR", strerror(errno
));
1638 cgiCopyTemplateLang("error.tmpl");
1646 * Create a temporary file for the new cupsd.conf file...
1649 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
1651 cgiStartHTML(cgiText(_("Change Settings")));
1652 cgiSetVariable("MESSAGE", cgiText(_("Unable to change server settings:")));
1653 cgiSetVariable("ERROR", strerror(errno
));
1654 cgiCopyTemplateLang("error.tmpl");
1658 cupsFileClose(cupsd
);
1662 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
1664 cgiStartHTML(cgiText(_("Change Settings")));
1665 cgiSetVariable("MESSAGE", cgiText(_("Unable to change server settings:")));
1666 cgiSetVariable("ERROR", strerror(errno
));
1667 cgiCopyTemplateLang("error.tmpl");
1673 cupsFileClose(cupsd
);
1678 * Copy the old file to the new, making changes along the way...
1681 in_admin_location
= 0;
1683 in_conf_location
= 0;
1685 in_root_location
= 0;
1687 wrote_admin_location
= 0;
1689 wrote_conf_location
= 0;
1692 wrote_port_listen
= 0;
1693 wrote_root_location
= 0;
1696 while (cupsFileGetConf(cupsd
, line
, sizeof(line
), &value
, &linenum
))
1698 if (!strcasecmp(line
, "Port") || !strcasecmp(line
, "Listen"))
1700 if (!wrote_port_listen
)
1702 wrote_port_listen
= 1;
1704 if (share_printers
|| remote_admin
)
1706 cupsFilePuts(temp
, "# Allow remote access\n");
1707 cupsFilePrintf(temp
, "Listen *:%d\n", ippPort());
1711 cupsFilePuts(temp
, "# Only listen for connections from the local machine.\n");
1712 cupsFilePrintf(temp
, "Listen localhost:%d\n", ippPort());
1715 #ifdef CUPS_DEFAULT_DOMAINSOCKET
1716 cupsFilePuts(temp
, "Listen " CUPS_DEFAULT_DOMAINSOCKET
"\n");
1717 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
1720 else if (!strcasecmp(line
, "Browsing") ||
1721 !strcasecmp(line
, "BrowseAddress") ||
1722 !strcasecmp(line
, "BrowseAllow") ||
1723 !strcasecmp(line
, "BrowseDeny") ||
1724 !strcasecmp(line
, "BrowseOrder"))
1726 if (!wrote_browsing
)
1730 if (remote_printers
|| share_printers
)
1732 if (remote_printers
&& share_printers
)
1733 cupsFilePuts(temp
, "# Enable printer sharing and shared printers.\n");
1734 else if (remote_printers
)
1735 cupsFilePuts(temp
, "# Show shared printers on the local network.\n");
1737 cupsFilePuts(temp
, "# Share local printers on the local network.\n");
1739 cupsFilePuts(temp
, "Browsing On\n");
1740 cupsFilePuts(temp
, "BrowseOrder allow,deny\n");
1742 if (remote_printers
)
1743 cupsFilePuts(temp
, "BrowseAllow @LOCAL\n");
1746 cupsFilePuts(temp
, "BrowseAddress @LOCAL\n");
1750 cupsFilePuts(temp
, "# Disable printer sharing and shared printers.\n");
1751 cupsFilePuts(temp
, "Browsing Off\n");
1755 else if (!strcasecmp(line
, "LogLevel"))
1761 cupsFilePuts(temp
, "# Show troubleshooting information in error_log.\n");
1762 cupsFilePuts(temp
, "LogLevel debug\n");
1766 cupsFilePuts(temp
, "# Show general information in error_log.\n");
1767 cupsFilePuts(temp
, "LogLevel info\n");
1770 else if (!strcasecmp(line
, "<Policy") && !strcasecmp(value
, "default"))
1774 cupsFilePrintf(temp
, "%s %s>\n", line
, value
);
1777 else if (!strcasecmp(line
, "</Policy>"))
1784 if (!user_cancel_any
)
1785 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
1786 " <Limit Cancel-Job>\n"
1787 " Order deny,allow\n"
1795 cupsFilePuts(temp
, "</Policy>\n");
1797 else if (!strcasecmp(line
, "<Location"))
1800 if (!strcmp(value
, "/admin"))
1801 in_admin_location
= 1;
1802 if (!strcmp(value
, "/admin/conf"))
1803 in_conf_location
= 1;
1804 else if (!strcmp(value
, "/"))
1805 in_root_location
= 1;
1807 cupsFilePrintf(temp
, "%s %s>\n", line
, value
);
1809 else if (!strcasecmp(line
, "</Location>"))
1812 if (in_admin_location
)
1814 wrote_admin_location
= 1;
1817 cupsFilePuts(temp
, " # Allow remote administration...\n");
1819 cupsFilePuts(temp
, " # Restrict access to the admin pages...\n");
1821 cupsFilePuts(temp
, " Order allow,deny\n");
1824 cupsFilePuts(temp
, " Allow @LOCAL\n");
1826 cupsFilePuts(temp
, " Allow localhost\n");
1828 else if (in_conf_location
)
1830 wrote_conf_location
= 1;
1833 cupsFilePuts(temp
, " # Allow remote access to the configuration files...\n");
1835 cupsFilePuts(temp
, " # Restrict access to the configuration files...\n");
1837 cupsFilePuts(temp
, " Order allow,deny\n");
1840 cupsFilePuts(temp
, " Allow @LOCAL\n");
1842 cupsFilePuts(temp
, " Allow localhost\n");
1844 else if (in_root_location
)
1846 wrote_root_location
= 1;
1848 if (remote_admin
&& share_printers
)
1849 cupsFilePuts(temp
, " # Allow shared printing and remote administration...\n");
1850 else if (remote_admin
)
1851 cupsFilePuts(temp
, " # Allow remote administration...\n");
1852 else if (share_printers
)
1853 cupsFilePuts(temp
, " # Allow shared printing...\n");
1855 cupsFilePuts(temp
, " # Restrict access to the server...\n");
1857 cupsFilePuts(temp
, " Order allow,deny\n");
1859 if (remote_admin
|| share_printers
)
1860 cupsFilePuts(temp
, " Allow @LOCAL\n");
1862 cupsFilePuts(temp
, " Allow localhost\n");
1865 in_admin_location
= 0;
1866 in_conf_location
= 0;
1867 in_root_location
= 0;
1869 cupsFilePuts(temp
, "</Location>\n");
1871 else if (!strcasecmp(line
, "<Limit") && in_policy
)
1874 * See if the policy limit is for the Cancel-Job operation...
1877 char *valptr
; /* Pointer into value */
1882 if (!strcasecmp(value
, "cancel-job"))
1885 * Don't write anything for this limit section...
1892 cupsFilePrintf(temp
, " %s", line
);
1896 for (valptr
= value
; !isspace(*valptr
& 255) && *valptr
; valptr
++);
1901 if (!strcasecmp(value
, "cancel-job"))
1904 * Write everything except for this definition...
1910 cupsFilePrintf(temp
, " %s", value
);
1912 for (value
= valptr
; isspace(*value
& 255); value
++);
1915 cupsFilePuts(temp
, ">\n");
1918 else if (!strcasecmp(line
, "</Limit>") && in_cancel_job
)
1922 if (in_cancel_job
== 1)
1923 cupsFilePuts(temp
, " </Limit>\n");
1927 if (!user_cancel_any
)
1928 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
1929 " <Limit Cancel-Job>\n"
1930 " Order deny,allow\n"
1931 " Require user @OWNER @SYSTEM\n"
1936 else if ((in_admin_location
|| in_conf_location
|| in_root_location
) &&
1937 (!strcasecmp(line
, "Allow") || !strcasecmp(line
, "Deny") ||
1938 !strcasecmp(line
, "Order")))
1940 else if (in_cancel_job
== 2)
1942 else if (!strcasecmp(line
, "<Limit") && value
)
1943 cupsFilePrintf(temp
, " %s %s>\n", line
, value
);
1944 else if (line
[0] == '<')
1948 cupsFilePrintf(temp
, "%*s%s %s>\n", indent
, "", line
, value
);
1956 cupsFilePrintf(temp
, "%*s%s\n", indent
, "", line
);
1960 cupsFilePrintf(temp
, "%*s%s %s\n", indent
, "", line
, value
);
1962 cupsFilePrintf(temp
, "%*s%s\n", indent
, "", line
);
1966 * Write any missing info...
1969 if (!wrote_browsing
)
1971 if (remote_printers
|| share_printers
)
1973 if (remote_printers
&& share_printers
)
1974 cupsFilePuts(temp
, "# Enable printer sharing and shared printers.\n");
1975 else if (remote_printers
)
1976 cupsFilePuts(temp
, "# Show shared printers on the local network.\n");
1978 cupsFilePuts(temp
, "# Share local printers on the local network.\n");
1980 cupsFilePuts(temp
, "Browsing On\n");
1981 cupsFilePuts(temp
, "BrowseOrder allow,deny\n");
1983 if (remote_printers
)
1984 cupsFilePuts(temp
, "BrowseAllow @LOCAL\n");
1987 cupsFilePuts(temp
, "BrowseAddress @LOCAL\n");
1991 cupsFilePuts(temp
, "# Disable printer sharing and shared printers.\n");
1992 cupsFilePuts(temp
, "Browsing Off\n");
1996 if (!wrote_loglevel
)
2000 cupsFilePuts(temp
, "# Show troubleshooting information in error_log.\n");
2001 cupsFilePuts(temp
, "LogLevel debug\n");
2005 cupsFilePuts(temp
, "# Show general information in error_log.\n");
2006 cupsFilePuts(temp
, "LogLevel info\n");
2010 if (!wrote_port_listen
)
2012 if (share_printers
|| remote_admin
)
2014 cupsFilePuts(temp
, "# Allow remote access\n");
2015 cupsFilePrintf(temp
, "Listen *:%d\n", ippPort());
2019 cupsFilePuts(temp
, "# Only listen for connections from the local machine.\n");
2020 cupsFilePrintf(temp
, "Listen localhost:%d\n", ippPort());
2023 #ifdef CUPS_DEFAULT_DOMAINSOCKET
2024 cupsFilePuts(temp
, "Listen " CUPS_DEFAULT_DOMAINSOCKET
"\n");
2025 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
2028 if (!wrote_root_location
)
2030 if (remote_admin
&& share_printers
)
2031 cupsFilePuts(temp
, "# Allow shared printing and remote administration...\n");
2032 else if (remote_admin
)
2033 cupsFilePuts(temp
, "# Allow remote administration...\n");
2034 else if (share_printers
)
2035 cupsFilePuts(temp
, "# Allow shared printing...\n");
2037 cupsFilePuts(temp
, "# Restrict access to the server...\n");
2039 cupsFilePuts(temp
, "<Location />\n"
2040 " Order allow,deny\n");
2042 if (remote_admin
|| share_printers
)
2043 cupsFilePuts(temp
, " Allow @LOCAL\n");
2045 cupsFilePuts(temp
, " Allow localhost\n");
2047 cupsFilePuts(temp
, "</Location>\n");
2050 if (!wrote_admin_location
)
2053 cupsFilePuts(temp
, "# Allow remote administration...\n");
2055 cupsFilePuts(temp
, "# Restrict access to the admin pages...\n");
2057 cupsFilePuts(temp
, "<Location /admin>\n"
2058 " Order allow,deny\n");
2061 cupsFilePuts(temp
, " Allow @LOCAL\n");
2063 cupsFilePuts(temp
, " Allow localhost\n");
2065 cupsFilePuts(temp
, "</Location>\n");
2068 if (!wrote_conf_location
)
2071 cupsFilePuts(temp
, "# Allow remote access to the configuration files...\n");
2073 cupsFilePuts(temp
, "# Restrict access to the configuration files...\n");
2075 cupsFilePuts(temp
, "<Location /admin/conf>\n"
2077 " Require user @SYSTEM\n"
2078 " Order allow,deny\n");
2081 cupsFilePuts(temp
, " Allow @LOCAL\n");
2083 cupsFilePuts(temp
, " Allow localhost\n");
2085 cupsFilePuts(temp
, "</Location>\n");
2090 cupsFilePuts(temp
, "<Policy default>\n"
2091 " # Job-related operations must be done by the owner or an adminstrator...\n"
2092 " <Limit Send-Document Send-URI Hold-Job Release-Job "
2093 "Restart-Job Purge-Jobs Set-Job-Attributes "
2094 "Create-Job-Subscription Renew-Subscription "
2095 "Cancel-Subscription Get-Notifications Reprocess-Job "
2096 "Cancel-Current-Job Suspend-Current-Job Resume-Job "
2098 " Require user @OWNER @SYSTEM\n"
2099 " Order deny,allow\n"
2101 " # All administration operations require an adminstrator to authenticate...\n"
2102 " <Limit Pause-Printer Resume-Printer "
2103 "Set-Printer-Attributes Enable-Printer "
2104 "Disable-Printer Pause-Printer-After-Current-Job "
2105 "Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer "
2106 "Activate-Printer Restart-Printer Shutdown-Printer "
2107 "Startup-Printer Promote-Job Schedule-Job-After "
2108 "CUPS-Add-Printer CUPS-Delete-Printer "
2109 "CUPS-Add-Class CUPS-Delete-Class "
2110 "CUPS-Accept-Jobs CUPS-Reject-Jobs "
2111 "CUPS-Set-Default CUPS-Add-Device CUPS-Delete-Device>\n"
2113 " Require user @SYSTEM\n"
2114 " Order deny,allow\n"
2117 if (!user_cancel_any
)
2118 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
2119 " <Limit Cancel-Job>\n"
2120 " Require user @OWNER @SYSTEM\n"
2121 " Order deny,allow\n"
2124 cupsFilePuts(temp
, " <Limit All>\n"
2125 " Order deny,allow\n"
2130 cupsFileClose(cupsd
);
2131 cupsFileClose(temp
);
2134 * Upload the configuration file to the server...
2137 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
2139 if (status
!= HTTP_CREATED
)
2141 cgiSetVariable("MESSAGE", cgiText(_("Unable to upload cupsd.conf file:")));
2142 cgiSetVariable("ERROR", httpStatus(status
));
2143 cgiStartHTML(cgiText(_("Change Settings")));
2144 cgiCopyTemplateLang("error.tmpl");
2148 cgiSetVariable("refresh_page", "5;/admin/?OP=redirect");
2150 cgiStartHTML(cgiText(_("Change Settings")));
2151 cgiCopyTemplateLang("restart.tmpl");
2158 else if (cgiIsPOST())
2161 * Save hand-edited config file...
2164 http_status_t status
; /* PUT status */
2165 char tempfile
[1024]; /* Temporary new cupsd.conf */
2166 int tempfd
; /* Temporary file descriptor */
2167 cups_file_t
*temp
; /* Temporary file */
2168 const char *start
, /* Start of line */
2169 *end
; /* End of line */
2173 * Create a temporary file for the new cupsd.conf file...
2176 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
2178 cgiStartHTML(cgiText(_("Edit Configuration File")));
2179 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
2180 cgiSetVariable("ERROR", strerror(errno
));
2181 cgiCopyTemplateLang("error.tmpl");
2188 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
2190 cgiStartHTML(cgiText(_("Edit Configuration File")));
2191 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
2192 cgiSetVariable("ERROR", strerror(errno
));
2193 cgiCopyTemplateLang("error.tmpl");
2203 * Copy the cupsd.conf text from the form variable...
2206 start
= cgiGetVariable("CUPSDCONF");
2209 if ((end
= strstr(start
, "\r\n")) == NULL
)
2210 if ((end
= strstr(start
, "\n")) == NULL
)
2211 end
= start
+ strlen(start
);
2213 cupsFileWrite(temp
, start
, end
- start
);
2214 cupsFilePutChar(temp
, '\n');
2218 else if (*end
== '\n')
2224 cupsFileClose(temp
);
2227 * Upload the configuration file to the server...
2230 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
2232 if (status
!= HTTP_CREATED
)
2234 cgiSetVariable("MESSAGE", cgiText(_("Unable to upload cupsd.conf file:")));
2235 cgiSetVariable("ERROR", httpStatus(status
));
2237 cgiStartHTML(cgiText(_("Edit Configuration File")));
2238 cgiCopyTemplateLang("error.tmpl");
2242 cgiSetVariable("refresh_page", "5;/admin/?OP=redirect");
2244 cgiStartHTML(cgiText(_("Edit Configuration File")));
2245 cgiCopyTemplateLang("restart.tmpl");
2254 struct stat info
; /* cupsd.conf information */
2255 cups_file_t
*cupsd
; /* cupsd.conf file */
2256 char *buffer
; /* Buffer for entire file */
2257 char filename
[1024]; /* Filename */
2258 const char *server_root
; /* Location of config files */
2262 * Locate the cupsd.conf file...
2265 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
2266 server_root
= CUPS_SERVERROOT
;
2268 snprintf(filename
, sizeof(filename
), "%s/cupsd.conf", server_root
);
2271 * Figure out the size...
2274 if (stat(filename
, &info
))
2276 cgiStartHTML(cgiText(_("Edit Configuration File")));
2277 cgiSetVariable("MESSAGE", cgiText(_("Unable to access cupsd.conf file:")));
2278 cgiSetVariable("ERROR", strerror(errno
));
2279 cgiCopyTemplateLang("error.tmpl");
2286 if (info
.st_size
> (1024 * 1024))
2288 cgiStartHTML(cgiText(_("Edit Configuration File")));
2289 cgiSetVariable("MESSAGE", cgiText(_("Unable to access cupsd.conf file:")));
2290 cgiSetVariable("ERROR",
2291 cgiText(_("Unable to edit cupsd.conf files larger than "
2293 cgiCopyTemplateLang("error.tmpl");
2296 fprintf(stderr
, "ERROR: \"%s\" too large (%ld) to edit!\n", filename
,
2297 (long)info
.st_size
);
2302 * Open the cupsd.conf file...
2305 if ((cupsd
= cupsFileOpen(filename
, "r")) == NULL
)
2308 * Unable to open - log an error...
2311 cgiStartHTML(cgiText(_("Edit Configuration File")));
2312 cgiSetVariable("MESSAGE", cgiText(_("Unable to access cupsd.conf file:")));
2313 cgiSetVariable("ERROR", strerror(errno
));
2314 cgiCopyTemplateLang("error.tmpl");
2322 * Allocate memory and load the file into a string buffer...
2325 buffer
= calloc(1, info
.st_size
+ 1);
2327 cupsFileRead(cupsd
, buffer
, info
.st_size
);
2328 cupsFileClose(cupsd
);
2330 cgiSetVariable("CUPSDCONF", buffer
);
2334 * Show the current config file...
2337 cgiStartHTML("Edit Configuration File");
2339 printf("<!-- \"%s\" -->\n", filename
);
2341 cgiCopyTemplateLang("edit-config.tmpl");
2349 * 'do_delete_class()' - Delete a class...
2353 do_delete_class(http_t
*http
) /* I - HTTP connection */
2355 ipp_t
*request
; /* IPP request */
2356 char uri
[HTTP_MAX_URI
]; /* Job URI */
2357 const char *pclass
; /* Printer class name */
2360 cgiStartHTML(cgiText(_("Delete Class")));
2362 if (cgiGetVariable("CONFIRM") == NULL
)
2364 cgiCopyTemplateLang("class-confirm.tmpl");
2369 if ((pclass
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2370 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2371 "localhost", 0, "/classes/%s", pclass
);
2374 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2375 cgiCopyTemplateLang("error.tmpl");
2381 * Build a CUPS_DELETE_CLASS request, which requires the following
2384 * attributes-charset
2385 * attributes-natural-language
2389 request
= ippNewRequest(CUPS_DELETE_CLASS
);
2391 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2395 * Do the request and get back a response...
2398 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
2400 if (cupsLastError() > IPP_OK_CONFLICT
)
2401 cgiShowIPPError(_("Unable to delete class:"));
2403 cgiCopyTemplateLang("class-deleted.tmpl");
2410 * 'do_delete_printer()' - Delete a printer...
2414 do_delete_printer(http_t
*http
) /* I - HTTP connection */
2416 ipp_t
*request
; /* IPP request */
2417 char uri
[HTTP_MAX_URI
]; /* Job URI */
2418 const char *printer
; /* Printer printer name */
2421 cgiStartHTML(cgiText(_("Delete Printer")));
2423 if (cgiGetVariable("CONFIRM") == NULL
)
2425 cgiCopyTemplateLang("printer-confirm.tmpl");
2430 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2431 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2432 "localhost", 0, "/printers/%s", printer
);
2435 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2436 cgiCopyTemplateLang("error.tmpl");
2442 * Build a CUPS_DELETE_PRINTER request, which requires the following
2445 * attributes-charset
2446 * attributes-natural-language
2450 request
= ippNewRequest(CUPS_DELETE_PRINTER
);
2452 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2456 * Do the request and get back a response...
2459 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
2461 if (cupsLastError() > IPP_OK_CONFLICT
)
2462 cgiShowIPPError(_("Unable to delete printer:"));
2464 cgiCopyTemplateLang("printer-deleted.tmpl");
2471 * 'do_export()' - Export printers to Samba...
2475 do_export(http_t
*http
) /* I - HTTP connection */
2477 int i
, j
; /* Looping vars */
2478 ipp_t
*request
, /* IPP request */
2479 *response
; /* IPP response */
2480 const char *username
, /* Samba username */
2481 *password
, /* Samba password */
2482 *export_all
; /* Export all printers? */
2483 int export_count
, /* Number of printers to export */
2484 printer_count
; /* Number of available printers */
2491 cgiStartHTML(cgiText(_("Export Printers to Samba")));
2497 username
= cgiGetVariable("USERNAME");
2498 password
= cgiGetVariable("PASSWORD");
2499 export_all
= cgiGetVariable("EXPORT_ALL");
2500 export_count
= cgiGetSize("EXPORT_NAME");
2502 if (username
&& *username
&& password
&& *password
&& export_count
<= 1000)
2508 char userpass
[1024], /* Username%password */
2509 *argv
[1005]; /* Arguments */
2510 int argc
; /* Number of arguments */
2511 int pid
; /* Process ID of child */
2512 int status
; /* Status of command */
2515 fputs("DEBUG: Export printers...\n", stderr
);
2518 * Create the command-line for cupsaddsmb...
2521 snprintf(userpass
, sizeof(userpass
), "%s%%%s", username
, password
);
2523 argv
[0] = "cupsaddsmb";
2530 argv
[argc
++] = "-a";
2533 for (i
= 0; i
< export_count
; i
++)
2534 argv
[argc
++] = (char *)cgiGetArray("EXPORT_NAME", i
);
2540 * Run the command...
2543 if ((pid
= fork()) == 0)
2546 * Child goes here...
2550 open("/dev/null", O_RDONLY
);
2554 execvp("cupsaddsmb", argv
);
2555 perror("ERROR: Unable to execute cupsaddsmb");
2559 cgiSetVariable("ERROR", cgiText(_("Unable to fork process!")));
2563 * Parent goes here, wait for child to finish...
2566 while (wait(&status
) < 0);
2570 char message
[1024]; /* Error message */
2573 if (WIFEXITED(status
))
2575 switch (WEXITSTATUS(status
))
2578 cgiSetVariable("ERROR", cgiText(_("Unable to connect to server!")));
2582 cgiSetVariable("ERROR", cgiText(_("Unable to get printer "
2587 cgiSetVariable("ERROR", cgiText(_("Unable to convert PPD file!")));
2591 cgiSetVariable("ERROR", cgiText(_("Unable to copy Windows 2000 "
2592 "printer driver files!")));
2596 cgiSetVariable("ERROR", cgiText(_("Unable to install Windows "
2597 "2000 printer driver files!")));
2601 cgiSetVariable("ERROR", cgiText(_("Unable to copy Windows 9x "
2602 "printer driver files!")));
2606 cgiSetVariable("ERROR", cgiText(_("Unable to install Windows "
2607 "9x printer driver files!")));
2611 cgiSetVariable("ERROR", cgiText(_("Unable to set Windows "
2612 "printer driver!")));
2616 cgiSetVariable("ERROR", cgiText(_("No printer drivers found!")));
2620 cgiSetVariable("ERROR", cgiText(_("Unable to execute "
2621 "cupsaddsmb command!")));
2625 snprintf(message
, sizeof(message
),
2626 cgiText(_("cupsaddsmb failed with status %d")),
2627 WEXITSTATUS(status
));
2629 cgiSetVariable("ERROR", message
);
2635 snprintf(message
, sizeof(message
),
2636 cgiText(_("cupsaddsmb crashed on signal %d")),
2639 cgiSetVariable("ERROR", message
);
2644 cgiCopyTemplateLang("samba-exported.tmpl");
2650 else if (username
&& !*username
)
2651 cgiSetVariable("ERROR",
2652 cgiText(_("A Samba username is required to export "
2653 "printer drivers!")));
2654 else if (username
&& (!password
|| !*password
))
2655 cgiSetVariable("ERROR",
2656 cgiText(_("A Samba password is required to export "
2657 "printer drivers!")));
2660 * Get list of available printers...
2663 cgiSetSize("PRINTER_NAME", 0);
2664 cgiSetSize("PRINTER_EXPORT", 0);
2666 request
= ippNewRequest(CUPS_GET_PRINTERS
);
2668 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
2671 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
2672 "printer-type-mask", CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
2673 CUPS_PRINTER_IMPLICIT
);
2675 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2676 "requested-attributes", NULL
, "printer-name");
2678 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2680 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
2681 ippDelete(response
);
2685 printer_count
= cgiGetSize("PRINTER_NAME");
2687 for (i
= 0; i
< printer_count
; i
++)
2689 for (j
= 0; j
< export_count
; j
++)
2690 if (!strcasecmp(cgiGetArray("PRINTER_NAME", i
),
2691 cgiGetArray("EXPORT_NAME", j
)))
2694 cgiSetArray("PRINTER_EXPORT", i
, j
< export_count
? "Y" : "");
2703 cgiCopyTemplateLang("samba-export.tmpl");
2709 * 'do_menu()' - Show the main menu...
2713 do_menu(http_t
*http
) /* I - HTTP connection */
2715 cups_file_t
*cupsd
; /* cupsd.conf file */
2716 char line
[1024], /* Line from cupsd.conf file */
2717 *value
; /* Value on line */
2718 const char *server_root
; /* Location of config files */
2719 const char *datadir
; /* Location of data files */
2720 ipp_t
*request
, /* IPP request */
2721 *response
; /* IPP response */
2722 ipp_attribute_t
*attr
; /* IPP attribute */
2726 * Locate the cupsd.conf file...
2729 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
2730 server_root
= CUPS_SERVERROOT
;
2732 snprintf(line
, sizeof(line
), "%s/cupsd.conf", server_root
);
2734 cgiStartHTML(cgiText(_("Administration")));
2736 printf("<!-- \"%s\" -->\n", line
);
2739 * Open the cupsd.conf file...
2742 if ((cupsd
= cupsFileOpen(line
, "r")) == NULL
)
2745 * Unable to open - log an error...
2748 cgiSetVariable("MESSAGE", cgiText(_("Unable to open cupsd.conf file:")));
2749 cgiSetVariable("ERROR", strerror(errno
));
2750 cgiCopyTemplateLang("error.tmpl");
2758 * Read the file, keeping track of what settings are enabled...
2761 int remote_access
= 0, /* Remote access allowed? */
2762 remote_admin
= 0, /* Remote administration allowed? */
2763 browsing
= 1, /* Browsing enabled? */
2764 browse_allow
= 1, /* Browse address set? */
2765 browse_address
= 0, /* Browse address set? */
2766 cancel_policy
= 1, /* Cancel-job policy set? */
2767 debug_logging
= 0; /* LogLevel debug set? */
2768 int linenum
= 0, /* Line number in file */
2769 in_policy
= 0, /* In a policy section? */
2770 in_cancel_job
= 0, /* In a cancel-job section? */
2771 in_admin_location
= 0; /* In the /admin location? */
2774 while (cupsFileGetConf(cupsd
, line
, sizeof(line
), &value
, &linenum
))
2776 if (!strcasecmp(line
, "Port"))
2780 else if (!strcasecmp(line
, "Listen"))
2782 char *port
; /* Pointer to port number, if any */
2785 if ((port
= strrchr(value
, ':')) != NULL
)
2788 if (strcasecmp(value
, "localhost") && strcmp(value
, "127.0.0.1"))
2791 else if (!strcasecmp(line
, "Browsing"))
2793 browsing
= !strcasecmp(value
, "yes") || !strcasecmp(value
, "on") ||
2794 !strcasecmp(value
, "true");
2796 else if (!strcasecmp(line
, "BrowseAddress"))
2800 else if (!strcasecmp(line
, "BrowseAllow"))
2804 else if (!strcasecmp(line
, "BrowseOrder"))
2806 browse_allow
= !strncasecmp(value
, "deny,", 5);
2808 else if (!strcasecmp(line
, "LogLevel"))
2810 debug_logging
= !strncasecmp(value
, "debug", 5);
2812 else if (!strcasecmp(line
, "<Policy") && !strcasecmp(value
, "default"))
2816 else if (!strcasecmp(line
, "</Policy>"))
2820 else if (!strcasecmp(line
, "<Limit") && in_policy
)
2823 * See if the policy limit is for the Cancel-Job operation...
2826 char *valptr
; /* Pointer into value */
2831 for (valptr
= value
; !isspace(*valptr
& 255) && *valptr
; valptr
++);
2836 if (!strcasecmp(value
, "cancel-job") || !strcasecmp(value
, "all"))
2842 for (value
= valptr
; isspace(*value
& 255); value
++);
2845 else if (!strcasecmp(line
, "</Limit>"))
2849 else if (!strcasecmp(line
, "Require") && in_cancel_job
)
2853 else if (!strcasecmp(line
, "<Location") && !strcasecmp(value
, "/admin"))
2855 in_admin_location
= 1;
2857 else if (!strcasecmp(line
, "</Location>"))
2859 in_admin_location
= 0;
2861 else if (!strcasecmp(line
, "Allow") && in_admin_location
&&
2862 strcasecmp(value
, "localhost") && strcasecmp(value
, "127.0.0.1"))
2868 cupsFileClose(cupsd
);
2870 if (browsing
&& browse_allow
)
2871 cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
2873 if (remote_access
&& browsing
&& browse_address
)
2874 cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2876 if (remote_access
&& remote_admin
)
2877 cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2880 cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2883 cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2887 * Get the list of printers and their devices...
2890 request
= ippNewRequest(CUPS_GET_PRINTERS
);
2892 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2893 "requested-attributes", NULL
, "device-uri");
2895 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type",
2896 CUPS_PRINTER_LOCAL
);
2897 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type-mask",
2898 CUPS_PRINTER_LOCAL
);
2900 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2903 * Got the printer list, now load the devices...
2906 int i
; /* Looping var */
2907 cups_array_t
*printer_devices
; /* Printer devices for local printers */
2908 char *printer_device
; /* Current printer device */
2912 * Allocate an array and copy the device strings...
2915 printer_devices
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2917 for (attr
= ippFindAttribute(response
, "device-uri", IPP_TAG_URI
);
2919 attr
= ippFindNextAttribute(response
, "device-uri", IPP_TAG_URI
))
2921 cupsArrayAdd(printer_devices
, strdup(attr
->values
[0].string
.text
));
2925 * Free the printer list and get the device list...
2928 ippDelete(response
);
2930 request
= ippNewRequest(CUPS_GET_DEVICES
);
2932 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2935 * Got the device list, let's parse it...
2938 const char *device_uri
, /* device-uri attribute value */
2939 *device_make_and_model
, /* device-make-and-model value */
2940 *device_info
; /* device-info value */
2943 for (i
= 0, attr
= response
->attrs
; attr
; attr
= attr
->next
)
2946 * Skip leading attributes until we hit a device...
2949 while (attr
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
2956 * Pull the needed attributes from this device...
2960 device_make_and_model
= NULL
;
2963 while (attr
&& attr
->group_tag
== IPP_TAG_PRINTER
)
2965 if (!strcmp(attr
->name
, "device-info") &&
2966 attr
->value_tag
== IPP_TAG_TEXT
)
2967 device_info
= attr
->values
[0].string
.text
;
2969 if (!strcmp(attr
->name
, "device-make-and-model") &&
2970 attr
->value_tag
== IPP_TAG_TEXT
)
2971 device_make_and_model
= attr
->values
[0].string
.text
;
2973 if (!strcmp(attr
->name
, "device-uri") &&
2974 attr
->value_tag
== IPP_TAG_URI
)
2975 device_uri
= attr
->values
[0].string
.text
;
2981 * See if we have everything needed...
2984 if (device_info
&& device_make_and_model
&& device_uri
&&
2985 strcasecmp(device_make_and_model
, "unknown") &&
2986 strchr(device_uri
, ':'))
2989 * Yes, now see if there is already a printer for this
2993 if (!cupsArrayFind(printer_devices
, (void *)device_uri
))
2996 * Not found, so it must be a new printer...
2999 char options
[1024], /* Form variables for this device */
3000 *options_ptr
; /* Pointer into string */
3001 const char *ptr
; /* Pointer into device string */
3005 * Format the printer name variable for this device...
3007 * We use the device-info string first, then device-uri,
3008 * and finally device-make-and-model to come up with a
3012 strcpy(options
, "PRINTER_NAME=");
3013 options_ptr
= options
+ strlen(options
);
3015 if (strncasecmp(device_info
, "unknown", 7))
3017 else if ((ptr
= strstr(device_uri
, "://")) != NULL
)
3020 ptr
= device_make_and_model
;
3023 options_ptr
< (options
+ sizeof(options
) - 1) && *ptr
;
3025 if (isalnum(*ptr
& 255) || *ptr
== '_' || *ptr
== '-' || *ptr
== '.')
3026 *options_ptr
++ = *ptr
;
3027 else if ((*ptr
== ' ' || *ptr
== '/') && options_ptr
[-1] != '_')
3028 *options_ptr
++ = '_';
3029 else if (*ptr
== '?' || *ptr
== '(')
3033 * Then add the make and model in the printer info, so
3034 * that MacOS clients see something reasonable...
3037 strlcpy(options_ptr
, "&PRINTER_LOCATION=Local+Printer"
3039 sizeof(options
) - (options_ptr
- options
));
3040 options_ptr
+= strlen(options_ptr
);
3042 cgiFormEncode(options_ptr
, device_make_and_model
,
3043 sizeof(options
) - (options_ptr
- options
));
3044 options_ptr
+= strlen(options_ptr
);
3047 * Then copy the device URI...
3050 strlcpy(options_ptr
, "&DEVICE_URI=",
3051 sizeof(options
) - (options_ptr
- options
));
3052 options_ptr
+= strlen(options_ptr
);
3054 cgiFormEncode(options_ptr
, device_uri
,
3055 sizeof(options
) - (options_ptr
- options
));
3056 options_ptr
+= strlen(options_ptr
);
3058 if (options_ptr
< (options
+ sizeof(options
) - 1))
3060 *options_ptr
++ = '|';
3061 cgiFormEncode(options_ptr
, device_make_and_model
,
3062 sizeof(options
) - (options_ptr
- options
));
3066 * Finally, set the form variables for this printer...
3069 cgiSetArray("device_info", i
, device_info
);
3070 cgiSetArray("device_make_and_model", i
, device_make_and_model
);
3071 cgiSetArray("device_options", i
, options
);
3072 cgiSetArray("device_uri", i
, device_uri
);
3081 ippDelete(response
);
3084 * Free the device list...
3087 for (printer_device
= (char *)cupsArrayFirst(printer_devices
);
3089 printer_device
= (char *)cupsArrayNext(printer_devices
))
3090 free(printer_device
);
3092 cupsArrayDelete(printer_devices
);
3097 * See if Samba and the Windows drivers are installed...
3100 if ((datadir
= getenv("CUPS_DATADIR")) == NULL
)
3101 datadir
= CUPS_DATADIR
;
3103 snprintf(line
, sizeof(line
), "%s/drivers/pscript5.dll", datadir
);
3104 if (!access(line
, 0))
3107 * Found Windows 2000 driver file, see if we have smbclient and
3111 if (cupsFileFind("smbclient", getenv("PATH"), 1, line
, sizeof(line
)) &&
3112 cupsFileFind("rpcclient", getenv("PATH"), 1, line
, sizeof(line
)))
3113 cgiSetVariable("HAVE_SAMBA", "Y");
3116 if (!cupsFileFind("smbclient", getenv("PATH"), 1, line
, sizeof(line
)))
3117 fputs("ERROR: smbclient not found!\n", stderr
);
3119 if (!cupsFileFind("rpcclient", getenv("PATH"), 1, line
, sizeof(line
)))
3120 fputs("ERROR: rpcclient not found!\n", stderr
);
3127 * Finally, show the main menu template...
3130 cgiCopyTemplateLang("admin.tmpl");
3137 * 'do_printer_op()' - Do a printer operation.
3141 do_printer_op(http_t
*http
, /* I - HTTP connection */
3142 ipp_op_t op
, /* I - Operation to perform */
3143 const char *title
) /* I - Title of page */
3145 ipp_t
*request
; /* IPP request */
3146 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3147 const char *printer
, /* Printer name (purge-jobs) */
3148 *is_class
; /* Is a class? */
3151 is_class
= cgiGetVariable("IS_CLASS");
3152 printer
= cgiGetVariable("PRINTER_NAME");
3156 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3157 cgiStartHTML(title
);
3158 cgiCopyTemplateLang("error.tmpl");
3164 * Build a printer request, which requires the following
3167 * attributes-charset
3168 * attributes-natural-language
3172 request
= ippNewRequest(op
);
3174 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3175 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3177 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3181 * Do the request and get back a response...
3184 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
3186 if (cupsLastError() > IPP_OK_CONFLICT
)
3188 cgiStartHTML(title
);
3189 cgiShowIPPError(_("Unable to change printer:"));
3194 * Redirect successful updates back to the printer page...
3197 char url
[1024], /* Printer/class URL */
3198 refresh
[1024]; /* Refresh URL */
3201 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3202 cgiFormEncode(uri
, url
, sizeof(uri
));
3203 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=%s", uri
);
3204 cgiSetVariable("refresh_page", refresh
);
3206 cgiStartHTML(title
);
3208 if (op
== IPP_PAUSE_PRINTER
)
3209 cgiCopyTemplateLang("printer-stop.tmpl");
3210 else if (op
== IPP_RESUME_PRINTER
)
3211 cgiCopyTemplateLang("printer-start.tmpl");
3212 else if (op
== CUPS_ACCEPT_JOBS
)
3213 cgiCopyTemplateLang("printer-accept.tmpl");
3214 else if (op
== CUPS_REJECT_JOBS
)
3215 cgiCopyTemplateLang("printer-reject.tmpl");
3216 else if (op
== IPP_PURGE_JOBS
)
3217 cgiCopyTemplateLang("printer-purge.tmpl");
3218 else if (op
== CUPS_SET_DEFAULT
)
3219 cgiCopyTemplateLang("printer-default.tmpl");
3227 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
3231 do_set_allowed_users(http_t
*http
) /* I - HTTP connection */
3233 int i
; /* Looping var */
3234 ipp_t
*request
, /* IPP request */
3235 *response
; /* IPP response */
3236 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3237 const char *printer
, /* Printer name (purge-jobs) */
3238 *is_class
, /* Is a class? */
3239 *users
, /* List of users or groups */
3240 *type
; /* Allow/deny type */
3241 int num_users
; /* Number of users */
3242 char *ptr
, /* Pointer into users string */
3243 *end
, /* Pointer to end of users string */
3244 quote
; /* Quote character */
3245 ipp_attribute_t
*attr
; /* Attribute */
3246 static const char * const attrs
[] = /* Requested attributes */
3248 "requesting-user-name-allowed",
3249 "requesting-user-name-denied"
3253 is_class
= cgiGetVariable("IS_CLASS");
3254 printer
= cgiGetVariable("PRINTER_NAME");
3258 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3259 cgiStartHTML(cgiText(_("Set Allowed Users")));
3260 cgiCopyTemplateLang("error.tmpl");
3265 users
= cgiGetVariable("users");
3266 type
= cgiGetVariable("type");
3268 if (!users
|| !type
||
3269 (strcmp(type
, "requesting-user-name-allowed") &&
3270 strcmp(type
, "requesting-user-name-denied")))
3273 * Build a Get-Printer-Attributes request, which requires the following
3276 * attributes-charset
3277 * attributes-natural-language
3279 * requested-attributes
3282 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
3284 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3285 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3287 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3290 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
3291 "requested-attributes",
3292 (int)(sizeof(attrs
) / sizeof(attrs
[0])), NULL
, attrs
);
3295 * Do the request and get back a response...
3298 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
3300 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3302 ippDelete(response
);
3305 cgiStartHTML(cgiText(_("Set Allowed Users")));
3307 if (cupsLastError() > IPP_OK_CONFLICT
)
3308 cgiShowIPPError(_("Unable to get printer attributes:"));
3310 cgiCopyTemplateLang("users.tmpl");
3317 * Save the changes...
3320 for (num_users
= 0, ptr
= (char *)users
; *ptr
; num_users
++)
3323 * Skip whitespace and commas...
3326 while (*ptr
== ',' || isspace(*ptr
& 255))
3329 if (*ptr
== '\'' || *ptr
== '\"')
3332 * Scan quoted name...
3337 for (end
= ptr
; *end
; end
++)
3344 * Scan space or comma-delimited name...
3347 for (end
= ptr
; *end
; end
++)
3348 if (isspace(*end
& 255) || *end
== ',')
3353 * Advance to the next name...
3360 * Build a CUPS-Add-Printer/Class request, which requires the following
3363 * attributes-charset
3364 * attributes-natural-language
3366 * requesting-user-name-{allowed,denied}
3369 request
= ippNewRequest(is_class
? CUPS_ADD_CLASS
: CUPS_ADD_PRINTER
);
3371 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3372 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3374 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3378 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
3379 "requesting-user-name-allowed", NULL
, "all");
3382 attr
= ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
3383 type
, num_users
, NULL
, NULL
);
3385 for (i
= 0, ptr
= (char *)users
; *ptr
; i
++)
3388 * Skip whitespace and commas...
3391 while (*ptr
== ',' || isspace(*ptr
& 255))
3394 if (*ptr
== '\'' || *ptr
== '\"')
3397 * Scan quoted name...
3402 for (end
= ptr
; *end
; end
++)
3409 * Scan space or comma-delimited name...
3412 for (end
= ptr
; *end
; end
++)
3413 if (isspace(*end
& 255) || *end
== ',')
3418 * Terminate the name...
3428 attr
->values
[i
].string
.text
= strdup(ptr
);
3431 * Advance to the next name...
3439 * Do the request and get back a response...
3442 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
3444 if (cupsLastError() > IPP_OK_CONFLICT
)
3446 cgiStartHTML(cgiText(_("Set Allowed Users")));
3447 cgiShowIPPError(_("Unable to change printer:"));
3452 * Redirect successful updates back to the printer page...
3455 char url
[1024], /* Printer/class URL */
3456 refresh
[1024]; /* Refresh URL */
3459 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3460 cgiFormEncode(uri
, url
, sizeof(uri
));
3461 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=%s", uri
);
3462 cgiSetVariable("refresh_page", refresh
);
3464 cgiStartHTML(cgiText(_("Set Allowed Users")));
3466 cgiCopyTemplateLang(is_class
? "class-modified.tmpl" :
3467 "printer-modified.tmpl");
3476 * 'do_set_sharing()' - Set printer-is-shared value...
3480 do_set_sharing(http_t
*http
) /* I - HTTP connection */
3482 ipp_t
*request
, /* IPP request */
3483 *response
; /* IPP response */
3484 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3485 const char *printer
, /* Printer name */
3486 *is_class
, /* Is a class? */
3487 *shared
; /* Sharing value */
3490 is_class
= cgiGetVariable("IS_CLASS");
3491 printer
= cgiGetVariable("PRINTER_NAME");
3492 shared
= cgiGetVariable("SHARED");
3494 if (!printer
|| !shared
)
3496 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3497 cgiStartHTML(cgiText(_("Set Publishing")));
3498 cgiCopyTemplateLang("error.tmpl");
3504 * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3505 * following attributes:
3507 * attributes-charset
3508 * attributes-natural-language
3513 request
= ippNewRequest(is_class
? CUPS_ADD_CLASS
: CUPS_ADD_PRINTER
);
3515 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3516 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3518 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3521 ippAddBoolean(request
, IPP_TAG_OPERATION
, "printer-is-shared", atoi(shared
));
3524 * Do the request and get back a response...
3527 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3529 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3531 ippDelete(response
);
3534 if (cupsLastError() > IPP_OK_CONFLICT
)
3536 cgiStartHTML(cgiText(_("Set Publishing")));
3537 cgiShowIPPError(_("Unable to change printer-is-shared attribute:"));
3542 * Redirect successful updates back to the printer page...
3545 char url
[1024], /* Printer/class URL */
3546 refresh
[1024]; /* Refresh URL */
3549 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3550 cgiFormEncode(uri
, url
, sizeof(uri
));
3551 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=%s", uri
);
3552 cgiSetVariable("refresh_page", refresh
);
3554 cgiStartHTML(cgiText(_("Set Publishing")));
3555 cgiCopyTemplateLang(is_class
? "class-modified.tmpl" :
3556 "printer-modified.tmpl");
3564 * 'match_string()' - Return the number of matching characters.
3567 static int /* O - Number of matching characters */
3568 match_string(const char *a
, /* I - First string */
3569 const char *b
) /* I - Second string */
3571 int count
; /* Number of matching characters */
3575 * Loop through both strings until we hit the end of either or we find
3576 * a non-matching character. For the purposes of comparison, we ignore
3577 * whitespace and do a case-insensitive comparison so that we have a
3578 * better chance of finding a match...
3581 for (count
= 0; *a
&& *b
; a
++, b
++, count
++)
3584 * Skip leading whitespace characters...
3587 while (isspace(*a
& 255))
3590 while (isspace(*b
& 255))
3594 * Break out if we run out of characters...
3601 * Do a case-insensitive comparison of the next two chars...
3604 if (tolower(*a
& 255) != tolower(*b
& 255))
3613 * End of "$Id: admin.c 5107 2006-02-15 19:14:17Z mike $".