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 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
92 perror("ERROR: Unable to connect to cupsd");
93 fprintf(stderr
, "DEBUG: cupsServer()=\"%s\"\n", cupsServer());
94 fprintf(stderr
, "DEBUG: ippPort()=%d\n", ippPort());
95 fprintf(stderr
, "DEBUG: cupsEncryption()=%d\n", cupsEncryption());
100 * Set the web interface section...
103 cgiSetVariable("SECTION", "admin");
106 * See if we have form data...
109 if (!cgiInitialize())
112 * Nope, send the administration menu...
117 else if ((op
= cgiGetVariable("OP")) != NULL
)
120 * Do the operation...
123 if (!strcmp(op
, "redirect"))
125 const char *url
; /* Redirection URL... */
128 if ((url
= cgiGetVariable("URL")) != NULL
)
129 printf("Location: %s\n\n", url
);
131 puts("Location: /admin\n");
133 else if (!strcmp(op
, "start-printer"))
134 do_printer_op(http
, IPP_RESUME_PRINTER
, cgiText(_("Start Printer")));
135 else if (!strcmp(op
, "stop-printer"))
136 do_printer_op(http
, IPP_PAUSE_PRINTER
, cgiText(_("Stop Printer")));
137 else if (!strcmp(op
, "start-class"))
138 do_printer_op(http
, IPP_RESUME_PRINTER
, cgiText(_("Start Class")));
139 else if (!strcmp(op
, "stop-class"))
140 do_printer_op(http
, IPP_PAUSE_PRINTER
, cgiText(_("Stop Class")));
141 else if (!strcmp(op
, "accept-jobs"))
142 do_printer_op(http
, CUPS_ACCEPT_JOBS
, cgiText(_("Accept Jobs")));
143 else if (!strcmp(op
, "reject-jobs"))
144 do_printer_op(http
, CUPS_REJECT_JOBS
, cgiText(_("Reject Jobs")));
145 else if (!strcmp(op
, "purge-jobs"))
146 do_printer_op(http
, IPP_PURGE_JOBS
, cgiText(_("Purge Jobs")));
147 else if (!strcmp(op
, "set-allowed-users"))
148 do_set_allowed_users(http
);
149 else if (!strcmp(op
, "set-as-default"))
150 do_printer_op(http
, CUPS_SET_DEFAULT
, cgiText(_("Set As Default")));
151 else if (!strcmp(op
, "set-sharing"))
152 do_set_sharing(http
);
153 else if (!strcmp(op
, "add-class"))
154 do_am_class(http
, 0);
155 else if (!strcmp(op
, "add-printer"))
156 do_am_printer(http
, 0);
157 else if (!strcmp(op
, "modify-class"))
158 do_am_class(http
, 1);
159 else if (!strcmp(op
, "modify-printer"))
160 do_am_printer(http
, 1);
161 else if (!strcmp(op
, "delete-class"))
162 do_delete_class(http
);
163 else if (!strcmp(op
, "delete-printer"))
164 do_delete_printer(http
);
165 else if (!strcmp(op
, "set-printer-options"))
166 do_config_printer(http
);
167 else if (!strcmp(op
, "config-server"))
168 do_config_server(http
);
169 else if (!strcmp(op
, "export-samba"))
174 * Bad operation code... Display an error...
177 cgiStartHTML(cgiText(_("Administration")));
178 cgiCopyTemplateLang("error-op.tmpl");
185 * Form data but no operation code... Display an error...
188 cgiStartHTML(cgiText(_("Administration")));
189 cgiCopyTemplateLang("error-op.tmpl");
194 * Close the HTTP server connection...
200 * Return with no errors...
208 * 'do_am_class()' - Add or modify a class.
212 do_am_class(http_t
*http
, /* I - HTTP connection */
213 int modify
) /* I - Modify the printer? */
215 int i
, j
; /* Looping vars */
216 int element
; /* Element number */
217 int num_printers
; /* Number of printers */
218 ipp_t
*request
, /* IPP request */
219 *response
; /* IPP response */
220 ipp_attribute_t
*attr
; /* member-uris attribute */
221 char uri
[HTTP_MAX_URI
]; /* Device or printer URI */
222 const char *name
, /* Pointer to class name */
223 *ptr
; /* Pointer to CGI variable */
224 const char *title
; /* Title of page */
225 static const char * const pattrs
[] = /* Requested printer attributes */
233 title
= cgiText(modify
? _("Modify Class") : _("Add Class"));
234 name
= cgiGetVariable("PRINTER_NAME");
236 if (cgiGetVariable("PRINTER_LOCATION") == NULL
)
239 * Build a CUPS_GET_PRINTERS request, which requires the
240 * following attributes:
243 * attributes-natural-language
247 request
= ippNewRequest(CUPS_GET_PRINTERS
);
249 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
250 NULL
, "ipp://localhost/printers");
253 * Do the request and get back a response...
256 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
259 * Create MEMBER_URIS and MEMBER_NAMES arrays...
262 for (element
= 0, attr
= response
->attrs
;
265 if (attr
->name
&& !strcmp(attr
->name
, "printer-uri-supported"))
267 if ((ptr
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
&&
268 (!name
|| strcasecmp(name
, ptr
+ 1)))
271 * Don't show the current class...
274 cgiSetArray("MEMBER_URIS", element
, attr
->values
[0].string
.text
);
279 for (element
= 0, attr
= response
->attrs
;
282 if (attr
->name
&& !strcmp(attr
->name
, "printer-name"))
284 if (!name
|| strcasecmp(name
, attr
->values
[0].string
.text
))
287 * Don't show the current class...
290 cgiSetArray("MEMBER_NAMES", element
, attr
->values
[0].string
.text
);
295 num_printers
= cgiGetSize("MEMBER_URIS");
305 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
306 * following attributes:
309 * attributes-natural-language
313 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
315 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
316 "localhost", 0, "/classes/%s", name
);
317 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
320 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
321 "requested-attributes",
322 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])),
326 * Do the request and get back a response...
329 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
331 if ((attr
= ippFindAttribute(response
, "member-names", IPP_TAG_NAME
)) != NULL
)
334 * Mark any current members in the class...
337 for (j
= 0; j
< num_printers
; j
++)
338 cgiSetArray("MEMBER_SELECTED", j
, "");
340 for (i
= 0; i
< attr
->num_values
; i
++)
342 for (j
= 0; j
< num_printers
; j
++)
344 if (!strcasecmp(attr
->values
[i
].string
.text
,
345 cgiGetArray("MEMBER_NAMES", j
)))
347 cgiSetArray("MEMBER_SELECTED", j
, "SELECTED");
354 if ((attr
= ippFindAttribute(response
, "printer-info",
355 IPP_TAG_TEXT
)) != NULL
)
356 cgiSetVariable("PRINTER_INFO", attr
->values
[0].string
.text
);
358 if ((attr
= ippFindAttribute(response
, "printer-location",
359 IPP_TAG_TEXT
)) != NULL
)
360 cgiSetVariable("PRINTER_LOCATION", attr
->values
[0].string
.text
);
366 * Update the location and description of an existing printer...
370 cgiCopyTemplateLang("modify-class.tmpl");
375 * Get the name, location, and description for a new printer...
379 cgiCopyTemplateLang("add-class.tmpl");
387 for (ptr
= name
; *ptr
; ptr
++)
388 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
391 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
393 cgiSetVariable("ERROR",
394 cgiText(_("The class name may only contain up to "
395 "127 printable characters and may not "
396 "contain spaces, slashes (/), or the "
397 "pound sign (#).")));
399 cgiCopyTemplateLang("error.tmpl");
405 * Build a CUPS_ADD_CLASS request, which requires the following
409 * attributes-natural-language
413 * printer-is-accepting-jobs
418 request
= ippNewRequest(CUPS_ADD_CLASS
);
420 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
421 "localhost", 0, "/classes/%s",
422 cgiGetVariable("PRINTER_NAME"));
423 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
426 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
427 NULL
, cgiGetVariable("PRINTER_LOCATION"));
429 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
430 NULL
, cgiGetVariable("PRINTER_INFO"));
432 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
434 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
437 if ((num_printers
= cgiGetSize("MEMBER_URIS")) > 0)
439 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "member-uris",
440 num_printers
, NULL
, NULL
);
441 for (i
= 0; i
< num_printers
; i
++)
442 attr
->values
[i
].string
.text
= strdup(cgiGetArray("MEMBER_URIS", i
));
446 * Do the request and get back a response...
449 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
451 if (cupsLastError() > IPP_OK_CONFLICT
)
454 cgiShowIPPError(modify
? _("Unable to modify class:") :
455 _("Unable to add class:"));
460 * Redirect successful updates back to the class page...
463 char refresh
[1024]; /* Refresh URL */
465 cgiFormEncode(uri
, name
, sizeof(uri
));
466 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=/classes/%s",
468 cgiSetVariable("refresh_page", refresh
);
473 cgiCopyTemplateLang("class-modified.tmpl");
475 cgiCopyTemplateLang("class-added.tmpl");
483 * 'do_am_printer()' - Add or modify a printer.
487 do_am_printer(http_t
*http
, /* I - HTTP connection */
488 int modify
) /* I - Modify the printer? */
490 int i
; /* Looping var */
491 int element
; /* Element number */
492 ipp_attribute_t
*attr
, /* Current attribute */
493 *last
; /* Last attribute */
494 ipp_t
*request
, /* IPP request */
495 *response
, /* IPP response */
496 *oldinfo
; /* Old printer information */
497 const cgi_file_t
*file
; /* Uploaded file, if any */
498 const char *var
; /* CGI variable */
499 char uri
[HTTP_MAX_URI
], /* Device or printer URI */
500 *uriptr
; /* Pointer into URI */
501 int maxrate
; /* Maximum baud rate */
502 char baudrate
[255]; /* Baud rate string */
503 const char *name
, /* Pointer to class name */
504 *ptr
; /* Pointer to CGI variable */
505 const char *title
; /* Title of page */
506 static int baudrates
[] = /* Baud rates */
521 fprintf(stderr
, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n",
522 cgiGetVariable("DEVICE_URI"));
524 title
= cgiText(modify
? _("Modify Printer") : _("Add Printer"));
529 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
530 * following attributes:
533 * attributes-natural-language
537 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
539 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
540 "localhost", 0, "/printers/%s",
541 cgiGetVariable("PRINTER_NAME"));
542 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
546 * Do the request and get back a response...
549 oldinfo
= cupsDoRequest(http
, request
, "/");
554 if ((name
= cgiGetVariable("PRINTER_NAME")) == NULL
||
555 cgiGetVariable("PRINTER_LOCATION") == NULL
)
562 * Update the location and description of an existing printer...
566 cgiSetIPPVars(oldinfo
, NULL
, NULL
, NULL
, 0);
568 cgiCopyTemplateLang("modify-printer.tmpl");
573 * Get the name, location, and description for a new printer...
576 cgiCopyTemplateLang("add-printer.tmpl");
587 for (ptr
= name
; *ptr
; ptr
++)
588 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
591 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
593 cgiSetVariable("ERROR",
594 cgiText(_("The printer name may only contain up to "
595 "127 printable characters and may not "
596 "contain spaces, slashes (/), or the "
597 "pound sign (#).")));
599 cgiCopyTemplateLang("error.tmpl");
608 fprintf(stderr
, "DEBUG: file->tempfile=%s\n", file
->tempfile
);
609 fprintf(stderr
, "DEBUG: file->name=%s\n", file
->name
);
610 fprintf(stderr
, "DEBUG: file->filename=%s\n", file
->filename
);
611 fprintf(stderr
, "DEBUG: file->mimetype=%s\n", file
->mimetype
);
614 if ((var
= cgiGetVariable("DEVICE_URI")) == NULL
)
617 * Build a CUPS_GET_DEVICES request, which requires the following
621 * attributes-natural-language
625 fputs("DEBUG: Getting list of devices...\n", stderr
);
627 request
= ippNewRequest(CUPS_GET_DEVICES
);
629 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
630 NULL
, "ipp://localhost/printers/");
633 * Do the request and get back a response...
636 fprintf(stderr
, "DEBUG: http=%p (%s)\n", http
, http
->hostname
);
638 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
640 fputs("DEBUG: Got device list!\n", stderr
);
642 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
647 "ERROR: CUPS-Get-Devices request failed with status %x: %s\n",
648 cupsLastError(), cupsLastErrorString());
651 * Let the user choose...
654 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
656 strlcpy(uri
, attr
->values
[0].string
.text
, sizeof(uri
));
657 if ((uriptr
= strchr(uri
, ':')) != NULL
&& strncmp(uriptr
, "://", 3) == 0)
660 cgiSetVariable("CURRENT_DEVICE_URI", attr
->values
[0].string
.text
);
661 cgiSetVariable("CURRENT_DEVICE_SCHEME", uri
);
665 cgiCopyTemplateLang("choose-device.tmpl");
668 else if (strchr(var
, '/') == NULL
)
670 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
673 * Set the current device URI for the form to the old one...
676 if (strncmp(attr
->values
[0].string
.text
, var
, strlen(var
)) == 0)
677 cgiSetVariable("DEVICE_URI", attr
->values
[0].string
.text
);
681 * User needs to set the full URI...
685 cgiCopyTemplateLang("choose-uri.tmpl");
688 else if (!strncmp(var
, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
691 * Need baud rate, parity, etc.
694 if ((var
= strchr(var
, '?')) != NULL
&&
695 strncmp(var
, "?baud=", 6) == 0)
696 maxrate
= atoi(var
+ 6);
700 for (i
= 0; i
< 10; i
++)
701 if (baudrates
[i
] > maxrate
)
705 sprintf(baudrate
, "%d", baudrates
[i
]);
706 cgiSetArray("BAUDRATES", i
, baudrate
);
710 cgiCopyTemplateLang("choose-serial.tmpl");
713 else if (!file
&& (var
= cgiGetVariable("PPD_NAME")) == NULL
)
718 * Get the PPD file...
721 int fd
; /* PPD file */
722 char filename
[1024]; /* PPD filename */
723 ppd_file_t
*ppd
; /* PPD information */
724 char buffer
[1024]; /* Buffer */
725 int bytes
; /* Number of bytes */
726 http_status_t get_status
; /* Status of GET */
729 /* TODO: Use cupsGetFile() API... */
730 snprintf(uri
, sizeof(uri
), "/printers/%s.ppd", name
);
732 if (httpGet(http
, uri
))
735 while ((get_status
= httpUpdate(http
)) == HTTP_CONTINUE
);
737 if (get_status
!= HTTP_OK
)
739 fprintf(stderr
, "ERROR: Unable to get PPD file %s: %d - %s\n",
740 uri
, get_status
, httpStatus(get_status
));
742 else if ((fd
= cupsTempFd(filename
, sizeof(filename
))) >= 0)
744 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
745 write(fd
, buffer
, bytes
);
749 if ((ppd
= ppdOpenFile(filename
)) != NULL
)
751 if (ppd
->manufacturer
)
752 cgiSetVariable("CURRENT_MAKE", ppd
->manufacturer
);
755 cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd
->nickname
);
762 fprintf(stderr
, "ERROR: Unable to open PPD file %s: %s\n",
763 filename
, ppdErrorString(ppdLastError(&bytes
)));
771 "ERROR: Unable to create temporary file for PPD file: %s\n",
775 else if ((uriptr
= strrchr(cgiGetVariable("DEVICE_URI"), '|')) != NULL
)
778 * Extract make and make/model from device URI string...
781 char make
[1024], /* Make string */
782 *makeptr
; /* Pointer into make */
787 strlcpy(make
, uriptr
, sizeof(make
));
789 if ((makeptr
= strchr(make
, ' ')) != NULL
)
791 else if ((makeptr
= strchr(make
, '-')) != NULL
)
793 else if (!strncasecmp(make
, "laserjet", 8) ||
794 !strncasecmp(make
, "deskjet", 7) ||
795 !strncasecmp(make
, "designjet", 9))
797 else if (!strncasecmp(make
, "phaser", 6))
798 strcpy(make
, "Xerox");
799 else if (!strncasecmp(make
, "stylus", 6))
800 strcpy(make
, "Epson");
802 strcpy(make
, "Generic");
804 cgiSetVariable("CURRENT_MAKE", make
);
805 cgiSetVariable("PPD_MAKE", make
);
806 cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr
);
810 * Build a CUPS_GET_PPDS request, which requires the following
814 * attributes-natural-language
818 request
= ippNewRequest(CUPS_GET_PPDS
);
820 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
821 NULL
, "ipp://localhost/printers/");
823 if ((var
= cgiGetVariable("PPD_MAKE")) != NULL
)
824 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
825 "ppd-make", NULL
, var
);
827 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
828 "requested-attributes", NULL
, "ppd-make");
831 * Do the request and get back a response...
834 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
837 * Got the list of PPDs, see if the user has selected a make...
840 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
845 * Let the user choose a make...
848 for (element
= 0, attr
= response
->attrs
, last
= NULL
;
851 if (attr
->name
&& strcmp(attr
->name
, "ppd-make") == 0)
853 strcasecmp(last
->values
[0].string
.text
,
854 attr
->values
[0].string
.text
) != 0)
856 cgiSetArray("PPD_MAKE", element
, attr
->values
[0].string
.text
);
862 cgiCopyTemplateLang("choose-make.tmpl");
868 * Let the user choose a model...
871 const char *make_model
; /* Current make/model string */
874 if ((make_model
= cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL
)
877 * Scan for "close" matches...
880 int match
, /* Current match */
881 best_match
, /* Best match so far */
882 count
; /* Number of drivers */
883 const char *best
, /* Best matching string */
884 *current
; /* Current string */
887 count
= cgiGetSize("PPD_MAKE_AND_MODEL");
889 for (i
= 0, best_match
= 0, best
= NULL
; i
< count
; i
++)
891 current
= cgiGetArray("PPD_MAKE_AND_MODEL", i
);
892 match
= match_string(make_model
, current
);
894 if (match
> best_match
)
901 if (best_match
> strlen(var
))
904 * Found a match longer than the make...
907 cgiSetVariable("CURRENT_MAKE_AND_MODEL", best
);
912 cgiCopyTemplateLang("choose-model.tmpl");
922 cgiShowIPPError(_("Unable to get list of printer drivers:"));
923 cgiCopyTemplateLang("error.tmpl");
930 * Build a CUPS_ADD_PRINTER request, which requires the following
934 * attributes-natural-language
940 * printer-is-accepting-jobs
944 request
= ippNewRequest(CUPS_ADD_PRINTER
);
946 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
947 "localhost", 0, "/printers/%s",
948 cgiGetVariable("PRINTER_NAME"));
949 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
952 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
953 NULL
, cgiGetVariable("PRINTER_LOCATION"));
955 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
956 NULL
, cgiGetVariable("PRINTER_INFO"));
959 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "ppd-name",
960 NULL
, cgiGetVariable("PPD_NAME"));
962 strlcpy(uri
, cgiGetVariable("DEVICE_URI"), sizeof(uri
));
965 * Strip make and model from URI...
968 if ((uriptr
= strrchr(uri
, '|')) != NULL
)
971 if (!strncmp(uri
, "serial:", 7))
974 * Update serial port URI to include baud rate, etc.
977 if ((uriptr
= strchr(uri
, '?')) == NULL
)
978 uriptr
= uri
+ strlen(uri
);
980 snprintf(uriptr
, sizeof(uri
) - (uriptr
- uri
),
981 "?baud=%s+bits=%s+parity=%s+flow=%s",
982 cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
983 cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
986 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "device-uri",
989 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
991 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
995 * Do the request and get back a response...
999 ippDelete(cupsDoFileRequest(http
, request
, "/admin/", file
->tempfile
));
1001 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
1003 if (cupsLastError() > IPP_OK_CONFLICT
)
1005 cgiStartHTML(title
);
1006 cgiShowIPPError(modify
? _("Unable to modify printer:") :
1007 _("Unable to add printer:"));
1012 * Redirect successful updates back to the printer or set-options pages...
1015 char refresh
[1024]; /* Refresh URL */
1018 cgiFormEncode(uri
, name
, sizeof(uri
));
1021 snprintf(refresh
, sizeof(refresh
),
1022 "5;/admin/?OP=redirect&URL=/printers/%s", uri
);
1024 snprintf(refresh
, sizeof(refresh
),
1025 "5;/admin/?OP=set-printer-options&PRINTER_NAME=%s", uri
);
1027 cgiSetVariable("refresh_page", refresh
);
1029 cgiStartHTML(title
);
1032 cgiCopyTemplateLang("printer-modified.tmpl");
1034 cgiCopyTemplateLang("printer-added.tmpl");
1046 * 'do_config_printer()' - Configure the default options for a printer.
1050 do_config_printer(http_t
*http
) /* I - HTTP connection */
1052 int i
, j
, k
, m
; /* Looping vars */
1053 int have_options
; /* Have options? */
1054 ipp_t
*request
, /* IPP request */
1055 *response
; /* IPP response */
1056 ipp_attribute_t
*attr
; /* IPP attribute */
1057 char uri
[HTTP_MAX_URI
]; /* Job URI */
1058 const char *var
; /* Variable value */
1059 const char *printer
; /* Printer printer name */
1060 const char *filename
; /* PPD filename */
1061 char tempfile
[1024]; /* Temporary filename */
1062 cups_file_t
*in
, /* Input file */
1063 *out
; /* Output file */
1064 char line
[1024]; /* Line from PPD file */
1065 char keyword
[1024], /* Keyword from Default line */
1066 *keyptr
; /* Pointer into keyword... */
1067 ppd_file_t
*ppd
; /* PPD file */
1068 ppd_group_t
*group
; /* Option group */
1069 ppd_option_t
*option
; /* Option */
1070 ppd_attr_t
*protocol
; /* cupsProtocol attribute */
1071 const char *title
; /* Page title */
1074 title
= cgiText(_("Set Printer Options"));
1077 * Get the printer name...
1080 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
1081 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1082 "localhost", 0, "/printers/%s", printer
);
1085 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
1086 cgiStartHTML(title
);
1087 cgiCopyTemplateLang("error.tmpl");
1093 * Get the PPD file...
1096 if ((filename
= cupsGetPPD(printer
)) == NULL
)
1098 cgiStartHTML(title
);
1099 cgiShowIPPError(_("Unable to get PPD file!"));
1104 if ((ppd
= ppdOpenFile(filename
)) == NULL
)
1106 cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i
)));
1107 cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:")));
1108 cgiStartHTML(title
);
1109 cgiCopyTemplateLang("error.tmpl");
1114 if (cgiGetVariable("job_sheets_start") != NULL
||
1115 cgiGetVariable("job_sheets_end") != NULL
)
1120 ppdMarkDefaults(ppd
);
1122 DEBUG_printf(("<P>ppd->num_groups = %d\n"
1123 "<UL>\n", ppd
->num_groups
));
1125 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
1127 DEBUG_printf(("<LI>%s<UL>\n", group
->text
));
1129 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
1130 if ((var
= cgiGetVariable(option
->keyword
)) != NULL
)
1132 DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option
->keyword
, var
));
1134 ppdMarkOption(ppd
, option
->keyword
, var
);
1138 printf("<LI>%s not defined!</LI>\n", option
->keyword
);
1141 DEBUG_puts("</UL></LI>");
1144 DEBUG_printf(("</UL>\n"
1145 "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd
)));
1147 if (!have_options
|| ppdConflicts(ppd
))
1150 * Show the options to the user...
1155 cgiStartHTML("Set Printer Options");
1156 cgiCopyTemplateLang("set-printer-options-header.tmpl");
1158 if (ppdConflicts(ppd
))
1160 for (i
= ppd
->num_groups
, k
= 0, group
= ppd
->groups
; i
> 0; i
--, group
++)
1161 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
1162 if (option
->conflicted
)
1164 cgiSetArray("ckeyword", k
, option
->keyword
);
1165 cgiSetArray("ckeytext", k
, option
->text
);
1169 cgiCopyTemplateLang("option-conflict.tmpl");
1172 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
1176 if (!strcmp(group
->name
, "InstallableOptions"))
1177 cgiSetVariable("GROUP", cgiText(_("Options Installed")));
1179 cgiSetVariable("GROUP", group
->text
);
1181 cgiCopyTemplateLang("option-header.tmpl");
1183 for (j
= group
->num_options
, option
= group
->options
;
1187 if (!strcmp(option
->keyword
, "PageRegion"))
1190 cgiSetVariable("KEYWORD", option
->keyword
);
1191 cgiSetVariable("KEYTEXT", option
->text
);
1193 if (option
->conflicted
)
1194 cgiSetVariable("CONFLICTED", "1");
1196 cgiSetVariable("CONFLICTED", "0");
1198 cgiSetSize("CHOICES", 0);
1199 cgiSetSize("TEXT", 0);
1200 for (k
= 0, m
= 0; k
< option
->num_choices
; k
++)
1203 * Hide custom option values...
1206 if (!strcmp(option
->choices
[k
].choice
, "Custom"))
1209 cgiSetArray("CHOICES", m
, option
->choices
[k
].choice
);
1210 cgiSetArray("TEXT", m
, option
->choices
[k
].text
);
1214 if (option
->choices
[k
].marked
)
1215 cgiSetVariable("DEFCHOICE", option
->choices
[k
].choice
);
1220 case PPD_UI_BOOLEAN
:
1221 cgiCopyTemplateLang("option-boolean.tmpl");
1223 case PPD_UI_PICKONE
:
1224 cgiCopyTemplateLang("option-pickone.tmpl");
1226 case PPD_UI_PICKMANY
:
1227 cgiCopyTemplateLang("option-pickmany.tmpl");
1232 cgiCopyTemplateLang("option-trailer.tmpl");
1236 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
1237 * following attributes:
1239 * attributes-charset
1240 * attributes-natural-language
1244 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1246 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1247 "localhost", 0, "/printers/%s", printer
);
1248 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1252 * Do the request and get back a response...
1255 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1257 if ((attr
= ippFindAttribute(response
, "job-sheets-supported",
1258 IPP_TAG_ZERO
)) != NULL
)
1261 * Add the job sheets options...
1264 cgiSetVariable("GROUP", cgiText(_("Banners")));
1265 cgiCopyTemplateLang("option-header.tmpl");
1267 cgiSetSize("CHOICES", attr
->num_values
);
1268 cgiSetSize("TEXT", attr
->num_values
);
1269 for (k
= 0; k
< attr
->num_values
; k
++)
1271 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1272 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1275 attr
= ippFindAttribute(response
, "job-sheets-default", IPP_TAG_ZERO
);
1277 cgiSetVariable("KEYWORD", "job_sheets_start");
1278 cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
1279 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1280 "" : attr
->values
[0].string
.text
);
1282 cgiCopyTemplateLang("option-pickone.tmpl");
1284 cgiSetVariable("KEYWORD", "job_sheets_end");
1285 cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
1286 cgiSetVariable("DEFCHOICE", attr
== NULL
&& attr
->num_values
> 1 ?
1287 "" : attr
->values
[1].string
.text
);
1289 cgiCopyTemplateLang("option-pickone.tmpl");
1291 cgiCopyTemplateLang("option-trailer.tmpl");
1294 if (ippFindAttribute(response
, "printer-error-policy-supported",
1296 ippFindAttribute(response
, "printer-op-policy-supported",
1300 * Add the error and operation policy options...
1303 cgiSetVariable("GROUP", cgiText(_("Policies")));
1304 cgiCopyTemplateLang("option-header.tmpl");
1310 attr
= ippFindAttribute(response
, "printer-error-policy-supported",
1315 cgiSetSize("CHOICES", attr
->num_values
);
1316 cgiSetSize("TEXT", attr
->num_values
);
1317 for (k
= 0; k
< attr
->num_values
; k
++)
1319 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1320 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1323 attr
= ippFindAttribute(response
, "printer-error-policy",
1326 cgiSetVariable("KEYWORD", "printer_error_policy");
1327 cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
1328 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1329 "" : attr
->values
[0].string
.text
);
1332 cgiCopyTemplateLang("option-pickone.tmpl");
1335 * Operation policy...
1338 attr
= ippFindAttribute(response
, "printer-op-policy-supported",
1343 cgiSetSize("CHOICES", attr
->num_values
);
1344 cgiSetSize("TEXT", attr
->num_values
);
1345 for (k
= 0; k
< attr
->num_values
; k
++)
1347 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
1348 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
1351 attr
= ippFindAttribute(response
, "printer-op-policy", IPP_TAG_ZERO
);
1353 cgiSetVariable("KEYWORD", "printer_op_policy");
1354 cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
1355 cgiSetVariable("DEFCHOICE", attr
== NULL
?
1356 "" : attr
->values
[0].string
.text
);
1358 cgiCopyTemplateLang("option-pickone.tmpl");
1361 cgiCopyTemplateLang("option-trailer.tmpl");
1364 ippDelete(response
);
1368 * Binary protocol support...
1371 if (ppd
->protocols
&& strstr(ppd
->protocols
, "BCP"))
1373 protocol
= ppdFindAttr(ppd
, "cupsProtocol", NULL
);
1375 cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol")));
1376 cgiCopyTemplateLang("option-header.tmpl");
1378 cgiSetSize("CHOICES", 2);
1379 cgiSetSize("TEXT", 2);
1380 cgiSetArray("CHOICES", 0, "None");
1381 cgiSetArray("TEXT", 0, cgiText(_("None")));
1383 if (strstr(ppd
->protocols
, "TBCP"))
1385 cgiSetArray("CHOICES", 1, "TBCP");
1386 cgiSetArray("TEXT", 1, "TBCP");
1390 cgiSetArray("CHOICES", 1, "BCP");
1391 cgiSetArray("TEXT", 1, "BCP");
1394 cgiSetVariable("KEYWORD", "protocol");
1395 cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol")));
1396 cgiSetVariable("DEFCHOICE", protocol
? protocol
->value
: "None");
1398 cgiCopyTemplateLang("option-pickone.tmpl");
1400 cgiCopyTemplateLang("option-trailer.tmpl");
1403 cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
1409 * Set default options...
1412 out
= cupsTempFile2(tempfile
, sizeof(tempfile
));
1413 in
= cupsFileOpen(filename
, "r");
1417 cgiSetVariable("ERROR", strerror(errno
));
1418 cgiStartHTML("Set Printer Options");
1419 cgiCopyTemplateLang("error.tmpl");
1435 while (cupsFileGets(in
, line
, sizeof(line
)))
1437 if (!strncmp(line
, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
1439 else if (strncmp(line
, "*Default", 8))
1440 cupsFilePrintf(out
, "%s\n", line
);
1444 * Get default option name...
1447 strlcpy(keyword
, line
+ 8, sizeof(keyword
));
1449 for (keyptr
= keyword
; *keyptr
; keyptr
++)
1450 if (*keyptr
== ':' || isspace(*keyptr
& 255))
1455 if (!strcmp(keyword
, "PageRegion"))
1456 var
= cgiGetVariable("PageSize");
1458 var
= cgiGetVariable(keyword
);
1461 cupsFilePrintf(out
, "*Default%s: %s\n", keyword
, var
);
1463 cupsFilePrintf(out
, "%s\n", line
);
1467 if ((var
= cgiGetVariable("protocol")) != NULL
)
1468 cupsFilePrintf(out
, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
1474 * Build a CUPS_ADD_PRINTER request, which requires the following
1477 * attributes-charset
1478 * attributes-natural-language
1480 * job-sheets-default
1484 request
= ippNewRequest(CUPS_ADD_PRINTER
);
1486 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1487 "localhost", 0, "/printers/%s",
1488 cgiGetVariable("PRINTER_NAME"));
1489 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1492 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1493 "job-sheets-default", 2, NULL
, NULL
);
1494 attr
->values
[0].string
.text
= strdup(cgiGetVariable("job_sheets_start"));
1495 attr
->values
[1].string
.text
= strdup(cgiGetVariable("job_sheets_end"));
1497 if ((var
= cgiGetVariable("printer_error_policy")) != NULL
)
1498 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1499 "printer-error-policy", NULL
, var
);
1501 if ((var
= cgiGetVariable("printer_op_policy")) != NULL
)
1502 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
1503 "printer-op-policy", NULL
, var
);
1506 * Do the request and get back a response...
1509 ippDelete(cupsDoFileRequest(http
, request
, "/admin/", tempfile
));
1511 if (cupsLastError() > IPP_OK_CONFLICT
)
1513 cgiStartHTML(title
);
1514 cgiShowIPPError(_("Unable to set options:"));
1519 * Redirect successful updates back to the printer page...
1522 char refresh
[1024]; /* Refresh URL */
1525 cgiFormEncode(uri
, printer
, sizeof(uri
));
1526 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=/printers/%s",
1528 cgiSetVariable("refresh_page", refresh
);
1530 cgiStartHTML(title
);
1532 cgiCopyTemplateLang("printer-configured.tmpl");
1545 * 'do_config_server()' - Configure server settings.
1549 do_config_server(http_t
*http
) /* I - HTTP connection */
1551 if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF"))
1554 * Save basic setting changes...
1557 http_status_t status
; /* PUT status */
1558 cups_file_t
*cupsd
; /* cupsd.conf file */
1559 char tempfile
[1024]; /* Temporary new cupsd.conf */
1560 int tempfd
; /* Temporary file descriptor */
1561 cups_file_t
*temp
; /* Temporary file */
1562 char line
[1024], /* Line from cupsd.conf file */
1563 *value
; /* Value on line */
1564 const char *server_root
; /* Location of config files */
1565 int linenum
, /* Line number in file */
1566 in_policy
, /* In a policy section? */
1567 in_cancel_job
, /* In a cancel-job section? */
1568 in_admin_location
, /* In the /admin location? */
1569 in_conf_location
, /* In the /admin/conf location? */
1570 in_root_location
; /* In the / location? */
1571 int remote_printers
, /* Show remote printers */
1572 share_printers
, /* Share local printers */
1573 remote_admin
, /* Remote administration allowed? */
1574 user_cancel_any
, /* Cancel-job policy set? */
1575 debug_logging
; /* LogLevel debug set? */
1576 int wrote_port_listen
, /* Wrote the port/listen lines? */
1577 wrote_browsing
, /* Wrote the browsing lines? */
1578 wrote_policy
, /* Wrote the policy? */
1579 wrote_loglevel
, /* Wrote the LogLevel line? */
1580 wrote_admin_location
, /* Wrote the /admin location? */
1581 wrote_conf_location
, /* Wrote the /admin/conf location? */
1582 wrote_root_location
; /* Wrote the / location? */
1583 int indent
; /* Indentation */
1587 * Get form variables...
1590 remote_printers
= cgiGetVariable("REMOTE_PRINTERS") != NULL
;
1591 share_printers
= cgiGetVariable("SHARE_PRINTERS") != NULL
;
1592 remote_admin
= cgiGetVariable("REMOTE_ADMIN") != NULL
;
1593 user_cancel_any
= cgiGetVariable("USER_CANCEL_ANY") != NULL
;
1594 debug_logging
= cgiGetVariable("DEBUG_LOGGING") != NULL
;
1597 * Locate the cupsd.conf file...
1600 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
1601 server_root
= CUPS_SERVERROOT
;
1603 snprintf(line
, sizeof(line
), "%s/cupsd.conf", server_root
);
1606 * Open the cupsd.conf file...
1609 if ((cupsd
= cupsFileOpen(line
, "r")) == NULL
)
1612 * Unable to open - log an error...
1615 cgiStartHTML(cgiText(_("Change Settings")));
1616 cgiSetVariable("MESSAGE", cgiText(_("Unable to change server settings:")));
1617 cgiSetVariable("ERROR", strerror(errno
));
1618 cgiCopyTemplateLang("error.tmpl");
1626 * Create a temporary file for the new cupsd.conf file...
1629 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
1631 cgiStartHTML(cgiText(_("Change Settings")));
1632 cgiSetVariable("MESSAGE", cgiText(_("Unable to change server settings:")));
1633 cgiSetVariable("ERROR", strerror(errno
));
1634 cgiCopyTemplateLang("error.tmpl");
1638 cupsFileClose(cupsd
);
1642 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
1644 cgiStartHTML(cgiText(_("Change Settings")));
1645 cgiSetVariable("MESSAGE", cgiText(_("Unable to change server settings:")));
1646 cgiSetVariable("ERROR", strerror(errno
));
1647 cgiCopyTemplateLang("error.tmpl");
1653 cupsFileClose(cupsd
);
1658 * Copy the old file to the new, making changes along the way...
1661 in_admin_location
= 0;
1663 in_conf_location
= 0;
1665 in_root_location
= 0;
1667 wrote_admin_location
= 0;
1669 wrote_conf_location
= 0;
1672 wrote_port_listen
= 0;
1673 wrote_root_location
= 0;
1676 while (cupsFileGetConf(cupsd
, line
, sizeof(line
), &value
, &linenum
))
1678 if (!strcasecmp(line
, "Port") || !strcasecmp(line
, "Listen"))
1680 if (!wrote_port_listen
)
1682 wrote_port_listen
= 1;
1684 if (share_printers
|| remote_admin
)
1686 cupsFilePuts(temp
, "# Allow remote access\n");
1687 cupsFilePrintf(temp
, "Listen *:%d\n", ippPort());
1691 cupsFilePuts(temp
, "# Only listen for connections from the local machine.\n");
1692 cupsFilePrintf(temp
, "Listen localhost:%d\n", ippPort());
1695 #ifdef CUPS_DEFAULT_DOMAINSOCKET
1696 cupsFilePuts(temp
, "Listen " CUPS_DEFAULT_DOMAINSOCKET
"\n");
1697 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
1700 else if (!strcasecmp(line
, "Browsing") ||
1701 !strcasecmp(line
, "BrowseAddress") ||
1702 !strcasecmp(line
, "BrowseAllow") ||
1703 !strcasecmp(line
, "BrowseDeny") ||
1704 !strcasecmp(line
, "BrowseOrder"))
1706 if (!wrote_browsing
)
1710 if (remote_printers
|| share_printers
)
1712 if (remote_printers
&& share_printers
)
1713 cupsFilePuts(temp
, "# Enable printer sharing and shared printers.\n");
1714 else if (remote_printers
)
1715 cupsFilePuts(temp
, "# Show shared printers on the local network.\n");
1717 cupsFilePuts(temp
, "# Share local printers on the local network.\n");
1719 cupsFilePuts(temp
, "Browsing On\n");
1720 cupsFilePuts(temp
, "BrowseOrder allow,deny\n");
1722 if (remote_printers
)
1723 cupsFilePuts(temp
, "BrowseAllow @LOCAL\n");
1726 cupsFilePuts(temp
, "BrowseAddress @LOCAL\n");
1730 cupsFilePuts(temp
, "# Disable printer sharing and shared printers.\n");
1731 cupsFilePuts(temp
, "Browsing Off\n");
1735 else if (!strcasecmp(line
, "LogLevel"))
1741 cupsFilePuts(temp
, "# Show troubleshooting information in error_log.\n");
1742 cupsFilePuts(temp
, "LogLevel debug\n");
1746 cupsFilePuts(temp
, "# Show general information in error_log.\n");
1747 cupsFilePuts(temp
, "LogLevel info\n");
1750 else if (!strcasecmp(line
, "<Policy") && !strcasecmp(value
, "default"))
1754 cupsFilePrintf(temp
, "%s %s>\n", line
, value
);
1757 else if (!strcasecmp(line
, "</Policy>"))
1764 if (!user_cancel_any
)
1765 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
1766 " <Limit Cancel-Job>\n"
1767 " Order deny,allow\n"
1775 cupsFilePuts(temp
, "</Policy>\n");
1777 else if (!strcasecmp(line
, "<Location"))
1780 if (!strcmp(value
, "/admin"))
1781 in_admin_location
= 1;
1782 if (!strcmp(value
, "/admin/conf"))
1783 in_conf_location
= 1;
1784 else if (!strcmp(value
, "/"))
1785 in_root_location
= 1;
1787 cupsFilePrintf(temp
, "%s %s>\n", line
, value
);
1789 else if (!strcasecmp(line
, "</Location>"))
1792 if (in_admin_location
)
1794 wrote_admin_location
= 1;
1797 cupsFilePuts(temp
, " # Allow remote administration...\n");
1799 cupsFilePuts(temp
, " # Restrict access to the admin pages...\n");
1801 cupsFilePuts(temp
, " Order allow,deny\n");
1804 cupsFilePuts(temp
, " Allow @LOCAL\n");
1806 cupsFilePuts(temp
, " Allow localhost\n");
1808 else if (in_conf_location
)
1810 wrote_conf_location
= 1;
1813 cupsFilePuts(temp
, " # Allow remote access to the configuration files...\n");
1815 cupsFilePuts(temp
, " # Restrict access to the configuration files...\n");
1817 cupsFilePuts(temp
, " Order allow,deny\n");
1820 cupsFilePuts(temp
, " Allow @LOCAL\n");
1822 cupsFilePuts(temp
, " Allow localhost\n");
1824 else if (in_root_location
)
1826 wrote_root_location
= 1;
1828 if (remote_admin
&& share_printers
)
1829 cupsFilePuts(temp
, " # Allow shared printing and remote administration...\n");
1830 else if (remote_admin
)
1831 cupsFilePuts(temp
, " # Allow remote administration...\n");
1832 else if (share_printers
)
1833 cupsFilePuts(temp
, " # Allow shared printing...\n");
1835 cupsFilePuts(temp
, " # Restrict access to the server...\n");
1837 cupsFilePuts(temp
, " Order allow,deny\n");
1839 if (remote_admin
|| share_printers
)
1840 cupsFilePuts(temp
, " Allow @LOCAL\n");
1842 cupsFilePuts(temp
, " Allow localhost\n");
1845 in_admin_location
= 0;
1846 in_conf_location
= 0;
1847 in_root_location
= 0;
1849 cupsFilePuts(temp
, "</Location>\n");
1851 else if (!strcasecmp(line
, "<Limit") && in_policy
)
1854 * See if the policy limit is for the Cancel-Job operation...
1857 char *valptr
; /* Pointer into value */
1862 if (!strcasecmp(value
, "cancel-job"))
1865 * Don't write anything for this limit section...
1872 cupsFilePrintf(temp
, " %s", line
);
1876 for (valptr
= value
; !isspace(*valptr
& 255) && *valptr
; valptr
++);
1881 if (!strcasecmp(value
, "cancel-job"))
1884 * Write everything except for this definition...
1890 cupsFilePrintf(temp
, " %s", value
);
1892 for (value
= valptr
; isspace(*value
& 255); value
++);
1895 cupsFilePuts(temp
, ">\n");
1898 else if (!strcasecmp(line
, "</Limit>") && in_cancel_job
)
1902 if (in_cancel_job
== 1)
1903 cupsFilePuts(temp
, " </Limit>\n");
1907 if (!user_cancel_any
)
1908 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
1909 " <Limit Cancel-Job>\n"
1910 " Order deny,allow\n"
1911 " Require user @OWNER @SYSTEM\n"
1916 else if ((in_admin_location
|| in_conf_location
|| in_root_location
) &&
1917 (!strcasecmp(line
, "Allow") || !strcasecmp(line
, "Deny") ||
1918 !strcasecmp(line
, "Order")))
1920 else if (in_cancel_job
== 2)
1922 else if (!strcasecmp(line
, "<Limit") && value
)
1923 cupsFilePrintf(temp
, " %s %s>\n", line
, value
);
1924 else if (line
[0] == '<')
1928 cupsFilePrintf(temp
, "%*s%s %s>\n", indent
, "", line
, value
);
1936 cupsFilePrintf(temp
, "%*s%s\n", indent
, "", line
);
1940 cupsFilePrintf(temp
, "%*s%s %s\n", indent
, "", line
, value
);
1942 cupsFilePrintf(temp
, "%*s%s\n", indent
, "", line
);
1946 * Write any missing info...
1949 if (!wrote_browsing
)
1951 if (remote_printers
|| share_printers
)
1953 if (remote_printers
&& share_printers
)
1954 cupsFilePuts(temp
, "# Enable printer sharing and shared printers.\n");
1955 else if (remote_printers
)
1956 cupsFilePuts(temp
, "# Show shared printers on the local network.\n");
1958 cupsFilePuts(temp
, "# Share local printers on the local network.\n");
1960 cupsFilePuts(temp
, "Browsing On\n");
1961 cupsFilePuts(temp
, "BrowseOrder allow,deny\n");
1963 if (remote_printers
)
1964 cupsFilePuts(temp
, "BrowseAllow @LOCAL\n");
1967 cupsFilePuts(temp
, "BrowseAddress @LOCAL\n");
1971 cupsFilePuts(temp
, "# Disable printer sharing and shared printers.\n");
1972 cupsFilePuts(temp
, "Browsing Off\n");
1976 if (!wrote_loglevel
)
1980 cupsFilePuts(temp
, "# Show troubleshooting information in error_log.\n");
1981 cupsFilePuts(temp
, "LogLevel debug\n");
1985 cupsFilePuts(temp
, "# Show general information in error_log.\n");
1986 cupsFilePuts(temp
, "LogLevel info\n");
1990 if (!wrote_port_listen
)
1992 if (share_printers
|| remote_admin
)
1994 cupsFilePuts(temp
, "# Allow remote access\n");
1995 cupsFilePrintf(temp
, "Listen *:%d\n", ippPort());
1999 cupsFilePuts(temp
, "# Only listen for connections from the local machine.\n");
2000 cupsFilePrintf(temp
, "Listen localhost:%d\n", ippPort());
2003 #ifdef CUPS_DEFAULT_DOMAINSOCKET
2004 cupsFilePuts(temp
, "Listen " CUPS_DEFAULT_DOMAINSOCKET
"\n");
2005 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
2008 if (!wrote_root_location
)
2010 if (remote_admin
&& share_printers
)
2011 cupsFilePuts(temp
, "# Allow shared printing and remote administration...\n");
2012 else if (remote_admin
)
2013 cupsFilePuts(temp
, "# Allow remote administration...\n");
2014 else if (share_printers
)
2015 cupsFilePuts(temp
, "# Allow shared printing...\n");
2017 cupsFilePuts(temp
, "# Restrict access to the server...\n");
2019 cupsFilePuts(temp
, "<Location />\n"
2020 " Order allow,deny\n");
2022 if (remote_admin
|| share_printers
)
2023 cupsFilePuts(temp
, " Allow @LOCAL\n");
2025 cupsFilePuts(temp
, " Allow localhost\n");
2027 cupsFilePuts(temp
, "</Location>\n");
2030 if (!wrote_admin_location
)
2033 cupsFilePuts(temp
, "# Allow remote administration...\n");
2035 cupsFilePuts(temp
, "# Restrict access to the admin pages...\n");
2037 cupsFilePuts(temp
, "<Location /admin>\n"
2038 " Order allow,deny\n");
2041 cupsFilePuts(temp
, " Allow @LOCAL\n");
2043 cupsFilePuts(temp
, " Allow localhost\n");
2045 cupsFilePuts(temp
, "</Location>\n");
2048 if (!wrote_conf_location
)
2051 cupsFilePuts(temp
, "# Allow remote access to the configuration files...\n");
2053 cupsFilePuts(temp
, "# Restrict access to the configuration files...\n");
2055 cupsFilePuts(temp
, "<Location /admin/conf>\n"
2057 " Require user @SYSTEM\n"
2058 " Order allow,deny\n");
2061 cupsFilePuts(temp
, " Allow @LOCAL\n");
2063 cupsFilePuts(temp
, " Allow localhost\n");
2065 cupsFilePuts(temp
, "</Location>\n");
2070 cupsFilePuts(temp
, "<Policy default>\n"
2071 " # Job-related operations must be done by the owner or an adminstrator...\n"
2072 " <Limit Send-Document Send-URI Hold-Job Release-Job "
2073 "Restart-Job Purge-Jobs Set-Job-Attributes "
2074 "Create-Job-Subscription Renew-Subscription "
2075 "Cancel-Subscription Get-Notifications Reprocess-Job "
2076 "Cancel-Current-Job Suspend-Current-Job Resume-Job "
2078 " Require user @OWNER @SYSTEM\n"
2079 " Order deny,allow\n"
2081 " # All administration operations require an adminstrator to authenticate...\n"
2082 " <Limit Pause-Printer Resume-Printer "
2083 "Set-Printer-Attributes Enable-Printer "
2084 "Disable-Printer Pause-Printer-After-Current-Job "
2085 "Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer "
2086 "Activate-Printer Restart-Printer Shutdown-Printer "
2087 "Startup-Printer Promote-Job Schedule-Job-After "
2088 "CUPS-Add-Printer CUPS-Delete-Printer "
2089 "CUPS-Add-Class CUPS-Delete-Class "
2090 "CUPS-Accept-Jobs CUPS-Reject-Jobs "
2091 "CUPS-Set-Default CUPS-Add-Device CUPS-Delete-Device>\n"
2093 " Require user @SYSTEM\n"
2094 " Order deny,allow\n"
2097 if (!user_cancel_any
)
2098 cupsFilePuts(temp
, " # Only the owner or an administrator can cancel a job...\n"
2099 " <Limit Cancel-Job>\n"
2100 " Require user @OWNER @SYSTEM\n"
2101 " Order deny,allow\n"
2104 cupsFilePuts(temp
, " <Limit All>\n"
2105 " Order deny,allow\n"
2110 cupsFileClose(cupsd
);
2111 cupsFileClose(temp
);
2114 * Upload the configuration file to the server...
2117 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
2119 if (status
!= HTTP_CREATED
)
2121 cgiSetVariable("MESSAGE", cgiText(_("Unable to upload cupsd.conf file:")));
2122 cgiSetVariable("ERROR", httpStatus(status
));
2123 cgiStartHTML(cgiText(_("Change Settings")));
2124 cgiCopyTemplateLang("error.tmpl");
2128 cgiSetVariable("refresh_page", "5;/admin/?OP=redirect");
2130 cgiStartHTML(cgiText(_("Change Settings")));
2131 cgiCopyTemplateLang("restart.tmpl");
2138 else if (cgiIsPOST())
2141 * Save hand-edited config file...
2144 http_status_t status
; /* PUT status */
2145 char tempfile
[1024]; /* Temporary new cupsd.conf */
2146 int tempfd
; /* Temporary file descriptor */
2147 cups_file_t
*temp
; /* Temporary file */
2148 const char *start
, /* Start of line */
2149 *end
; /* End of line */
2153 * Create a temporary file for the new cupsd.conf file...
2156 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
2158 cgiStartHTML(cgiText(_("Edit Configuration File")));
2159 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
2160 cgiSetVariable("ERROR", strerror(errno
));
2161 cgiCopyTemplateLang("error.tmpl");
2168 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
2170 cgiStartHTML(cgiText(_("Edit Configuration File")));
2171 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
2172 cgiSetVariable("ERROR", strerror(errno
));
2173 cgiCopyTemplateLang("error.tmpl");
2183 * Copy the cupsd.conf text from the form variable...
2186 start
= cgiGetVariable("CUPSDCONF");
2189 if ((end
= strstr(start
, "\r\n")) == NULL
)
2190 if ((end
= strstr(start
, "\n")) == NULL
)
2191 end
= start
+ strlen(start
);
2193 cupsFileWrite(temp
, start
, end
- start
);
2194 cupsFilePutChar(temp
, '\n');
2198 else if (*end
== '\n')
2204 cupsFileClose(temp
);
2207 * Upload the configuration file to the server...
2210 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
2212 if (status
!= HTTP_CREATED
)
2214 cgiSetVariable("MESSAGE", cgiText(_("Unable to upload cupsd.conf file:")));
2215 cgiSetVariable("ERROR", httpStatus(status
));
2217 cgiStartHTML(cgiText(_("Edit Configuration File")));
2218 cgiCopyTemplateLang("error.tmpl");
2222 cgiSetVariable("refresh_page", "5;/admin/?OP=redirect");
2224 cgiStartHTML(cgiText(_("Edit Configuration File")));
2225 cgiCopyTemplateLang("restart.tmpl");
2234 struct stat info
; /* cupsd.conf information */
2235 cups_file_t
*cupsd
; /* cupsd.conf file */
2236 char *buffer
; /* Buffer for entire file */
2237 char filename
[1024]; /* Filename */
2238 const char *server_root
; /* Location of config files */
2242 * Locate the cupsd.conf file...
2245 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
2246 server_root
= CUPS_SERVERROOT
;
2248 snprintf(filename
, sizeof(filename
), "%s/cupsd.conf", server_root
);
2251 * Figure out the size...
2254 if (stat(filename
, &info
))
2256 cgiStartHTML(cgiText(_("Edit Configuration File")));
2257 cgiSetVariable("MESSAGE", cgiText(_("Unable to access cupsd.conf file:")));
2258 cgiSetVariable("ERROR", strerror(errno
));
2259 cgiCopyTemplateLang("error.tmpl");
2266 if (info
.st_size
> (1024 * 1024))
2268 cgiStartHTML(cgiText(_("Edit Configuration File")));
2269 cgiSetVariable("MESSAGE", cgiText(_("Unable to access cupsd.conf file:")));
2270 cgiSetVariable("ERROR",
2271 cgiText(_("Unable to edit cupsd.conf files larger than "
2273 cgiCopyTemplateLang("error.tmpl");
2276 fprintf(stderr
, "ERROR: \"%s\" too large (%ld) to edit!\n", filename
,
2277 (long)info
.st_size
);
2282 * Open the cupsd.conf file...
2285 if ((cupsd
= cupsFileOpen(filename
, "r")) == NULL
)
2288 * Unable to open - log an error...
2291 cgiStartHTML(cgiText(_("Edit Configuration File")));
2292 cgiSetVariable("MESSAGE", cgiText(_("Unable to access cupsd.conf file:")));
2293 cgiSetVariable("ERROR", strerror(errno
));
2294 cgiCopyTemplateLang("error.tmpl");
2302 * Allocate memory and load the file into a string buffer...
2305 buffer
= calloc(1, info
.st_size
+ 1);
2307 cupsFileRead(cupsd
, buffer
, info
.st_size
);
2308 cupsFileClose(cupsd
);
2310 cgiSetVariable("CUPSDCONF", buffer
);
2314 * Show the current config file...
2317 cgiStartHTML("Edit Configuration File");
2319 printf("<!-- \"%s\" -->\n", filename
);
2321 cgiCopyTemplateLang("edit-config.tmpl");
2329 * 'do_delete_class()' - Delete a class...
2333 do_delete_class(http_t
*http
) /* I - HTTP connection */
2335 ipp_t
*request
; /* IPP request */
2336 char uri
[HTTP_MAX_URI
]; /* Job URI */
2337 const char *pclass
; /* Printer class name */
2340 cgiStartHTML(cgiText(_("Delete Class")));
2342 if (cgiGetVariable("CONFIRM") == NULL
)
2344 cgiCopyTemplateLang("class-confirm.tmpl");
2349 if ((pclass
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2350 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2351 "localhost", 0, "/classes/%s", pclass
);
2354 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2355 cgiCopyTemplateLang("error.tmpl");
2361 * Build a CUPS_DELETE_CLASS request, which requires the following
2364 * attributes-charset
2365 * attributes-natural-language
2369 request
= ippNewRequest(CUPS_DELETE_CLASS
);
2371 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2375 * Do the request and get back a response...
2378 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
2380 if (cupsLastError() > IPP_OK_CONFLICT
)
2381 cgiShowIPPError(_("Unable to delete class:"));
2383 cgiCopyTemplateLang("class-deleted.tmpl");
2390 * 'do_delete_printer()' - Delete a printer...
2394 do_delete_printer(http_t
*http
) /* I - HTTP connection */
2396 ipp_t
*request
; /* IPP request */
2397 char uri
[HTTP_MAX_URI
]; /* Job URI */
2398 const char *printer
; /* Printer printer name */
2401 cgiStartHTML(cgiText(_("Delete Printer")));
2403 if (cgiGetVariable("CONFIRM") == NULL
)
2405 cgiCopyTemplateLang("printer-confirm.tmpl");
2410 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2411 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2412 "localhost", 0, "/printers/%s", printer
);
2415 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2416 cgiCopyTemplateLang("error.tmpl");
2422 * Build a CUPS_DELETE_PRINTER request, which requires the following
2425 * attributes-charset
2426 * attributes-natural-language
2430 request
= ippNewRequest(CUPS_DELETE_PRINTER
);
2432 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2436 * Do the request and get back a response...
2439 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
2441 if (cupsLastError() > IPP_OK_CONFLICT
)
2442 cgiShowIPPError(_("Unable to delete printer:"));
2444 cgiCopyTemplateLang("printer-deleted.tmpl");
2451 * 'do_export()' - Export printers to Samba...
2455 do_export(http_t
*http
) /* I - HTTP connection */
2457 int i
, j
; /* Looping vars */
2458 ipp_t
*request
, /* IPP request */
2459 *response
; /* IPP response */
2460 const char *username
, /* Samba username */
2461 *password
, /* Samba password */
2462 *export_all
; /* Export all printers? */
2463 int export_count
, /* Number of printers to export */
2464 printer_count
; /* Number of available printers */
2471 cgiStartHTML(cgiText(_("Export Printers to Samba")));
2477 username
= cgiGetVariable("USERNAME");
2478 password
= cgiGetVariable("PASSWORD");
2479 export_all
= cgiGetVariable("EXPORT_ALL");
2480 export_count
= cgiGetSize("EXPORT_NAME");
2482 if (username
&& *username
&& password
&& *password
&& export_count
<= 1000)
2488 char userpass
[1024], /* Username%password */
2489 *argv
[1005]; /* Arguments */
2490 int argc
; /* Number of arguments */
2491 int pid
; /* Process ID of child */
2492 int status
; /* Status of command */
2495 fputs("DEBUG: Export printers...\n", stderr
);
2498 * Create the command-line for cupsaddsmb...
2501 snprintf(userpass
, sizeof(userpass
), "%s%%%s", username
, password
);
2503 argv
[0] = "cupsaddsmb";
2510 argv
[argc
++] = "-a";
2513 for (i
= 0; i
< export_count
; i
++)
2514 argv
[argc
++] = (char *)cgiGetArray("EXPORT_NAME", i
);
2520 * Run the command...
2523 if ((pid
= fork()) == 0)
2526 * Child goes here...
2530 open("/dev/null", O_RDONLY
);
2534 execvp("cupsaddsmb", argv
);
2535 perror("ERROR: Unable to execute cupsaddsmb");
2539 cgiSetVariable("ERROR", cgiText(_("Unable to fork process!")));
2543 * Parent goes here, wait for child to finish...
2546 while (wait(&status
) < 0);
2550 char message
[1024]; /* Error message */
2553 if (WIFEXITED(status
))
2555 switch (WEXITSTATUS(status
))
2558 cgiSetVariable("ERROR", cgiText(_("Unable to connect to server!")));
2562 cgiSetVariable("ERROR", cgiText(_("Unable to get printer "
2567 cgiSetVariable("ERROR", cgiText(_("Unable to convert PPD file!")));
2571 cgiSetVariable("ERROR", cgiText(_("Unable to copy Windows 2000 "
2572 "printer driver files!")));
2576 cgiSetVariable("ERROR", cgiText(_("Unable to install Windows "
2577 "2000 printer driver files!")));
2581 cgiSetVariable("ERROR", cgiText(_("Unable to copy Windows 9x "
2582 "printer driver files!")));
2586 cgiSetVariable("ERROR", cgiText(_("Unable to install Windows "
2587 "9x printer driver files!")));
2591 cgiSetVariable("ERROR", cgiText(_("Unable to set Windows "
2592 "printer driver!")));
2596 cgiSetVariable("ERROR", cgiText(_("No printer drivers found!")));
2600 cgiSetVariable("ERROR", cgiText(_("Unable to execute "
2601 "cupsaddsmb command!")));
2605 snprintf(message
, sizeof(message
),
2606 cgiText(_("cupsaddsmb failed with status %d")),
2607 WEXITSTATUS(status
));
2609 cgiSetVariable("ERROR", message
);
2615 snprintf(message
, sizeof(message
),
2616 cgiText(_("cupsaddsmb crashed on signal %d")),
2619 cgiSetVariable("ERROR", message
);
2624 cgiCopyTemplateLang("samba-exported");
2630 else if (username
&& !*username
)
2631 cgiSetVariable("ERROR",
2632 cgiText(_("A Samba username is required to export "
2633 "printer drivers!")));
2634 else if (username
&& (!password
|| !*password
))
2635 cgiSetVariable("ERROR",
2636 cgiText(_("A Samba password is required to export "
2637 "printer drivers!")));
2640 * Get list of available printers...
2643 cgiSetSize("PRINTER_NAME", 0);
2644 cgiSetSize("PRINTER_EXPORT", 0);
2646 request
= ippNewRequest(CUPS_GET_PRINTERS
);
2648 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
2651 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
2652 "printer-type-mask", CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
2653 CUPS_PRINTER_IMPLICIT
);
2655 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2656 "requested-attributes", NULL
, "printer-name");
2658 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2660 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
2661 ippDelete(response
);
2665 printer_count
= cgiGetSize("PRINTER_NAME");
2667 for (i
= 0; i
< printer_count
; i
++)
2669 for (j
= 0; j
< export_count
; j
++)
2670 if (!strcasecmp(cgiGetArray("PRINTER_NAME", i
),
2671 cgiGetArray("EXPORT_NAME", j
)))
2674 cgiSetArray("PRINTER_EXPORT", i
, j
< export_count
? "Y" : "");
2683 cgiCopyTemplateLang("samba-export.tmpl");
2689 * 'do_menu()' - Show the main menu...
2693 do_menu(http_t
*http
) /* I - HTTP connection */
2695 cups_file_t
*cupsd
; /* cupsd.conf file */
2696 char line
[1024], /* Line from cupsd.conf file */
2697 *value
; /* Value on line */
2698 const char *server_root
; /* Location of config files */
2699 const char *datadir
; /* Location of data files */
2700 ipp_t
*request
, /* IPP request */
2701 *response
; /* IPP response */
2702 ipp_attribute_t
*attr
; /* IPP attribute */
2706 * Locate the cupsd.conf file...
2709 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
2710 server_root
= CUPS_SERVERROOT
;
2712 snprintf(line
, sizeof(line
), "%s/cupsd.conf", server_root
);
2714 cgiStartHTML(cgiText(_("Administration")));
2716 printf("<!-- \"%s\" -->\n", line
);
2719 * Open the cupsd.conf file...
2722 if ((cupsd
= cupsFileOpen(line
, "r")) == NULL
)
2725 * Unable to open - log an error...
2728 cgiSetVariable("MESSAGE", cgiText(_("Unable to open cupsd.conf file:")));
2729 cgiSetVariable("ERROR", strerror(errno
));
2730 cgiCopyTemplateLang("error.tmpl");
2738 * Read the file, keeping track of what settings are enabled...
2741 int remote_access
= 0, /* Remote access allowed? */
2742 remote_admin
= 0, /* Remote administration allowed? */
2743 browsing
= 1, /* Browsing enabled? */
2744 browse_allow
= 1, /* Browse address set? */
2745 browse_address
= 0, /* Browse address set? */
2746 cancel_policy
= 1, /* Cancel-job policy set? */
2747 debug_logging
= 0; /* LogLevel debug set? */
2748 int linenum
= 0, /* Line number in file */
2749 in_policy
= 0, /* In a policy section? */
2750 in_cancel_job
= 0, /* In a cancel-job section? */
2751 in_admin_location
= 0; /* In the /admin location? */
2754 while (cupsFileGetConf(cupsd
, line
, sizeof(line
), &value
, &linenum
))
2756 if (!strcasecmp(line
, "Port"))
2760 else if (!strcasecmp(line
, "Listen"))
2762 char *port
; /* Pointer to port number, if any */
2765 if ((port
= strrchr(value
, ':')) != NULL
)
2768 if (strcasecmp(value
, "localhost") && strcmp(value
, "127.0.0.1"))
2771 else if (!strcasecmp(line
, "Browsing"))
2773 browsing
= !strcasecmp(value
, "yes") || !strcasecmp(value
, "on") ||
2774 !strcasecmp(value
, "true");
2776 else if (!strcasecmp(line
, "BrowseAddress"))
2780 else if (!strcasecmp(line
, "BrowseAllow"))
2784 else if (!strcasecmp(line
, "BrowseOrder"))
2786 browse_allow
= !strncasecmp(value
, "deny,", 5);
2788 else if (!strcasecmp(line
, "LogLevel"))
2790 debug_logging
= !strncasecmp(value
, "debug", 5);
2792 else if (!strcasecmp(line
, "<Policy") && !strcasecmp(value
, "default"))
2796 else if (!strcasecmp(line
, "</Policy>"))
2800 else if (!strcasecmp(line
, "<Limit") && in_policy
)
2803 * See if the policy limit is for the Cancel-Job operation...
2806 char *valptr
; /* Pointer into value */
2811 for (valptr
= value
; !isspace(*valptr
& 255) && *valptr
; valptr
++);
2816 if (!strcasecmp(value
, "cancel-job") || !strcasecmp(value
, "all"))
2822 for (value
= valptr
; isspace(*value
& 255); value
++);
2825 else if (!strcasecmp(line
, "</Limit>"))
2829 else if (!strcasecmp(line
, "Require") && in_cancel_job
)
2833 else if (!strcasecmp(line
, "<Location") && !strcasecmp(value
, "/admin"))
2835 in_admin_location
= 1;
2837 else if (!strcasecmp(line
, "</Location>"))
2839 in_admin_location
= 0;
2841 else if (!strcasecmp(line
, "Allow") && in_admin_location
&&
2842 strcasecmp(value
, "localhost") && strcasecmp(value
, "127.0.0.1"))
2848 cupsFileClose(cupsd
);
2850 if (browsing
&& browse_allow
)
2851 cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
2853 if (remote_access
&& browsing
&& browse_address
)
2854 cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2856 if (remote_access
&& remote_admin
)
2857 cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2860 cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2863 cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2867 * Get the list of printers and their devices...
2870 request
= ippNewRequest(CUPS_GET_PRINTERS
);
2872 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2873 "requested-attributes", NULL
, "device-uri");
2875 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type",
2876 CUPS_PRINTER_LOCAL
);
2877 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type-mask",
2878 CUPS_PRINTER_LOCAL
);
2880 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2883 * Got the printer list, now load the devices...
2886 int i
; /* Looping var */
2887 cups_array_t
*printer_devices
; /* Printer devices for local printers */
2888 char *printer_device
; /* Current printer device */
2892 * Allocate an array and copy the device strings...
2895 printer_devices
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2897 for (attr
= ippFindAttribute(response
, "device-uri", IPP_TAG_URI
);
2899 attr
= ippFindNextAttribute(response
, "device-uri", IPP_TAG_URI
))
2901 cupsArrayAdd(printer_devices
, strdup(attr
->values
[0].string
.text
));
2905 * Free the printer list and get the device list...
2908 ippDelete(response
);
2910 request
= ippNewRequest(CUPS_GET_DEVICES
);
2912 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2915 * Got the device list, let's parse it...
2918 const char *device_uri
, /* device-uri attribute value */
2919 *device_make_and_model
, /* device-make-and-model value */
2920 *device_info
; /* device-info value */
2923 for (i
= 0, attr
= response
->attrs
; attr
; attr
= attr
->next
)
2926 * Skip leading attributes until we hit a device...
2929 while (attr
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
2936 * Pull the needed attributes from this device...
2940 device_make_and_model
= NULL
;
2943 while (attr
&& attr
->group_tag
== IPP_TAG_PRINTER
)
2945 if (!strcmp(attr
->name
, "device-info") &&
2946 attr
->value_tag
== IPP_TAG_TEXT
)
2947 device_info
= attr
->values
[0].string
.text
;
2949 if (!strcmp(attr
->name
, "device-make-and-model") &&
2950 attr
->value_tag
== IPP_TAG_TEXT
)
2951 device_make_and_model
= attr
->values
[0].string
.text
;
2953 if (!strcmp(attr
->name
, "device-uri") &&
2954 attr
->value_tag
== IPP_TAG_URI
)
2955 device_uri
= attr
->values
[0].string
.text
;
2961 * See if we have everything needed...
2964 if (device_info
&& device_make_and_model
&& device_uri
&&
2965 strcasecmp(device_make_and_model
, "unknown") &&
2966 strchr(device_uri
, ':'))
2969 * Yes, now see if there is already a printer for this
2973 if (!cupsArrayFind(printer_devices
, (void *)device_uri
))
2976 * Not found, so it must be a new printer...
2979 char options
[1024], /* Form variables for this device */
2980 *options_ptr
; /* Pointer into string */
2981 const char *ptr
; /* Pointer into device string */
2985 * Format the printer name variable for this device...
2987 * We use the device-info string first, then device-uri,
2988 * and finally device-make-and-model to come up with a
2992 strcpy(options
, "PRINTER_NAME=");
2993 options_ptr
= options
+ strlen(options
);
2995 if (strncasecmp(device_info
, "unknown", 7))
2997 else if ((ptr
= strstr(device_uri
, "://")) != NULL
)
3000 ptr
= device_make_and_model
;
3003 options_ptr
< (options
+ sizeof(options
) - 1) && *ptr
;
3005 if (isalnum(*ptr
& 255) || *ptr
== '_' || *ptr
== '-' || *ptr
== '.')
3006 *options_ptr
++ = *ptr
;
3007 else if ((*ptr
== ' ' || *ptr
== '/') && options_ptr
[-1] != '_')
3008 *options_ptr
++ = '_';
3009 else if (*ptr
== '?' || *ptr
== '(')
3013 * Then add the make and model in the printer info, so
3014 * that MacOS clients see something reasonable...
3017 strlcpy(options_ptr
, "&PRINTER_LOCATION=Local+Printer"
3019 sizeof(options
) - (options_ptr
- options
));
3020 options_ptr
+= strlen(options_ptr
);
3022 cgiFormEncode(options_ptr
, device_make_and_model
,
3023 sizeof(options
) - (options_ptr
- options
));
3024 options_ptr
+= strlen(options_ptr
);
3027 * Then copy the device URI...
3030 strlcpy(options_ptr
, "&DEVICE_URI=",
3031 sizeof(options
) - (options_ptr
- options
));
3032 options_ptr
+= strlen(options_ptr
);
3034 cgiFormEncode(options_ptr
, device_uri
,
3035 sizeof(options
) - (options_ptr
- options
));
3036 options_ptr
+= strlen(options_ptr
);
3038 if (options_ptr
< (options
+ sizeof(options
) - 1))
3040 *options_ptr
++ = '|';
3041 cgiFormEncode(options_ptr
, device_make_and_model
,
3042 sizeof(options
) - (options_ptr
- options
));
3046 * Finally, set the form variables for this printer...
3049 cgiSetArray("device_info", i
, device_info
);
3050 cgiSetArray("device_make_and_model", i
, device_make_and_model
);
3051 cgiSetArray("device_options", i
, options
);
3052 cgiSetArray("device_uri", i
, device_uri
);
3061 ippDelete(response
);
3064 * Free the device list...
3067 for (printer_device
= (char *)cupsArrayFirst(printer_devices
);
3069 printer_device
= (char *)cupsArrayNext(printer_devices
))
3070 free(printer_device
);
3072 cupsArrayDelete(printer_devices
);
3077 * See if Samba and the Windows drivers are installed...
3080 if ((datadir
= getenv("CUPS_DATADIR")) == NULL
)
3081 datadir
= CUPS_DATADIR
;
3083 snprintf(line
, sizeof(line
), "%s/drivers/pscript5.dll", datadir
);
3084 if (!access(line
, 0))
3087 * Found Windows 2000 driver file, see if we have smbclient and
3091 if (cupsFileFind("smbclient", getenv("PATH"), line
, sizeof(line
)) &&
3092 cupsFileFind("rpcclient", getenv("PATH"), line
, sizeof(line
)))
3093 cgiSetVariable("HAVE_SAMBA", "Y");
3096 if (!cupsFileFind("smbclient", getenv("PATH"), line
, sizeof(line
)))
3097 fputs("ERROR: smbclient not found!\n", stderr
);
3099 if (!cupsFileFind("rpcclient", getenv("PATH"), line
, sizeof(line
)))
3100 fputs("ERROR: rpcclient not found!\n", stderr
);
3107 * Finally, show the main menu template...
3110 cgiCopyTemplateLang("admin.tmpl");
3117 * 'do_printer_op()' - Do a printer operation.
3121 do_printer_op(http_t
*http
, /* I - HTTP connection */
3122 ipp_op_t op
, /* I - Operation to perform */
3123 const char *title
) /* I - Title of page */
3125 ipp_t
*request
; /* IPP request */
3126 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3127 const char *printer
, /* Printer name (purge-jobs) */
3128 *is_class
; /* Is a class? */
3131 is_class
= cgiGetVariable("IS_CLASS");
3132 printer
= cgiGetVariable("PRINTER_NAME");
3136 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3137 cgiStartHTML(title
);
3138 cgiCopyTemplateLang("error.tmpl");
3144 * Build a printer request, which requires the following
3147 * attributes-charset
3148 * attributes-natural-language
3152 request
= ippNewRequest(op
);
3154 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3155 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3157 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3161 * Do the request and get back a response...
3164 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
3166 if (cupsLastError() > IPP_OK_CONFLICT
)
3168 cgiStartHTML(title
);
3169 cgiShowIPPError(_("Unable to change printer:"));
3174 * Redirect successful updates back to the printer page...
3177 char url
[1024], /* Printer/class URL */
3178 refresh
[1024]; /* Refresh URL */
3181 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3182 cgiFormEncode(uri
, url
, sizeof(uri
));
3183 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=%s", uri
);
3184 cgiSetVariable("refresh_page", refresh
);
3186 cgiStartHTML(title
);
3188 if (op
== IPP_PAUSE_PRINTER
)
3189 cgiCopyTemplateLang("printer-stop.tmpl");
3190 else if (op
== IPP_RESUME_PRINTER
)
3191 cgiCopyTemplateLang("printer-start.tmpl");
3192 else if (op
== CUPS_ACCEPT_JOBS
)
3193 cgiCopyTemplateLang("printer-accept.tmpl");
3194 else if (op
== CUPS_REJECT_JOBS
)
3195 cgiCopyTemplateLang("printer-reject.tmpl");
3196 else if (op
== IPP_PURGE_JOBS
)
3197 cgiCopyTemplateLang("printer-purge.tmpl");
3198 else if (op
== CUPS_SET_DEFAULT
)
3199 cgiCopyTemplateLang("printer-default.tmpl");
3207 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
3211 do_set_allowed_users(http_t
*http
) /* I - HTTP connection */
3213 int i
; /* Looping var */
3214 ipp_t
*request
, /* IPP request */
3215 *response
; /* IPP response */
3216 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3217 const char *printer
, /* Printer name (purge-jobs) */
3218 *is_class
, /* Is a class? */
3219 *users
, /* List of users or groups */
3220 *type
; /* Allow/deny type */
3221 int num_users
; /* Number of users */
3222 char *ptr
, /* Pointer into users string */
3223 *end
, /* Pointer to end of users string */
3224 quote
; /* Quote character */
3225 ipp_attribute_t
*attr
; /* Attribute */
3226 static const char * const attrs
[] = /* Requested attributes */
3228 "requesting-user-name-allowed",
3229 "requesting-user-name-denied"
3233 is_class
= cgiGetVariable("IS_CLASS");
3234 printer
= cgiGetVariable("PRINTER_NAME");
3238 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3239 cgiStartHTML(cgiText(_("Set Allowed Users")));
3240 cgiCopyTemplateLang("error.tmpl");
3245 users
= cgiGetVariable("users");
3246 type
= cgiGetVariable("type");
3248 if (!users
|| !type
||
3249 (strcmp(type
, "requesting-user-name-allowed") &&
3250 strcmp(type
, "requesting-user-name-denied")))
3253 * Build a Get-Printer-Attributes request, which requires the following
3256 * attributes-charset
3257 * attributes-natural-language
3259 * requested-attributes
3262 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
3264 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3265 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3267 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3270 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
3271 "requested-attributes",
3272 (int)(sizeof(attrs
) / sizeof(attrs
[0])), NULL
, attrs
);
3275 * Do the request and get back a response...
3278 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
3280 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3282 ippDelete(response
);
3285 cgiStartHTML(cgiText(_("Set Allowed Users")));
3287 if (cupsLastError() > IPP_OK_CONFLICT
)
3288 cgiShowIPPError(_("Unable to get printer attributes:"));
3290 cgiCopyTemplateLang("users.tmpl");
3297 * Save the changes...
3300 for (num_users
= 0, ptr
= (char *)users
; *ptr
; num_users
++)
3303 * Skip whitespace and commas...
3306 while (*ptr
== ',' || isspace(*ptr
& 255))
3309 if (*ptr
== '\'' || *ptr
== '\"')
3312 * Scan quoted name...
3317 for (end
= ptr
; *end
; end
++)
3324 * Scan space or comma-delimited name...
3327 for (end
= ptr
; *end
; end
++)
3328 if (isspace(*end
& 255) || *end
== ',')
3333 * Advance to the next name...
3340 * Build a CUPS-Add-Printer/Class request, which requires the following
3343 * attributes-charset
3344 * attributes-natural-language
3346 * requesting-user-name-{allowed,denied}
3349 request
= ippNewRequest(is_class
? CUPS_ADD_CLASS
: CUPS_ADD_PRINTER
);
3351 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3352 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3354 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3358 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
3359 "requesting-user-name-allowed", NULL
, "all");
3362 attr
= ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
3363 type
, num_users
, NULL
, NULL
);
3365 for (i
= 0, ptr
= (char *)users
; *ptr
; i
++)
3368 * Skip whitespace and commas...
3371 while (*ptr
== ',' || isspace(*ptr
& 255))
3374 if (*ptr
== '\'' || *ptr
== '\"')
3377 * Scan quoted name...
3382 for (end
= ptr
; *end
; end
++)
3389 * Scan space or comma-delimited name...
3392 for (end
= ptr
; *end
; end
++)
3393 if (isspace(*end
& 255) || *end
== ',')
3398 * Terminate the name...
3408 attr
->values
[i
].string
.text
= strdup(ptr
);
3411 * Advance to the next name...
3419 * Do the request and get back a response...
3422 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
3424 if (cupsLastError() > IPP_OK_CONFLICT
)
3426 cgiStartHTML(cgiText(_("Set Allowed Users")));
3427 cgiShowIPPError(_("Unable to change printer:"));
3432 * Redirect successful updates back to the printer page...
3435 char url
[1024], /* Printer/class URL */
3436 refresh
[1024]; /* Refresh URL */
3439 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3440 cgiFormEncode(uri
, url
, sizeof(uri
));
3441 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=%s", uri
);
3442 cgiSetVariable("refresh_page", refresh
);
3444 cgiStartHTML(cgiText(_("Set Allowed Users")));
3446 cgiCopyTemplateLang(is_class
? "class-modified.tmpl" :
3447 "printer-modified.tmpl");
3456 * 'do_set_sharing()' - Set printer-is-shared value...
3460 do_set_sharing(http_t
*http
) /* I - HTTP connection */
3462 ipp_t
*request
, /* IPP request */
3463 *response
; /* IPP response */
3464 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3465 const char *printer
, /* Printer name */
3466 *is_class
, /* Is a class? */
3467 *shared
; /* Sharing value */
3470 is_class
= cgiGetVariable("IS_CLASS");
3471 printer
= cgiGetVariable("PRINTER_NAME");
3472 shared
= cgiGetVariable("SHARED");
3474 if (!printer
|| !shared
)
3476 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3477 cgiStartHTML(cgiText(_("Set Publishing")));
3478 cgiCopyTemplateLang("error.tmpl");
3484 * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3485 * following attributes:
3487 * attributes-charset
3488 * attributes-natural-language
3493 request
= ippNewRequest(is_class
? CUPS_ADD_CLASS
: CUPS_ADD_PRINTER
);
3495 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3496 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3498 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3501 ippAddBoolean(request
, IPP_TAG_OPERATION
, "printer-is-shared", atoi(shared
));
3504 * Do the request and get back a response...
3507 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3509 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3511 ippDelete(response
);
3514 if (cupsLastError() > IPP_OK_CONFLICT
)
3516 cgiStartHTML(cgiText(_("Set Publishing")));
3517 cgiShowIPPError(_("Unable to change printer-is-shared attribute:"));
3522 * Redirect successful updates back to the printer page...
3525 char url
[1024], /* Printer/class URL */
3526 refresh
[1024]; /* Refresh URL */
3529 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3530 cgiFormEncode(uri
, url
, sizeof(uri
));
3531 snprintf(refresh
, sizeof(refresh
), "5;/admin/?OP=redirect&URL=%s", uri
);
3532 cgiSetVariable("refresh_page", refresh
);
3534 cgiStartHTML(cgiText(_("Set Publishing")));
3535 cgiCopyTemplateLang(is_class
? "class-modified.tmpl" :
3536 "printer-modified.tmpl");
3544 * 'match_string()' - Return the number of matching characters.
3547 static int /* O - Number of matching characters */
3548 match_string(const char *a
, /* I - First string */
3549 const char *b
) /* I - Second string */
3551 int count
; /* Number of matching characters */
3555 * Loop through both strings until we hit the end of either or we find
3556 * a non-matching character. For the purposes of comparison, we ignore
3557 * whitespace and do a case-insensitive comparison so that we have a
3558 * better chance of finding a match...
3561 for (count
= 0; *a
&& *b
; a
++, b
++, count
++)
3564 * Skip leading whitespace characters...
3567 while (isspace(*a
& 255))
3570 while (isspace(*b
& 255))
3574 * Break out if we run out of characters...
3581 * Do a case-insensitive comparison of the next two chars...
3584 if (tolower(*a
& 255) != tolower(*b
& 255))