2 * "$Id: admin.c 7012 2007-10-10 21:22:45Z mike $"
4 * Administration CGI for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * main() - Main entry for CGI.
18 * do_add_rss_subscription() - Add a RSS subscription.
19 * do_am_class() - Add or modify a class.
20 * do_am_printer() - Add or modify a printer.
21 * do_cancel_subscription() - Cancel a subscription.
22 * do_config_server() - Configure server settings.
23 * do_delete_class() - Delete a class...
24 * do_delete_printer() - Delete a printer...
25 * do_export() - Export printers to Samba...
26 * do_list_printers() - List available printers...
27 * do_menu() - Show the main menu...
28 * do_printer_op() - Do a printer operation.
29 * do_set_allowed_users() - Set the allowed/denied users for a queue.
30 * do_set_options() - Configure the default options for a queue.
31 * do_set_sharing() - Set printer-is-shared value...
32 * match_string() - Return the number of matching characters.
36 * Include necessary headers...
39 #include "cgi-private.h"
40 #include <cups/adminutil.h>
41 #include <cups/file.h>
52 static void do_add_rss_subscription(http_t
*http
);
53 static void do_am_class(http_t
*http
, int modify
);
54 static void do_am_printer(http_t
*http
, int modify
);
55 static void do_cancel_subscription(http_t
*http
);
56 static void do_set_options(http_t
*http
, int is_class
);
57 static void do_config_server(http_t
*http
);
58 static void do_delete_class(http_t
*http
);
59 static void do_delete_printer(http_t
*http
);
60 static void do_export(http_t
*http
);
61 static void do_list_printers(http_t
*http
);
62 static void do_menu(http_t
*http
);
63 static void do_printer_op(http_t
*http
,
64 ipp_op_t op
, const char *title
);
65 static void do_set_allowed_users(http_t
*http
);
66 static void do_set_sharing(http_t
*http
);
67 static int match_string(const char *a
, const char *b
);
71 * 'main()' - Main entry for CGI.
74 int /* O - Exit status */
75 main(int argc
, /* I - Number of command-line arguments */
76 char *argv
[]) /* I - Command-line arguments */
78 http_t
*http
; /* Connection to the server */
79 const char *op
; /* Operation name */
83 * Connect to the HTTP server...
86 fputs("DEBUG: admin.cgi started...\n", stderr
);
88 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
92 perror("ERROR: Unable to connect to cupsd");
93 fprintf(stderr
, "DEBUG: cupsServer()=\"%s\"\n",
94 cupsServer() ? cupsServer() : "(null)");
95 fprintf(stderr
, "DEBUG: ippPort()=%d\n", ippPort());
96 fprintf(stderr
, "DEBUG: cupsEncryption()=%d\n", cupsEncryption());
100 fprintf(stderr
, "DEBUG: http=%p\n", http
);
103 * Set the web interface section...
106 cgiSetVariable("SECTION", "admin");
109 * See if we have form data...
112 if (!cgiInitialize())
115 * Nope, send the administration menu...
118 fputs("DEBUG: No form data, showing main menu...\n", stderr
);
122 else if ((op
= cgiGetVariable("OP")) != NULL
&& cgiIsPOST())
125 * Do the operation...
128 fprintf(stderr
, "DEBUG: op=\"%s\"...\n", op
);
130 if (!strcmp(op
, "start-printer"))
131 do_printer_op(http
, IPP_RESUME_PRINTER
, cgiText(_("Start Printer")));
132 else if (!strcmp(op
, "stop-printer"))
133 do_printer_op(http
, IPP_PAUSE_PRINTER
, cgiText(_("Stop Printer")));
134 else if (!strcmp(op
, "start-class"))
135 do_printer_op(http
, IPP_RESUME_PRINTER
, cgiText(_("Start Class")));
136 else if (!strcmp(op
, "stop-class"))
137 do_printer_op(http
, IPP_PAUSE_PRINTER
, cgiText(_("Stop Class")));
138 else if (!strcmp(op
, "accept-jobs"))
139 do_printer_op(http
, CUPS_ACCEPT_JOBS
, cgiText(_("Accept Jobs")));
140 else if (!strcmp(op
, "reject-jobs"))
141 do_printer_op(http
, CUPS_REJECT_JOBS
, cgiText(_("Reject Jobs")));
142 else if (!strcmp(op
, "purge-jobs"))
143 do_printer_op(http
, IPP_PURGE_JOBS
, cgiText(_("Purge Jobs")));
144 else if (!strcmp(op
, "set-allowed-users"))
145 do_set_allowed_users(http
);
146 else if (!strcmp(op
, "set-as-default"))
147 do_printer_op(http
, CUPS_SET_DEFAULT
, cgiText(_("Set As Default")));
148 else if (!strcmp(op
, "set-sharing"))
149 do_set_sharing(http
);
150 else if (!strcmp(op
, "find-new-printers") ||
151 !strcmp(op
, "list-available-printers"))
152 do_list_printers(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-class-options"))
166 do_set_options(http
, 1);
167 else if (!strcmp(op
, "set-printer-options"))
168 do_set_options(http
, 0);
169 else if (!strcmp(op
, "config-server"))
170 do_config_server(http
);
171 else if (!strcmp(op
, "export-samba"))
173 else if (!strcmp(op
, "add-rss-subscription"))
174 do_add_rss_subscription(http
);
175 else if (!strcmp(op
, "cancel-subscription"))
176 do_cancel_subscription(http
);
180 * Bad operation code... Display an error...
183 cgiStartHTML(cgiText(_("Administration")));
184 cgiCopyTemplateLang("error-op.tmpl");
188 else if (op
&& !strcmp(op
, "redirect"))
190 const char *url
; /* Redirection URL... */
191 char prefix
[1024]; /* URL prefix */
195 snprintf(prefix
, sizeof(prefix
), "https://%s:%s",
196 getenv("SERVER_NAME"), getenv("SERVER_PORT"));
198 snprintf(prefix
, sizeof(prefix
), "http://%s:%s",
199 getenv("SERVER_NAME"), getenv("SERVER_PORT"));
201 if ((url
= cgiGetVariable("URL")) != NULL
)
202 printf("Location: %s%s\n\n", prefix
, url
);
204 printf("Location: %s/admin\n\n", prefix
);
209 * Form data but no operation code... Display an error...
212 cgiStartHTML(cgiText(_("Administration")));
213 cgiCopyTemplateLang("error-op.tmpl");
218 * Close the HTTP server connection...
224 * Return with no errors...
232 * 'do_add_rss_subscription()' - Add a RSS subscription.
236 do_add_rss_subscription(http_t
*http
) /* I - HTTP connection */
238 ipp_t
*request
, /* IPP request data */
239 *response
; /* IPP response data */
240 char rss_uri
[1024]; /* RSS notify-recipient URI */
241 int num_events
; /* Number of events */
242 const char *events
[12], /* Subscribed events */
243 *subscription_name
, /* Subscription name */
244 *printer_uri
, /* Printer URI */
245 *ptr
, /* Pointer into name */
246 *user
; /* Username */
247 int max_events
; /* Maximum number of events */
251 * See if we have all of the required information...
254 subscription_name
= cgiGetVariable("SUBSCRIPTION_NAME");
255 printer_uri
= cgiGetVariable("PRINTER_URI");
258 if (cgiGetVariable("EVENT_JOB_CREATED"))
259 events
[num_events
++] = "job-created";
260 if (cgiGetVariable("EVENT_JOB_COMPLETED"))
261 events
[num_events
++] = "job-completed";
262 if (cgiGetVariable("EVENT_JOB_STOPPED"))
263 events
[num_events
++] = "job-stopped";
264 if (cgiGetVariable("EVENT_JOB_CONFIG_CHANGED"))
265 events
[num_events
++] = "job-config-changed";
266 if (cgiGetVariable("EVENT_PRINTER_STOPPED"))
267 events
[num_events
++] = "printer-stopped";
268 if (cgiGetVariable("EVENT_PRINTER_ADDED"))
269 events
[num_events
++] = "printer-added";
270 if (cgiGetVariable("EVENT_PRINTER_MODIFIED"))
271 events
[num_events
++] = "printer-modified";
272 if (cgiGetVariable("EVENT_PRINTER_DELETED"))
273 events
[num_events
++] = "printer-deleted";
274 if (cgiGetVariable("EVENT_SERVER_STARTED"))
275 events
[num_events
++] = "server-started";
276 if (cgiGetVariable("EVENT_SERVER_STOPPED"))
277 events
[num_events
++] = "server-stopped";
278 if (cgiGetVariable("EVENT_SERVER_RESTARTED"))
279 events
[num_events
++] = "server-restarted";
280 if (cgiGetVariable("EVENT_SERVER_AUDIT"))
281 events
[num_events
++] = "server-audit";
283 if ((ptr
= cgiGetVariable("MAX_EVENTS")) != NULL
)
284 max_events
= atoi(ptr
);
288 if (!subscription_name
|| !printer_uri
|| !num_events
||
289 max_events
<= 0 || max_events
> 9999)
292 * Don't have everything we need, so get the available printers
293 * and classes and (re)show the add page...
296 request
= ippNewRequest(CUPS_GET_PRINTERS
);
297 response
= cupsDoRequest(http
, request
, "/");
299 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
303 cgiStartHTML(cgiText(_("Add RSS Subscription")));
305 cgiCopyTemplateLang("add-rss-subscription.tmpl");
312 * Validate the subscription name...
315 for (ptr
= subscription_name
; *ptr
; ptr
++)
316 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' ||
317 *ptr
== '?' || *ptr
== '#')
322 cgiSetVariable("ERROR",
323 cgiText(_("The subscription name may not "
324 "contain spaces, slashes (/), question marks (?), "
325 "or the pound sign (#).")));
326 cgiStartHTML(_("Add RSS Subscription"));
327 cgiCopyTemplateLang("error.tmpl");
333 * Add the subscription...
336 ptr
= subscription_name
+ strlen(subscription_name
) - 4;
337 if (ptr
< subscription_name
|| strcmp(ptr
, ".rss"))
338 httpAssembleURIf(HTTP_URI_CODING_ALL
, rss_uri
, sizeof(rss_uri
), "rss",
339 NULL
, NULL
, 0, "/%s.rss?max_events=%d", subscription_name
,
342 httpAssembleURIf(HTTP_URI_CODING_ALL
, rss_uri
, sizeof(rss_uri
), "rss",
343 NULL
, NULL
, 0, "/%s?max_events=%d", subscription_name
,
346 request
= ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION
);
348 if (!strcasecmp(printer_uri
, "#ALL#"))
349 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
350 NULL
, "ipp://localhost/");
352 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
355 if ((user
= getenv("REMOTE_USER")) == NULL
)
358 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
361 ippAddString(request
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_URI
,
362 "notify-recipient-uri", NULL
, rss_uri
);
363 ippAddStrings(request
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_KEYWORD
, "notify-events",
364 num_events
, NULL
, events
);
365 ippAddInteger(request
, IPP_TAG_SUBSCRIPTION
, IPP_TAG_INTEGER
,
366 "notify-lease-duration", 0);
368 ippDelete(cupsDoRequest(http
, request
, "/"));
370 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
372 puts("Status: 401\n");
375 else if (cupsLastError() > IPP_OK_CONFLICT
)
377 cgiStartHTML(_("Add RSS Subscription"));
378 cgiShowIPPError(_("Unable to add RSS subscription:"));
383 * Redirect successful updates back to the admin page...
386 cgiSetVariable("refresh_page", "5;URL=/admin");
387 cgiStartHTML(_("Add RSS Subscription"));
388 cgiCopyTemplateLang("subscription-added.tmpl");
396 * 'do_am_class()' - Add or modify a class.
400 do_am_class(http_t
*http
, /* I - HTTP connection */
401 int modify
) /* I - Modify the printer? */
403 int i
, j
; /* Looping vars */
404 int element
; /* Element number */
405 int num_printers
; /* Number of printers */
406 ipp_t
*request
, /* IPP request */
407 *response
; /* IPP response */
408 ipp_attribute_t
*attr
; /* member-uris attribute */
409 char uri
[HTTP_MAX_URI
]; /* Device or printer URI */
410 const char *name
, /* Pointer to class name */
411 *ptr
; /* Pointer to CGI variable */
412 const char *title
; /* Title of page */
413 static const char * const pattrs
[] = /* Requested printer attributes */
421 title
= cgiText(modify
? _("Modify Class") : _("Add Class"));
422 name
= cgiGetVariable("PRINTER_NAME");
424 if (cgiGetVariable("PRINTER_LOCATION") == NULL
)
427 * Build a CUPS_GET_PRINTERS request, which requires the
428 * following attributes:
431 * attributes-natural-language
434 request
= ippNewRequest(CUPS_GET_PRINTERS
);
437 * Do the request and get back a response...
440 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
443 * Create MEMBER_URIS and MEMBER_NAMES arrays...
446 for (element
= 0, attr
= response
->attrs
;
449 if (attr
->name
&& !strcmp(attr
->name
, "printer-uri-supported"))
451 if ((ptr
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
&&
452 (!name
|| strcasecmp(name
, ptr
+ 1)))
455 * Don't show the current class...
458 cgiSetArray("MEMBER_URIS", element
, attr
->values
[0].string
.text
);
463 for (element
= 0, attr
= response
->attrs
;
466 if (attr
->name
&& !strcmp(attr
->name
, "printer-name"))
468 if (!name
|| strcasecmp(name
, attr
->values
[0].string
.text
))
471 * Don't show the current class...
474 cgiSetArray("MEMBER_NAMES", element
, attr
->values
[0].string
.text
);
479 num_printers
= cgiGetSize("MEMBER_URIS");
489 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
490 * following attributes:
493 * attributes-natural-language
497 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
499 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
500 "localhost", 0, "/classes/%s", name
);
501 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
504 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
505 "requested-attributes",
506 (int)(sizeof(pattrs
) / sizeof(pattrs
[0])),
510 * Do the request and get back a response...
513 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
515 if ((attr
= ippFindAttribute(response
, "member-names",
516 IPP_TAG_NAME
)) != NULL
)
519 * Mark any current members in the class...
522 for (j
= 0; j
< num_printers
; j
++)
523 cgiSetArray("MEMBER_SELECTED", j
, "");
525 for (i
= 0; i
< attr
->num_values
; i
++)
527 for (j
= 0; j
< num_printers
; j
++)
529 if (!strcasecmp(attr
->values
[i
].string
.text
,
530 cgiGetArray("MEMBER_NAMES", j
)))
532 cgiSetArray("MEMBER_SELECTED", j
, "SELECTED");
539 if ((attr
= ippFindAttribute(response
, "printer-info",
540 IPP_TAG_TEXT
)) != NULL
)
541 cgiSetVariable("PRINTER_INFO", attr
->values
[0].string
.text
);
543 if ((attr
= ippFindAttribute(response
, "printer-location",
544 IPP_TAG_TEXT
)) != NULL
)
545 cgiSetVariable("PRINTER_LOCATION", attr
->values
[0].string
.text
);
551 * Update the location and description of an existing printer...
555 cgiCopyTemplateLang("modify-class.tmpl");
560 * Get the name, location, and description for a new printer...
564 cgiCopyTemplateLang("add-class.tmpl");
572 for (ptr
= name
; *ptr
; ptr
++)
573 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
576 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
578 cgiSetVariable("ERROR",
579 cgiText(_("The class name may only contain up to "
580 "127 printable characters and may not "
581 "contain spaces, slashes (/), or the "
582 "pound sign (#).")));
584 cgiCopyTemplateLang("error.tmpl");
590 * Build a CUPS_ADD_CLASS request, which requires the following
594 * attributes-natural-language
598 * printer-is-accepting-jobs
603 request
= ippNewRequest(CUPS_ADD_CLASS
);
605 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
606 "localhost", 0, "/classes/%s",
607 cgiGetVariable("PRINTER_NAME"));
608 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
611 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
612 NULL
, cgiGetVariable("PRINTER_LOCATION"));
614 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
615 NULL
, cgiGetVariable("PRINTER_INFO"));
617 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
619 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
622 if ((num_printers
= cgiGetSize("MEMBER_URIS")) > 0)
624 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "member-uris",
625 num_printers
, NULL
, NULL
);
626 for (i
= 0; i
< num_printers
; i
++)
627 attr
->values
[i
].string
.text
= strdup(cgiGetArray("MEMBER_URIS", i
));
631 * Do the request and get back a response...
634 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
636 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
638 puts("Status: 401\n");
641 else if (cupsLastError() > IPP_OK_CONFLICT
)
644 cgiShowIPPError(modify
? _("Unable to modify class:") :
645 _("Unable to add class:"));
650 * Redirect successful updates back to the class page...
653 char refresh
[1024]; /* Refresh URL */
655 cgiFormEncode(uri
, name
, sizeof(uri
));
656 snprintf(refresh
, sizeof(refresh
), "5;URL=/admin/?OP=redirect&URL=/classes/%s",
658 cgiSetVariable("refresh_page", refresh
);
663 cgiCopyTemplateLang("class-modified.tmpl");
665 cgiCopyTemplateLang("class-added.tmpl");
673 * 'do_am_printer()' - Add or modify a printer.
677 do_am_printer(http_t
*http
, /* I - HTTP connection */
678 int modify
) /* I - Modify the printer? */
680 int i
; /* Looping var */
681 ipp_attribute_t
*attr
; /* Current attribute */
682 ipp_t
*request
, /* IPP request */
683 *response
, /* IPP response */
684 *oldinfo
; /* Old printer information */
685 const cgi_file_t
*file
; /* Uploaded file, if any */
686 const char *var
; /* CGI variable */
687 char uri
[HTTP_MAX_URI
], /* Device or printer URI */
688 *uriptr
; /* Pointer into URI */
689 int maxrate
; /* Maximum baud rate */
690 char baudrate
[255]; /* Baud rate string */
691 const char *name
, /* Pointer to class name */
692 *ptr
; /* Pointer to CGI variable */
693 const char *title
; /* Title of page */
694 static int baudrates
[] = /* Baud rates */
709 ptr
= cgiGetVariable("DEVICE_URI");
710 fprintf(stderr
, "DEBUG: do_am_printer: DEVICE_URI=\"%s\"\n",
711 ptr
? ptr
: "(null)");
713 title
= cgiText(modify
? _("Modify Printer") : _("Add Printer"));
718 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
719 * following attributes:
722 * attributes-natural-language
726 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
728 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
729 "localhost", 0, "/printers/%s",
730 cgiGetVariable("PRINTER_NAME"));
731 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
735 * Do the request and get back a response...
738 oldinfo
= cupsDoRequest(http
, request
, "/");
747 fprintf(stderr
, "DEBUG: file->tempfile=%s\n", file
->tempfile
);
748 fprintf(stderr
, "DEBUG: file->name=%s\n", file
->name
);
749 fprintf(stderr
, "DEBUG: file->filename=%s\n", file
->filename
);
750 fprintf(stderr
, "DEBUG: file->mimetype=%s\n", file
->mimetype
);
753 if ((name
= cgiGetVariable("PRINTER_NAME")) != NULL
)
755 for (ptr
= name
; *ptr
; ptr
++)
756 if ((*ptr
>= 0 && *ptr
<= ' ') || *ptr
== 127 || *ptr
== '/' || *ptr
== '#')
759 if (*ptr
|| ptr
== name
|| strlen(name
) > 127)
761 cgiSetVariable("ERROR",
762 cgiText(_("The printer name may only contain up to "
763 "127 printable characters and may not "
764 "contain spaces, slashes (/), or the "
765 "pound sign (#).")));
767 cgiCopyTemplateLang("error.tmpl");
773 if ((var
= cgiGetVariable("DEVICE_URI")) != NULL
)
775 if ((uriptr
= strrchr(var
, '|')) != 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 if (!cgiGetVariable("CURRENT_MAKE"))
805 cgiSetVariable("CURRENT_MAKE", make
);
807 cgiSetVariable("PPD_MAKE", make
);
809 if (!cgiGetVariable("CURRENT_MAKE_AND_MODEL"))
810 cgiSetVariable("CURRENT_MAKE_AND_MODEL", uriptr
);
814 char template[128], /* Template name */
815 *tptr
; /* Pointer into template name */
817 cgiSetVariable("PRINTER_INFO", uriptr
);
819 for (tptr
= template;
820 tptr
< (template + sizeof(template) - 1) && *uriptr
;
822 if (isalnum(*uriptr
& 255) || *uriptr
== '_' || *uriptr
== '-' ||
825 else if ((*uriptr
== ' ' || *uriptr
== '/') && tptr
[-1] != '_')
827 else if (*uriptr
== '?' || *uriptr
== '(')
832 cgiSetVariable("TEMPLATE_NAME", template);
840 * Build a CUPS_GET_DEVICES request, which requires the following
844 * attributes-natural-language
848 fputs("DEBUG: Getting list of devices...\n", stderr
);
850 request
= ippNewRequest(CUPS_GET_DEVICES
);
852 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
853 NULL
, "ipp://localhost/printers/");
856 * Do the request and get back a response...
859 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
861 fputs("DEBUG: Got device list!\n", stderr
);
863 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
868 "ERROR: CUPS-Get-Devices request failed with status %x: %s\n",
869 cupsLastError(), cupsLastErrorString());
872 * Let the user choose...
875 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
877 strlcpy(uri
, attr
->values
[0].string
.text
, sizeof(uri
));
878 if ((uriptr
= strchr(uri
, ':')) != NULL
&& strncmp(uriptr
, "://", 3) == 0)
881 cgiSetVariable("CURRENT_DEVICE_URI", attr
->values
[0].string
.text
);
882 cgiSetVariable("CURRENT_DEVICE_SCHEME", uri
);
886 cgiCopyTemplateLang("choose-device.tmpl");
889 else if (strchr(var
, '/') == NULL
)
891 if ((attr
= ippFindAttribute(oldinfo
, "device-uri", IPP_TAG_URI
)) != NULL
)
894 * Set the current device URI for the form to the old one...
897 if (strncmp(attr
->values
[0].string
.text
, var
, strlen(var
)) == 0)
898 cgiSetVariable("DEVICE_URI", attr
->values
[0].string
.text
);
902 * User needs to set the full URI...
906 cgiCopyTemplateLang("choose-uri.tmpl");
909 else if (!strncmp(var
, "serial:", 7) && !cgiGetVariable("BAUDRATE"))
912 * Need baud rate, parity, etc.
915 if ((var
= strchr(var
, '?')) != NULL
&&
916 strncmp(var
, "?baud=", 6) == 0)
917 maxrate
= atoi(var
+ 6);
921 for (i
= 0; i
< 10; i
++)
922 if (baudrates
[i
] > maxrate
)
926 sprintf(baudrate
, "%d", baudrates
[i
]);
927 cgiSetArray("BAUDRATES", i
, baudrate
);
931 cgiCopyTemplateLang("choose-serial.tmpl");
934 else if (!name
|| !cgiGetVariable("PRINTER_LOCATION"))
941 * Update the location and description of an existing printer...
945 cgiSetIPPVars(oldinfo
, NULL
, NULL
, NULL
, 0);
947 cgiCopyTemplateLang("modify-printer.tmpl");
952 * Get the name, location, and description for a new printer...
955 cgiCopyTemplateLang("add-printer.tmpl");
965 else if (!file
&& (var
= cgiGetVariable("PPD_NAME")) == NULL
)
970 * Get the PPD file...
973 int fd
; /* PPD file */
974 char filename
[1024]; /* PPD filename */
975 ppd_file_t
*ppd
; /* PPD information */
976 char buffer
[1024]; /* Buffer */
977 int bytes
; /* Number of bytes */
978 http_status_t get_status
; /* Status of GET */
981 /* TODO: Use cupsGetFile() API... */
982 snprintf(uri
, sizeof(uri
), "/printers/%s.ppd", name
);
984 if (httpGet(http
, uri
))
987 while ((get_status
= httpUpdate(http
)) == HTTP_CONTINUE
);
989 if (get_status
!= HTTP_OK
)
991 fprintf(stderr
, "ERROR: Unable to get PPD file %s: %d - %s\n",
992 uri
, get_status
, httpStatus(get_status
));
994 else if ((fd
= cupsTempFd(filename
, sizeof(filename
))) >= 0)
996 while ((bytes
= httpRead2(http
, buffer
, sizeof(buffer
))) > 0)
997 write(fd
, buffer
, bytes
);
1001 if ((ppd
= ppdOpenFile(filename
)) != NULL
)
1003 if (ppd
->manufacturer
)
1004 cgiSetVariable("CURRENT_MAKE", ppd
->manufacturer
);
1007 cgiSetVariable("CURRENT_MAKE_AND_MODEL", ppd
->nickname
);
1014 fprintf(stderr
, "ERROR: Unable to open PPD file %s: %s\n",
1015 filename
, ppdErrorString(ppdLastError(&bytes
)));
1023 "ERROR: Unable to create temporary file for PPD file: %s\n",
1029 * Build a CUPS_GET_PPDS request, which requires the following
1032 * attributes-charset
1033 * attributes-natural-language
1037 request
= ippNewRequest(CUPS_GET_PPDS
);
1039 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1040 NULL
, "ipp://localhost/printers/");
1042 if ((var
= cgiGetVariable("CURRENT_MAKE")) == NULL
)
1043 var
= cgiGetVariable("PPD_MAKE");
1045 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_TEXT
,
1046 "ppd-make", NULL
, var
);
1048 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1049 "requested-attributes", NULL
, "ppd-make");
1052 * Do the request and get back a response...
1055 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1058 * Got the list of PPDs, see if the user has selected a make...
1061 if (cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0) == 0)
1064 * No PPD files with this make, try again with all makes...
1067 ippDelete(response
);
1069 request
= ippNewRequest(CUPS_GET_PPDS
);
1071 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1072 NULL
, "ipp://localhost/printers/");
1074 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1075 "requested-attributes", NULL
, "ppd-make");
1077 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1078 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
1080 cgiStartHTML(title
);
1081 cgiCopyTemplateLang("choose-make.tmpl");
1084 else if (!var
|| cgiGetVariable("SELECT_MAKE"))
1086 cgiStartHTML(title
);
1087 cgiCopyTemplateLang("choose-make.tmpl");
1093 * Let the user choose a model...
1096 const char *make_model
; /* Current make/model string */
1099 if ((make_model
= cgiGetVariable("CURRENT_MAKE_AND_MODEL")) != NULL
)
1102 * Scan for "close" matches...
1105 int match
, /* Current match */
1106 best_match
, /* Best match so far */
1107 count
; /* Number of drivers */
1108 const char *best
, /* Best matching string */
1109 *current
; /* Current string */
1112 count
= cgiGetSize("PPD_MAKE_AND_MODEL");
1114 for (i
= 0, best_match
= 0, best
= NULL
; i
< count
; i
++)
1116 current
= cgiGetArray("PPD_MAKE_AND_MODEL", i
);
1117 match
= match_string(make_model
, current
);
1119 if (match
> best_match
)
1126 if (best_match
> strlen(var
))
1129 * Found a match longer than the make...
1132 cgiSetVariable("CURRENT_MAKE_AND_MODEL", best
);
1136 cgiStartHTML(title
);
1137 cgiCopyTemplateLang("choose-model.tmpl");
1141 ippDelete(response
);
1145 cgiStartHTML(title
);
1146 cgiShowIPPError(_("Unable to get list of printer drivers:"));
1147 cgiCopyTemplateLang("error.tmpl");
1154 * Build a CUPS_ADD_PRINTER request, which requires the following
1157 * attributes-charset
1158 * attributes-natural-language
1164 * printer-is-accepting-jobs
1168 request
= ippNewRequest(CUPS_ADD_PRINTER
);
1170 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1171 "localhost", 0, "/printers/%s",
1172 cgiGetVariable("PRINTER_NAME"));
1173 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1176 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-location",
1177 NULL
, cgiGetVariable("PRINTER_LOCATION"));
1179 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_TEXT
, "printer-info",
1180 NULL
, cgiGetVariable("PRINTER_INFO"));
1183 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
, "ppd-name",
1184 NULL
, cgiGetVariable("PPD_NAME"));
1186 strlcpy(uri
, cgiGetVariable("DEVICE_URI"), sizeof(uri
));
1189 * Strip make and model from URI...
1192 if ((uriptr
= strrchr(uri
, '|')) != NULL
)
1195 if (!strncmp(uri
, "serial:", 7))
1198 * Update serial port URI to include baud rate, etc.
1201 if ((uriptr
= strchr(uri
, '?')) == NULL
)
1202 uriptr
= uri
+ strlen(uri
);
1204 snprintf(uriptr
, sizeof(uri
) - (uriptr
- uri
),
1205 "?baud=%s+bits=%s+parity=%s+flow=%s",
1206 cgiGetVariable("BAUDRATE"), cgiGetVariable("BITS"),
1207 cgiGetVariable("PARITY"), cgiGetVariable("FLOW"));
1210 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_URI
, "device-uri",
1213 ippAddBoolean(request
, IPP_TAG_PRINTER
, "printer-is-accepting-jobs", 1);
1215 ippAddInteger(request
, IPP_TAG_PRINTER
, IPP_TAG_ENUM
, "printer-state",
1219 * Do the request and get back a response...
1223 ippDelete(cupsDoFileRequest(http
, request
, "/admin/", file
->tempfile
));
1225 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
1227 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
1229 puts("Status: 401\n");
1232 else if (cupsLastError() > IPP_OK_CONFLICT
)
1234 cgiStartHTML(title
);
1235 cgiShowIPPError(modify
? _("Unable to modify printer:") :
1236 _("Unable to add printer:"));
1241 * Redirect successful updates back to the printer page...
1244 char refresh
[1024]; /* Refresh URL */
1247 cgiFormEncode(uri
, name
, sizeof(uri
));
1249 snprintf(refresh
, sizeof(refresh
),
1250 "5;/admin/?OP=redirect&URL=/printers/%s", uri
);
1252 cgiSetVariable("refresh_page", refresh
);
1254 cgiStartHTML(title
);
1256 cgiCopyTemplateLang("printer-modified.tmpl");
1261 * Set the printer options...
1264 cgiSetVariable("OP", "set-printer-options");
1265 do_set_options(http
, 0);
1278 * 'do_cancel_subscription()' - Cancel a subscription.
1282 do_cancel_subscription(http_t
*http
)/* I - HTTP connection */
1284 ipp_t
*request
; /* IPP request data */
1285 const char *var
, /* Form variable */
1286 *user
; /* Username */
1287 int id
; /* Subscription ID */
1291 * See if we have all of the required information...
1294 if ((var
= cgiGetVariable("NOTIFY_SUBSCRIPTION_ID")) != NULL
)
1301 cgiSetVariable("ERROR", cgiText(_("Bad subscription ID!")));
1302 cgiStartHTML(_("Cancel RSS Subscription"));
1303 cgiCopyTemplateLang("error.tmpl");
1309 * Cancel the subscription...
1312 request
= ippNewRequest(IPP_CANCEL_SUBSCRIPTION
);
1314 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1315 NULL
, "ipp://localhost/");
1316 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
,
1317 "notify-subscription-id", id
);
1319 if ((user
= getenv("REMOTE_USER")) == NULL
)
1322 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
1325 ippDelete(cupsDoRequest(http
, request
, "/"));
1327 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
1329 puts("Status: 401\n");
1332 else if (cupsLastError() > IPP_OK_CONFLICT
)
1334 cgiStartHTML(_("Cancel RSS Subscription"));
1335 cgiShowIPPError(_("Unable to cancel RSS subscription:"));
1340 * Redirect successful updates back to the admin page...
1343 cgiSetVariable("refresh_page", "5;URL=/admin");
1344 cgiStartHTML(_("Cancel RSS Subscription"));
1345 cgiCopyTemplateLang("subscription-canceled.tmpl");
1353 * 'do_config_server()' - Configure server settings.
1357 do_config_server(http_t
*http
) /* I - HTTP connection */
1359 if (cgiGetVariable("CHANGESETTINGS"))
1362 * Save basic setting changes...
1365 int num_settings
; /* Number of server settings */
1366 cups_option_t
*settings
; /* Server settings */
1367 const char *debug_logging
, /* DEBUG_LOGGING value */
1368 *remote_admin
, /* REMOTE_ADMIN value */
1369 *remote_any
, /* REMOTE_ANY value */
1371 /* REMOTE_PRINTERS value */
1372 *share_printers
,/* SHARE_PRINTERS value */
1375 /* DefaultAuthType value */
1376 #endif /* HAVE_GSSAPI */
1378 /* USER_CANCEL_ANY value */
1382 * Get the checkbox values from the form...
1385 debug_logging
= cgiGetVariable("DEBUG_LOGGING") ? "1" : "0";
1386 remote_admin
= cgiGetVariable("REMOTE_ADMIN") ? "1" : "0";
1387 remote_any
= cgiGetVariable("REMOTE_ANY") ? "1" : "0";
1388 remote_printers
= cgiGetVariable("REMOTE_PRINTERS") ? "1" : "0";
1389 share_printers
= cgiGetVariable("SHARE_PRINTERS") ? "1" : "0";
1390 user_cancel_any
= cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0";
1393 * Get the current server settings...
1396 if (!cupsAdminGetServerSettings(http
, &num_settings
, &settings
))
1398 cgiStartHTML(cgiText(_("Change Settings")));
1399 cgiSetVariable("MESSAGE",
1400 cgiText(_("Unable to change server settings:")));
1401 cgiSetVariable("ERROR", cupsLastErrorString());
1402 cgiCopyTemplateLang("error.tmpl");
1409 * Get authentication settings...
1412 if (cgiGetVariable("KERBEROS"))
1413 default_auth_type
= "Negotiate";
1416 default_auth_type
= cupsGetOption("DefaultAuthType", num_settings
,
1418 if (!strcasecmp(default_auth_type
, "Negotiate"))
1419 default_auth_type
= "Basic";
1422 fprintf(stderr
, "DEBUG: DefaultAuthType %s\n", default_auth_type
);
1423 #endif /* HAVE_GSSAPI */
1426 * See if the settings have changed...
1429 if (strcmp(debug_logging
, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING
,
1430 num_settings
, settings
)) ||
1431 strcmp(remote_admin
, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN
,
1432 num_settings
, settings
)) ||
1433 strcmp(remote_any
, cupsGetOption(CUPS_SERVER_REMOTE_ANY
,
1434 num_settings
, settings
)) ||
1435 strcmp(remote_printers
, cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS
,
1436 num_settings
, settings
)) ||
1437 strcmp(share_printers
, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS
,
1438 num_settings
, settings
)) ||
1440 !cupsGetOption("DefaultAuthType", num_settings
, settings
) ||
1441 strcmp(default_auth_type
, cupsGetOption("DefaultAuthType",
1442 num_settings
, settings
)) ||
1443 #endif /* HAVE_GSSAPI */
1444 strcmp(user_cancel_any
, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY
,
1445 num_settings
, settings
)))
1448 * Settings *have* changed, so save the changes...
1451 cupsFreeOptions(num_settings
, settings
);
1454 num_settings
= cupsAddOption(CUPS_SERVER_DEBUG_LOGGING
,
1455 debug_logging
, num_settings
, &settings
);
1456 num_settings
= cupsAddOption(CUPS_SERVER_REMOTE_ADMIN
,
1457 remote_admin
, num_settings
, &settings
);
1458 num_settings
= cupsAddOption(CUPS_SERVER_REMOTE_ANY
,
1459 remote_any
, num_settings
, &settings
);
1460 num_settings
= cupsAddOption(CUPS_SERVER_REMOTE_PRINTERS
,
1461 remote_printers
, num_settings
, &settings
);
1462 num_settings
= cupsAddOption(CUPS_SERVER_SHARE_PRINTERS
,
1463 share_printers
, num_settings
, &settings
);
1464 num_settings
= cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY
,
1465 user_cancel_any
, num_settings
, &settings
);
1467 num_settings
= cupsAddOption("DefaultAuthType", default_auth_type
,
1468 num_settings
, &settings
);
1469 #endif /* HAVE_GSSAPI */
1471 if (!cupsAdminSetServerSettings(http
, num_settings
, settings
))
1473 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
1475 puts("Status: 401\n");
1479 cgiStartHTML(cgiText(_("Change Settings")));
1480 cgiSetVariable("MESSAGE",
1481 cgiText(_("Unable to change server settings:")));
1482 cgiSetVariable("ERROR", cupsLastErrorString());
1483 cgiCopyTemplateLang("error.tmpl");
1487 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1488 cgiStartHTML(cgiText(_("Change Settings")));
1489 cgiCopyTemplateLang("restart.tmpl");
1498 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1499 cgiStartHTML(cgiText(_("Change Settings")));
1500 cgiCopyTemplateLang("norestart.tmpl");
1503 cupsFreeOptions(num_settings
, settings
);
1507 else if (cgiGetVariable("SAVECHANGES") && cgiGetVariable("CUPSDCONF"))
1510 * Save hand-edited config file...
1513 http_status_t status
; /* PUT status */
1514 char tempfile
[1024]; /* Temporary new cupsd.conf */
1515 int tempfd
; /* Temporary file descriptor */
1516 cups_file_t
*temp
; /* Temporary file */
1517 const char *start
, /* Start of line */
1518 *end
; /* End of line */
1522 * Create a temporary file for the new cupsd.conf file...
1525 if ((tempfd
= cupsTempFd(tempfile
, sizeof(tempfile
))) < 0)
1527 cgiStartHTML(cgiText(_("Edit Configuration File")));
1528 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
1529 cgiSetVariable("ERROR", strerror(errno
));
1530 cgiCopyTemplateLang("error.tmpl");
1537 if ((temp
= cupsFileOpenFd(tempfd
, "w")) == NULL
)
1539 cgiStartHTML(cgiText(_("Edit Configuration File")));
1540 cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:")));
1541 cgiSetVariable("ERROR", strerror(errno
));
1542 cgiCopyTemplateLang("error.tmpl");
1552 * Copy the cupsd.conf text from the form variable...
1555 start
= cgiGetVariable("CUPSDCONF");
1558 if ((end
= strstr(start
, "\r\n")) == NULL
)
1559 if ((end
= strstr(start
, "\n")) == NULL
)
1560 end
= start
+ strlen(start
);
1562 cupsFileWrite(temp
, start
, end
- start
);
1563 cupsFilePutChar(temp
, '\n');
1567 else if (*end
== '\n')
1573 cupsFileClose(temp
);
1576 * Upload the configuration file to the server...
1579 status
= cupsPutFile(http
, "/admin/conf/cupsd.conf", tempfile
);
1581 if (status
== HTTP_UNAUTHORIZED
)
1583 puts("Status: 401\n");
1587 else if (status
!= HTTP_CREATED
)
1589 cgiSetVariable("MESSAGE",
1590 cgiText(_("Unable to upload cupsd.conf file:")));
1591 cgiSetVariable("ERROR", httpStatus(status
));
1593 cgiStartHTML(cgiText(_("Edit Configuration File")));
1594 cgiCopyTemplateLang("error.tmpl");
1598 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect");
1600 cgiStartHTML(cgiText(_("Edit Configuration File")));
1601 cgiCopyTemplateLang("restart.tmpl");
1610 struct stat info
; /* cupsd.conf information */
1611 cups_file_t
*cupsd
; /* cupsd.conf file */
1612 char *buffer
, /* Buffer for entire file */
1613 *bufptr
, /* Pointer into buffer */
1614 *bufend
; /* End of buffer */
1615 int ch
; /* Character from file */
1616 char filename
[1024]; /* Filename */
1617 const char *server_root
; /* Location of config files */
1621 * Locate the cupsd.conf file...
1624 if ((server_root
= getenv("CUPS_SERVERROOT")) == NULL
)
1625 server_root
= CUPS_SERVERROOT
;
1627 snprintf(filename
, sizeof(filename
), "%s/cupsd.conf", server_root
);
1630 * Figure out the size...
1633 if (stat(filename
, &info
))
1635 cgiStartHTML(cgiText(_("Edit Configuration File")));
1636 cgiSetVariable("MESSAGE",
1637 cgiText(_("Unable to access cupsd.conf file:")));
1638 cgiSetVariable("ERROR", strerror(errno
));
1639 cgiCopyTemplateLang("error.tmpl");
1646 if (info
.st_size
> (1024 * 1024))
1648 cgiStartHTML(cgiText(_("Edit Configuration File")));
1649 cgiSetVariable("MESSAGE",
1650 cgiText(_("Unable to access cupsd.conf file:")));
1651 cgiSetVariable("ERROR",
1652 cgiText(_("Unable to edit cupsd.conf files larger than "
1654 cgiCopyTemplateLang("error.tmpl");
1657 fprintf(stderr
, "ERROR: \"%s\" too large (%ld) to edit!\n", filename
,
1658 (long)info
.st_size
);
1663 * Open the cupsd.conf file...
1666 if ((cupsd
= cupsFileOpen(filename
, "r")) == NULL
)
1669 * Unable to open - log an error...
1672 cgiStartHTML(cgiText(_("Edit Configuration File")));
1673 cgiSetVariable("MESSAGE",
1674 cgiText(_("Unable to access cupsd.conf file:")));
1675 cgiSetVariable("ERROR", strerror(errno
));
1676 cgiCopyTemplateLang("error.tmpl");
1684 * Allocate memory and load the file into a string buffer...
1687 if ((buffer
= calloc(1, info
.st_size
+ 1)) != NULL
)
1689 cupsFileRead(cupsd
, buffer
, info
.st_size
);
1690 cgiSetVariable("CUPSDCONF", buffer
);
1694 cupsFileClose(cupsd
);
1697 * Then get the default cupsd.conf file and put that into a string as
1701 strlcat(filename
, ".default", sizeof(filename
));
1703 if (!stat(filename
, &info
) && info
.st_size
< (1024 * 1024) &&
1704 (cupsd
= cupsFileOpen(filename
, "r")) != NULL
)
1706 if ((buffer
= calloc(1, 2 * info
.st_size
+ 1)) != NULL
)
1708 bufend
= buffer
+ 2 * info
.st_size
- 1;
1710 for (bufptr
= buffer
;
1711 bufptr
< bufend
&& (ch
= cupsFileGetChar(cupsd
)) != EOF
;)
1713 if (ch
== '\\' || ch
== '\"')
1718 else if (ch
== '\n')
1723 else if (ch
== '\t')
1734 cgiSetVariable("CUPSDCONF_DEFAULT", buffer
);
1738 cupsFileClose(cupsd
);
1742 * Show the current config file...
1745 cgiStartHTML(cgiText(_("Edit Configuration File")));
1747 cgiCopyTemplateLang("edit-config.tmpl");
1755 * 'do_delete_class()' - Delete a class...
1759 do_delete_class(http_t
*http
) /* I - HTTP connection */
1761 ipp_t
*request
; /* IPP request */
1762 char uri
[HTTP_MAX_URI
]; /* Job URI */
1763 const char *pclass
; /* Printer class name */
1767 * Get form variables...
1770 if (cgiGetVariable("CONFIRM") == NULL
)
1772 cgiStartHTML(cgiText(_("Delete Class")));
1773 cgiCopyTemplateLang("class-confirm.tmpl");
1778 if ((pclass
= cgiGetVariable("PRINTER_NAME")) != NULL
)
1779 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1780 "localhost", 0, "/classes/%s", pclass
);
1783 cgiStartHTML(cgiText(_("Delete Class")));
1784 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
1785 cgiCopyTemplateLang("error.tmpl");
1791 * Build a CUPS_DELETE_CLASS request, which requires the following
1794 * attributes-charset
1795 * attributes-natural-language
1799 request
= ippNewRequest(CUPS_DELETE_CLASS
);
1801 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1805 * Do the request and get back a response...
1808 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
1811 * Show the results...
1814 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
1816 puts("Status: 401\n");
1819 else if (cupsLastError() <= IPP_OK_CONFLICT
)
1822 * Redirect successful updates back to the classes page...
1825 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes");
1828 cgiStartHTML(cgiText(_("Delete Class")));
1830 if (cupsLastError() > IPP_OK_CONFLICT
)
1831 cgiShowIPPError(_("Unable to delete class:"));
1833 cgiCopyTemplateLang("class-deleted.tmpl");
1840 * 'do_delete_printer()' - Delete a printer...
1844 do_delete_printer(http_t
*http
) /* I - HTTP connection */
1846 ipp_t
*request
; /* IPP request */
1847 char uri
[HTTP_MAX_URI
]; /* Job URI */
1848 const char *printer
; /* Printer printer name */
1852 * Get form variables...
1855 if (cgiGetVariable("CONFIRM") == NULL
)
1857 cgiStartHTML(cgiText(_("Delete Printer")));
1858 cgiCopyTemplateLang("printer-confirm.tmpl");
1863 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
1864 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1865 "localhost", 0, "/printers/%s", printer
);
1868 cgiStartHTML(cgiText(_("Delete Printer")));
1869 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
1870 cgiCopyTemplateLang("error.tmpl");
1876 * Build a CUPS_DELETE_PRINTER request, which requires the following
1879 * attributes-charset
1880 * attributes-natural-language
1884 request
= ippNewRequest(CUPS_DELETE_PRINTER
);
1886 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1890 * Do the request and get back a response...
1893 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
1896 * Show the results...
1899 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
1901 puts("Status: 401\n");
1904 else if (cupsLastError() <= IPP_OK_CONFLICT
)
1907 * Redirect successful updates back to the printers page...
1910 cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers");
1913 cgiStartHTML(cgiText(_("Delete Printer")));
1915 if (cupsLastError() > IPP_OK_CONFLICT
)
1916 cgiShowIPPError(_("Unable to delete printer:"));
1918 cgiCopyTemplateLang("printer-deleted.tmpl");
1925 * 'do_export()' - Export printers to Samba...
1929 do_export(http_t
*http
) /* I - HTTP connection */
1931 int i
, j
; /* Looping vars */
1932 ipp_t
*request
, /* IPP request */
1933 *response
; /* IPP response */
1934 const char *username
, /* Samba username */
1935 *password
, /* Samba password */
1936 *export_all
; /* Export all printers? */
1937 int export_count
, /* Number of printers to export */
1938 printer_count
; /* Number of available printers */
1939 const char *name
, /* What name to pull */
1940 *dest
; /* Current destination */
1941 char ppd
[1024]; /* PPD file */
1948 username
= cgiGetVariable("USERNAME");
1949 password
= cgiGetVariable("PASSWORD");
1950 export_all
= cgiGetVariable("EXPORT_ALL");
1951 export_count
= cgiGetSize("EXPORT_NAME");
1954 * Get list of available printers...
1957 cgiSetSize("PRINTER_NAME", 0);
1958 cgiSetSize("PRINTER_EXPORT", 0);
1960 request
= ippNewRequest(CUPS_GET_PRINTERS
);
1962 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
1965 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
,
1966 "printer-type-mask", CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1967 CUPS_PRINTER_IMPLICIT
);
1969 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1970 "requested-attributes", NULL
, "printer-name");
1972 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1974 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
1975 ippDelete(response
);
1979 printer_count
= cgiGetSize("PRINTER_NAME");
1981 for (i
= 0; i
< printer_count
; i
++)
1983 dest
= cgiGetArray("PRINTER_NAME", i
);
1985 for (j
= 0; j
< export_count
; j
++)
1986 if (!strcasecmp(dest
, cgiGetArray("EXPORT_NAME", j
)))
1989 cgiSetArray("PRINTER_EXPORT", i
, j
< export_count
? "Y" : "");
1995 * Export or get the printers to export...
1998 if (username
&& *username
&& password
&& *password
&&
1999 (export_all
|| export_count
> 0))
2005 fputs("DEBUG: Export printers...\n", stderr
);
2009 name
= "PRINTER_NAME";
2010 export_count
= cgiGetSize("PRINTER_NAME");
2013 name
= "EXPORT_NAME";
2015 for (i
= 0; i
< export_count
; i
++)
2017 dest
= cgiGetArray(name
, i
);
2019 if (!cupsAdminCreateWindowsPPD(http
, dest
, ppd
, sizeof(ppd
)))
2022 j
= cupsAdminExportSamba(dest
, ppd
, "localhost", username
, password
,
2031 if (i
< export_count
)
2032 cgiSetVariable("ERROR", cupsLastErrorString());
2035 cgiStartHTML(cgiText(_("Export Printers to Samba")));
2036 cgiCopyTemplateLang("samba-exported.tmpl");
2041 else if (username
&& !*username
)
2042 cgiSetVariable("ERROR",
2043 cgiText(_("A Samba username is required to export "
2044 "printer drivers!")));
2045 else if (username
&& (!password
|| !*password
))
2046 cgiSetVariable("ERROR",
2047 cgiText(_("A Samba password is required to export "
2048 "printer drivers!")));
2054 cgiStartHTML(cgiText(_("Export Printers to Samba")));
2055 cgiCopyTemplateLang("samba-export.tmpl");
2061 * 'do_list_printers()' - List available printers...
2065 do_list_printers(http_t
*http
) /* I - HTTP connection */
2067 ipp_t
*request
, /* IPP request */
2068 *response
; /* IPP response */
2069 ipp_attribute_t
*attr
; /* IPP attribute */
2072 cgiStartHTML(cgiText(_("List Available Printers")));
2076 * Get the list of printers and their devices...
2079 request
= ippNewRequest(CUPS_GET_PRINTERS
);
2081 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2082 "requested-attributes", NULL
, "device-uri");
2084 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type",
2085 CUPS_PRINTER_LOCAL
);
2086 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_ENUM
, "printer-type-mask",
2087 CUPS_PRINTER_LOCAL
);
2089 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2092 * Got the printer list, now load the devices...
2095 int i
; /* Looping var */
2096 cups_array_t
*printer_devices
; /* Printer devices for local printers */
2097 char *printer_device
; /* Current printer device */
2101 * Allocate an array and copy the device strings...
2104 printer_devices
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
);
2106 for (attr
= ippFindAttribute(response
, "device-uri", IPP_TAG_URI
);
2108 attr
= ippFindNextAttribute(response
, "device-uri", IPP_TAG_URI
))
2110 cupsArrayAdd(printer_devices
, strdup(attr
->values
[0].string
.text
));
2114 * Free the printer list and get the device list...
2117 ippDelete(response
);
2119 request
= ippNewRequest(CUPS_GET_DEVICES
);
2121 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2124 * Got the device list, let's parse it...
2127 const char *device_uri
, /* device-uri attribute value */
2128 *device_make_and_model
, /* device-make-and-model value */
2129 *device_info
; /* device-info value */
2132 for (i
= 0, attr
= response
->attrs
; attr
; attr
= attr
->next
)
2135 * Skip leading attributes until we hit a device...
2138 while (attr
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
2145 * Pull the needed attributes from this device...
2149 device_make_and_model
= NULL
;
2152 while (attr
&& attr
->group_tag
== IPP_TAG_PRINTER
)
2154 if (!strcmp(attr
->name
, "device-info") &&
2155 attr
->value_tag
== IPP_TAG_TEXT
)
2156 device_info
= attr
->values
[0].string
.text
;
2158 if (!strcmp(attr
->name
, "device-make-and-model") &&
2159 attr
->value_tag
== IPP_TAG_TEXT
)
2160 device_make_and_model
= attr
->values
[0].string
.text
;
2162 if (!strcmp(attr
->name
, "device-uri") &&
2163 attr
->value_tag
== IPP_TAG_URI
)
2164 device_uri
= attr
->values
[0].string
.text
;
2170 * See if we have everything needed...
2173 if (device_info
&& device_make_and_model
&& device_uri
&&
2174 strcasecmp(device_make_and_model
, "unknown") &&
2175 strchr(device_uri
, ':'))
2178 * Yes, now see if there is already a printer for this
2182 if (!cupsArrayFind(printer_devices
, (void *)device_uri
))
2185 * Not found, so it must be a new printer...
2188 char option
[1024], /* Form variables for this device */
2189 *option_ptr
; /* Pointer into string */
2190 const char *ptr
; /* Pointer into device string */
2194 * Format the printer name variable for this device...
2196 * We use the device-info string first, then device-uri,
2197 * and finally device-make-and-model to come up with a
2201 if (strncasecmp(device_info
, "unknown", 7))
2203 else if ((ptr
= strstr(device_uri
, "://")) != NULL
)
2206 ptr
= device_make_and_model
;
2208 for (option_ptr
= option
;
2209 option_ptr
< (option
+ sizeof(option
) - 1) && *ptr
;
2211 if (isalnum(*ptr
& 255) || *ptr
== '_' || *ptr
== '-' ||
2213 *option_ptr
++ = *ptr
;
2214 else if ((*ptr
== ' ' || *ptr
== '/') && option_ptr
[-1] != '_')
2215 *option_ptr
++ = '_';
2216 else if (*ptr
== '?' || *ptr
== '(')
2221 cgiSetArray("TEMPLATE_NAME", i
, option
);
2224 * Finally, set the form variables for this printer...
2227 cgiSetArray("device_info", i
, device_info
);
2228 cgiSetArray("device_make_and_model", i
, device_make_and_model
);
2229 cgiSetArray("device_uri", i
, device_uri
);
2238 ippDelete(response
);
2241 * Free the device list...
2244 for (printer_device
= (char *)cupsArrayFirst(printer_devices
);
2246 printer_device
= (char *)cupsArrayNext(printer_devices
))
2247 free(printer_device
);
2249 cupsArrayDelete(printer_devices
);
2254 * Finally, show the printer list...
2257 cgiCopyTemplateLang("list-available-printers.tmpl");
2264 * 'do_menu()' - Show the main menu...
2268 do_menu(http_t
*http
) /* I - HTTP connection */
2270 int num_settings
; /* Number of server settings */
2271 cups_option_t
*settings
; /* Server settings */
2272 const char *val
; /* Setting value */
2273 char filename
[1024]; /* Temporary filename */
2274 const char *datadir
; /* Location of data files */
2275 ipp_t
*request
, /* IPP request */
2276 *response
; /* IPP response */
2280 * Get the current server settings...
2283 if (!cupsAdminGetServerSettings(http
, &num_settings
, &settings
))
2285 cgiSetVariable("SETTINGS_MESSAGE",
2286 cgiText(_("Unable to open cupsd.conf file:")));
2287 cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString());
2290 if ((val
= cupsGetOption(CUPS_SERVER_DEBUG_LOGGING
, num_settings
,
2291 settings
)) != NULL
&& atoi(val
))
2292 cgiSetVariable("DEBUG_LOGGING", "CHECKED");
2294 if ((val
= cupsGetOption(CUPS_SERVER_REMOTE_ADMIN
, num_settings
,
2295 settings
)) != NULL
&& atoi(val
))
2296 cgiSetVariable("REMOTE_ADMIN", "CHECKED");
2298 if ((val
= cupsGetOption(CUPS_SERVER_REMOTE_ANY
, num_settings
,
2299 settings
)) != NULL
&& atoi(val
))
2300 cgiSetVariable("REMOTE_ANY", "CHECKED");
2302 if ((val
= cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS
, num_settings
,
2303 settings
)) != NULL
&& atoi(val
))
2304 cgiSetVariable("REMOTE_PRINTERS", "CHECKED");
2306 if ((val
= cupsGetOption(CUPS_SERVER_SHARE_PRINTERS
, num_settings
,
2307 settings
)) != NULL
&& atoi(val
))
2308 cgiSetVariable("SHARE_PRINTERS", "CHECKED");
2310 if ((val
= cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY
, num_settings
,
2311 settings
)) != NULL
&& atoi(val
))
2312 cgiSetVariable("USER_CANCEL_ANY", "CHECKED");
2315 cgiSetVariable("HAVE_GSSAPI", "1");
2317 if ((val
= cupsGetOption("DefaultAuthType", num_settings
,
2318 settings
)) != NULL
&& !strcasecmp(val
, "Negotiate"))
2319 cgiSetVariable("KERBEROS", "CHECKED");
2320 #endif /* HAVE_GSSAPI */
2322 cupsFreeOptions(num_settings
, settings
);
2325 * See if Samba and the Windows drivers are installed...
2328 if ((datadir
= getenv("CUPS_DATADIR")) == NULL
)
2329 datadir
= CUPS_DATADIR
;
2331 snprintf(filename
, sizeof(filename
), "%s/drivers/pscript5.dll", datadir
);
2332 if (!access(filename
, R_OK
))
2335 * Found Windows 2000 driver file, see if we have smbclient and
2339 if (cupsFileFind("smbclient", getenv("PATH"), 1, filename
,
2340 sizeof(filename
)) &&
2341 cupsFileFind("rpcclient", getenv("PATH"), 1, filename
,
2343 cgiSetVariable("HAVE_SAMBA", "Y");
2346 if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename
,
2348 fputs("ERROR: smbclient not found!\n", stderr
);
2350 if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename
,
2352 fputs("ERROR: rpcclient not found!\n", stderr
);
2362 request
= ippNewRequest(IPP_GET_SUBSCRIPTIONS
);
2364 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2365 NULL
, "ipp://localhost/");
2367 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2369 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
2370 ippDelete(response
);
2374 * Finally, show the main menu template...
2377 cgiStartHTML(cgiText(_("Administration")));
2379 cgiCopyTemplateLang("admin.tmpl");
2386 * 'do_printer_op()' - Do a printer operation.
2390 do_printer_op(http_t
*http
, /* I - HTTP connection */
2391 ipp_op_t op
, /* I - Operation to perform */
2392 const char *title
) /* I - Title of page */
2394 ipp_t
*request
; /* IPP request */
2395 char uri
[HTTP_MAX_URI
]; /* Printer URI */
2396 const char *printer
, /* Printer name (purge-jobs) */
2397 *is_class
; /* Is a class? */
2400 is_class
= cgiGetVariable("IS_CLASS");
2401 printer
= cgiGetVariable("PRINTER_NAME");
2405 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2406 cgiStartHTML(title
);
2407 cgiCopyTemplateLang("error.tmpl");
2413 * Build a printer request, which requires the following
2416 * attributes-charset
2417 * attributes-natural-language
2421 request
= ippNewRequest(op
);
2423 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2424 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
2426 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2430 * Do the request and get back a response...
2433 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
2435 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
2437 puts("Status: 401\n");
2440 else if (cupsLastError() > IPP_OK_CONFLICT
)
2442 cgiStartHTML(title
);
2443 cgiShowIPPError(_("Unable to change printer:"));
2448 * Redirect successful updates back to the printer page...
2451 char url
[1024], /* Printer/class URL */
2452 refresh
[1024]; /* Refresh URL */
2455 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
2456 cgiFormEncode(uri
, url
, sizeof(uri
));
2457 snprintf(refresh
, sizeof(refresh
), "5;URL=/admin/?OP=redirect&URL=%s", uri
);
2458 cgiSetVariable("refresh_page", refresh
);
2460 cgiStartHTML(title
);
2462 if (op
== IPP_PAUSE_PRINTER
)
2463 cgiCopyTemplateLang("printer-stop.tmpl");
2464 else if (op
== IPP_RESUME_PRINTER
)
2465 cgiCopyTemplateLang("printer-start.tmpl");
2466 else if (op
== CUPS_ACCEPT_JOBS
)
2467 cgiCopyTemplateLang("printer-accept.tmpl");
2468 else if (op
== CUPS_REJECT_JOBS
)
2469 cgiCopyTemplateLang("printer-reject.tmpl");
2470 else if (op
== IPP_PURGE_JOBS
)
2471 cgiCopyTemplateLang("printer-purge.tmpl");
2472 else if (op
== CUPS_SET_DEFAULT
)
2473 cgiCopyTemplateLang("printer-default.tmpl");
2481 * 'do_set_allowed_users()' - Set the allowed/denied users for a queue.
2485 do_set_allowed_users(http_t
*http
) /* I - HTTP connection */
2487 int i
; /* Looping var */
2488 ipp_t
*request
, /* IPP request */
2489 *response
; /* IPP response */
2490 char uri
[HTTP_MAX_URI
]; /* Printer URI */
2491 const char *printer
, /* Printer name (purge-jobs) */
2492 *is_class
, /* Is a class? */
2493 *users
, /* List of users or groups */
2494 *type
; /* Allow/deny type */
2495 int num_users
; /* Number of users */
2496 char *ptr
, /* Pointer into users string */
2497 *end
, /* Pointer to end of users string */
2498 quote
; /* Quote character */
2499 ipp_attribute_t
*attr
; /* Attribute */
2500 static const char * const attrs
[] = /* Requested attributes */
2502 "requesting-user-name-allowed",
2503 "requesting-user-name-denied"
2507 is_class
= cgiGetVariable("IS_CLASS");
2508 printer
= cgiGetVariable("PRINTER_NAME");
2512 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2513 cgiStartHTML(cgiText(_("Set Allowed Users")));
2514 cgiCopyTemplateLang("error.tmpl");
2519 users
= cgiGetVariable("users");
2520 type
= cgiGetVariable("type");
2522 if (!users
|| !type
||
2523 (strcmp(type
, "requesting-user-name-allowed") &&
2524 strcmp(type
, "requesting-user-name-denied")))
2527 * Build a Get-Printer-Attributes request, which requires the following
2530 * attributes-charset
2531 * attributes-natural-language
2533 * requested-attributes
2536 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
2538 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2539 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
2541 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2544 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
2545 "requested-attributes",
2546 (int)(sizeof(attrs
) / sizeof(attrs
[0])), NULL
, attrs
);
2549 * Do the request and get back a response...
2552 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2554 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
2556 ippDelete(response
);
2559 cgiStartHTML(cgiText(_("Set Allowed Users")));
2561 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
2563 puts("Status: 401\n");
2566 else if (cupsLastError() > IPP_OK_CONFLICT
)
2567 cgiShowIPPError(_("Unable to get printer attributes:"));
2569 cgiCopyTemplateLang("users.tmpl");
2576 * Save the changes...
2579 for (num_users
= 0, ptr
= (char *)users
; *ptr
; num_users
++)
2582 * Skip whitespace and commas...
2585 while (*ptr
== ',' || isspace(*ptr
& 255))
2588 if (*ptr
== '\'' || *ptr
== '\"')
2591 * Scan quoted name...
2596 for (end
= ptr
; *end
; end
++)
2603 * Scan space or comma-delimited name...
2606 for (end
= ptr
; *end
; end
++)
2607 if (isspace(*end
& 255) || *end
== ',')
2612 * Advance to the next name...
2619 * Build a CUPS-Add-Printer/Class request, which requires the following
2622 * attributes-charset
2623 * attributes-natural-language
2625 * requesting-user-name-{allowed,denied}
2628 request
= ippNewRequest(is_class
? CUPS_ADD_CLASS
: CUPS_ADD_PRINTER
);
2630 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2631 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
2633 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2637 ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
2638 "requesting-user-name-allowed", NULL
, "all");
2641 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
2642 type
, num_users
, NULL
, NULL
);
2644 for (i
= 0, ptr
= (char *)users
; *ptr
; i
++)
2647 * Skip whitespace and commas...
2650 while (*ptr
== ',' || isspace(*ptr
& 255))
2653 if (*ptr
== '\'' || *ptr
== '\"')
2656 * Scan quoted name...
2661 for (end
= ptr
; *end
; end
++)
2668 * Scan space or comma-delimited name...
2671 for (end
= ptr
; *end
; end
++)
2672 if (isspace(*end
& 255) || *end
== ',')
2677 * Terminate the name...
2687 attr
->values
[i
].string
.text
= strdup(ptr
);
2690 * Advance to the next name...
2698 * Do the request and get back a response...
2701 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
2703 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
2705 puts("Status: 401\n");
2708 else if (cupsLastError() > IPP_OK_CONFLICT
)
2710 cgiStartHTML(cgiText(_("Set Allowed Users")));
2711 cgiShowIPPError(_("Unable to change printer:"));
2716 * Redirect successful updates back to the printer page...
2719 char url
[1024], /* Printer/class URL */
2720 refresh
[1024]; /* Refresh URL */
2723 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
2724 cgiFormEncode(uri
, url
, sizeof(uri
));
2725 snprintf(refresh
, sizeof(refresh
), "5;URL=/admin/?OP=redirect&URL=%s",
2727 cgiSetVariable("refresh_page", refresh
);
2729 cgiStartHTML(cgiText(_("Set Allowed Users")));
2731 cgiCopyTemplateLang(is_class
? "class-modified.tmpl" :
2732 "printer-modified.tmpl");
2741 * 'do_set_options()' - Configure the default options for a queue.
2745 do_set_options(http_t
*http
, /* I - HTTP connection */
2746 int is_class
) /* I - Set options for class? */
2748 int i
, j
, k
, m
; /* Looping vars */
2749 int have_options
; /* Have options? */
2750 ipp_t
*request
, /* IPP request */
2751 *response
; /* IPP response */
2752 ipp_attribute_t
*attr
; /* IPP attribute */
2753 char uri
[HTTP_MAX_URI
]; /* Job URI */
2754 const char *var
; /* Variable value */
2755 const char *printer
; /* Printer printer name */
2756 const char *filename
; /* PPD filename */
2757 char tempfile
[1024]; /* Temporary filename */
2758 cups_file_t
*in
, /* Input file */
2759 *out
; /* Output file */
2760 char line
[1024]; /* Line from PPD file */
2761 char keyword
[1024], /* Keyword from Default line */
2762 *keyptr
; /* Pointer into keyword... */
2763 ppd_file_t
*ppd
; /* PPD file */
2764 ppd_group_t
*group
; /* Option group */
2765 ppd_option_t
*option
; /* Option */
2766 ppd_attr_t
*protocol
; /* cupsProtocol attribute */
2767 const char *title
; /* Page title */
2770 title
= cgiText(is_class
? _("Set Class Options") : _("Set Printer Options"));
2772 fprintf(stderr
, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http
,
2776 * Get the printer name...
2779 if ((printer
= cgiGetVariable("PRINTER_NAME")) != NULL
)
2780 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2781 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
2785 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
2786 cgiStartHTML(title
);
2787 cgiCopyTemplateLang("error.tmpl");
2792 fprintf(stderr
, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer
, uri
);
2795 * Get the PPD file...
2801 filename
= cupsGetPPD2(http
, printer
);
2805 fprintf(stderr
, "DEBUG: Got PPD file: \"%s\"\n", filename
);
2807 if ((ppd
= ppdOpenFile(filename
)) == NULL
)
2809 cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i
)));
2810 cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:")));
2811 cgiStartHTML(title
);
2812 cgiCopyTemplateLang("error.tmpl");
2819 fputs("DEBUG: No PPD file\n", stderr
);
2823 if (cgiGetVariable("job_sheets_start") != NULL
||
2824 cgiGetVariable("job_sheets_end") != NULL
)
2831 ppdMarkDefaults(ppd
);
2833 DEBUG_printf(("<P>ppd->num_groups = %d\n"
2834 "<UL>\n", ppd
->num_groups
));
2836 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
2838 DEBUG_printf(("<LI>%s<UL>\n", group
->text
));
2840 for (j
= group
->num_options
, option
= group
->options
;
2843 if ((var
= cgiGetVariable(option
->keyword
)) != NULL
)
2845 DEBUG_printf(("<LI>%s = \"%s\"</LI>\n", option
->keyword
, var
));
2847 ppdMarkOption(ppd
, option
->keyword
, var
);
2851 printf("<LI>%s not defined!</LI>\n", option
->keyword
);
2854 DEBUG_puts("</UL></LI>");
2857 DEBUG_printf(("</UL>\n"
2858 "<P>ppdConflicts(ppd) = %d\n", ppdConflicts(ppd
)));
2861 if (!have_options
|| ppdConflicts(ppd
))
2864 * Show the options to the user...
2867 fputs("DEBUG: Showing options...\n", stderr
);
2869 cgiStartHTML(cgiText(_("Set Printer Options")));
2870 cgiCopyTemplateLang("set-printer-options-header.tmpl");
2876 if (ppdConflicts(ppd
))
2878 for (i
= ppd
->num_groups
, k
= 0, group
= ppd
->groups
;
2881 for (j
= group
->num_options
, option
= group
->options
;
2884 if (option
->conflicted
)
2886 cgiSetArray("ckeyword", k
, option
->keyword
);
2887 cgiSetArray("ckeytext", k
, option
->text
);
2891 cgiCopyTemplateLang("option-conflict.tmpl");
2894 for (i
= ppd
->num_groups
, group
= ppd
->groups
;
2898 if (!strcmp(group
->name
, "InstallableOptions"))
2899 cgiSetVariable("GROUP", cgiText(_("Options Installed")));
2901 cgiSetVariable("GROUP", group
->text
);
2903 cgiCopyTemplateLang("option-header.tmpl");
2905 for (j
= group
->num_options
, option
= group
->options
;
2909 if (!strcmp(option
->keyword
, "PageRegion"))
2912 cgiSetVariable("KEYWORD", option
->keyword
);
2913 cgiSetVariable("KEYTEXT", option
->text
);
2915 if (option
->conflicted
)
2916 cgiSetVariable("CONFLICTED", "1");
2918 cgiSetVariable("CONFLICTED", "0");
2920 cgiSetSize("CHOICES", 0);
2921 cgiSetSize("TEXT", 0);
2922 for (k
= 0, m
= 0; k
< option
->num_choices
; k
++)
2925 * Hide custom option values...
2928 if (!strcmp(option
->choices
[k
].choice
, "Custom"))
2931 cgiSetArray("CHOICES", m
, option
->choices
[k
].choice
);
2932 cgiSetArray("TEXT", m
, option
->choices
[k
].text
);
2936 if (option
->choices
[k
].marked
)
2937 cgiSetVariable("DEFCHOICE", option
->choices
[k
].choice
);
2942 case PPD_UI_BOOLEAN
:
2943 cgiCopyTemplateLang("option-boolean.tmpl");
2945 case PPD_UI_PICKONE
:
2946 cgiCopyTemplateLang("option-pickone.tmpl");
2948 case PPD_UI_PICKMANY
:
2949 cgiCopyTemplateLang("option-pickmany.tmpl");
2954 cgiCopyTemplateLang("option-trailer.tmpl");
2959 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
2960 * following attributes:
2962 * attributes-charset
2963 * attributes-natural-language
2967 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
2969 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
2970 "localhost", 0, "/printers/%s", printer
);
2971 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
2975 * Do the request and get back a response...
2978 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
2980 if ((attr
= ippFindAttribute(response
, "job-sheets-supported",
2981 IPP_TAG_ZERO
)) != NULL
)
2984 * Add the job sheets options...
2987 cgiSetVariable("GROUP", cgiText(_("Banners")));
2988 cgiCopyTemplateLang("option-header.tmpl");
2990 cgiSetSize("CHOICES", attr
->num_values
);
2991 cgiSetSize("TEXT", attr
->num_values
);
2992 for (k
= 0; k
< attr
->num_values
; k
++)
2994 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
2995 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
2998 attr
= ippFindAttribute(response
, "job-sheets-default", IPP_TAG_ZERO
);
3000 cgiSetVariable("KEYWORD", "job_sheets_start");
3001 cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner")));
3002 cgiSetVariable("DEFCHOICE", attr
== NULL
?
3003 "" : attr
->values
[0].string
.text
);
3005 cgiCopyTemplateLang("option-pickone.tmpl");
3007 cgiSetVariable("KEYWORD", "job_sheets_end");
3008 cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner")));
3009 cgiSetVariable("DEFCHOICE", attr
== NULL
&& attr
->num_values
> 1 ?
3010 "" : attr
->values
[1].string
.text
);
3012 cgiCopyTemplateLang("option-pickone.tmpl");
3014 cgiCopyTemplateLang("option-trailer.tmpl");
3017 if (ippFindAttribute(response
, "printer-error-policy-supported",
3019 ippFindAttribute(response
, "printer-op-policy-supported",
3023 * Add the error and operation policy options...
3026 cgiSetVariable("GROUP", cgiText(_("Policies")));
3027 cgiCopyTemplateLang("option-header.tmpl");
3033 attr
= ippFindAttribute(response
, "printer-error-policy-supported",
3038 cgiSetSize("CHOICES", attr
->num_values
);
3039 cgiSetSize("TEXT", attr
->num_values
);
3040 for (k
= 0; k
< attr
->num_values
; k
++)
3042 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
3043 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
3046 attr
= ippFindAttribute(response
, "printer-error-policy",
3049 cgiSetVariable("KEYWORD", "printer_error_policy");
3050 cgiSetVariable("KEYTEXT", cgiText(_("Error Policy")));
3051 cgiSetVariable("DEFCHOICE", attr
== NULL
?
3052 "" : attr
->values
[0].string
.text
);
3055 cgiCopyTemplateLang("option-pickone.tmpl");
3058 * Operation policy...
3061 attr
= ippFindAttribute(response
, "printer-op-policy-supported",
3066 cgiSetSize("CHOICES", attr
->num_values
);
3067 cgiSetSize("TEXT", attr
->num_values
);
3068 for (k
= 0; k
< attr
->num_values
; k
++)
3070 cgiSetArray("CHOICES", k
, attr
->values
[k
].string
.text
);
3071 cgiSetArray("TEXT", k
, attr
->values
[k
].string
.text
);
3074 attr
= ippFindAttribute(response
, "printer-op-policy", IPP_TAG_ZERO
);
3076 cgiSetVariable("KEYWORD", "printer_op_policy");
3077 cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy")));
3078 cgiSetVariable("DEFCHOICE", attr
== NULL
?
3079 "" : attr
->values
[0].string
.text
);
3081 cgiCopyTemplateLang("option-pickone.tmpl");
3084 cgiCopyTemplateLang("option-trailer.tmpl");
3087 ippDelete(response
);
3091 * Binary protocol support...
3094 if (ppd
&& ppd
->protocols
&& strstr(ppd
->protocols
, "BCP"))
3096 protocol
= ppdFindAttr(ppd
, "cupsProtocol", NULL
);
3098 cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol")));
3099 cgiCopyTemplateLang("option-header.tmpl");
3101 cgiSetSize("CHOICES", 2);
3102 cgiSetSize("TEXT", 2);
3103 cgiSetArray("CHOICES", 0, "None");
3104 cgiSetArray("TEXT", 0, cgiText(_("None")));
3106 if (strstr(ppd
->protocols
, "TBCP"))
3108 cgiSetArray("CHOICES", 1, "TBCP");
3109 cgiSetArray("TEXT", 1, "TBCP");
3113 cgiSetArray("CHOICES", 1, "BCP");
3114 cgiSetArray("TEXT", 1, "BCP");
3117 cgiSetVariable("KEYWORD", "protocol");
3118 cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol")));
3119 cgiSetVariable("DEFCHOICE", protocol
? protocol
->value
: "None");
3121 cgiCopyTemplateLang("option-pickone.tmpl");
3123 cgiCopyTemplateLang("option-trailer.tmpl");
3126 cgiCopyTemplateLang("set-printer-options-trailer.tmpl");
3132 * Set default options...
3135 fputs("DEBUG: Setting options...\n", stderr
);
3139 out
= cupsTempFile2(tempfile
, sizeof(tempfile
));
3140 in
= cupsFileOpen(filename
, "r");
3144 cgiSetVariable("ERROR", strerror(errno
));
3145 cgiStartHTML(cgiText(_("Set Printer Options")));
3146 cgiCopyTemplateLang("error.tmpl");
3162 while (cupsFileGets(in
, line
, sizeof(line
)))
3164 if (!strncmp(line
, "*cupsProtocol:", 14) && cgiGetVariable("protocol"))
3166 else if (strncmp(line
, "*Default", 8))
3167 cupsFilePrintf(out
, "%s\n", line
);
3171 * Get default option name...
3174 strlcpy(keyword
, line
+ 8, sizeof(keyword
));
3176 for (keyptr
= keyword
; *keyptr
; keyptr
++)
3177 if (*keyptr
== ':' || isspace(*keyptr
& 255))
3182 if (!strcmp(keyword
, "PageRegion") ||
3183 !strcmp(keyword
, "PaperDimension") ||
3184 !strcmp(keyword
, "ImageableArea"))
3185 var
= cgiGetVariable("PageSize");
3187 var
= cgiGetVariable(keyword
);
3190 cupsFilePrintf(out
, "*Default%s: %s\n", keyword
, var
);
3192 cupsFilePrintf(out
, "%s\n", line
);
3196 if ((var
= cgiGetVariable("protocol")) != NULL
)
3197 cupsFilePrintf(out
, "*cupsProtocol: %s\n", cgiGetVariable("protocol"));
3205 * Make sure temporary filename is cleared when there is no PPD...
3212 * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the
3213 * following attributes:
3215 * attributes-charset
3216 * attributes-natural-language
3218 * job-sheets-default
3219 * printer-error-policy
3224 request
= ippNewRequest(is_class
? CUPS_ADD_MODIFY_CLASS
:
3225 CUPS_ADD_MODIFY_PRINTER
);
3227 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3230 attr
= ippAddStrings(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
3231 "job-sheets-default", 2, NULL
, NULL
);
3232 attr
->values
[0].string
.text
= _cupsStrAlloc(cgiGetVariable("job_sheets_start"));
3233 attr
->values
[1].string
.text
= _cupsStrAlloc(cgiGetVariable("job_sheets_end"));
3235 if ((var
= cgiGetVariable("printer_error_policy")) != NULL
)
3236 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
3237 "printer-error-policy", NULL
, var
);
3239 if ((var
= cgiGetVariable("printer_op_policy")) != NULL
)
3240 attr
= ippAddString(request
, IPP_TAG_PRINTER
, IPP_TAG_NAME
,
3241 "printer-op-policy", NULL
, var
);
3244 * Do the request and get back a response...
3248 ippDelete(cupsDoFileRequest(http
, request
, "/admin/", tempfile
));
3250 ippDelete(cupsDoRequest(http
, request
, "/admin/"));
3252 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
3254 puts("Status: 401\n");
3257 else if (cupsLastError() > IPP_OK_CONFLICT
)
3259 cgiStartHTML(title
);
3260 cgiShowIPPError(_("Unable to set options:"));
3265 * Redirect successful updates back to the printer page...
3268 char refresh
[1024]; /* Refresh URL */
3271 cgiFormEncode(uri
, printer
, sizeof(uri
));
3272 snprintf(refresh
, sizeof(refresh
), "5;URL=/admin/?OP=redirect&URL=/%s/%s",
3273 is_class
? "classes" : "printers", uri
);
3274 cgiSetVariable("refresh_page", refresh
);
3276 cgiStartHTML(title
);
3278 cgiCopyTemplateLang("printer-configured.tmpl");
3293 * 'do_set_sharing()' - Set printer-is-shared value...
3297 do_set_sharing(http_t
*http
) /* I - HTTP connection */
3299 ipp_t
*request
, /* IPP request */
3300 *response
; /* IPP response */
3301 char uri
[HTTP_MAX_URI
]; /* Printer URI */
3302 const char *printer
, /* Printer name */
3303 *is_class
, /* Is a class? */
3304 *shared
; /* Sharing value */
3307 is_class
= cgiGetVariable("IS_CLASS");
3308 printer
= cgiGetVariable("PRINTER_NAME");
3309 shared
= cgiGetVariable("SHARED");
3311 if (!printer
|| !shared
)
3313 cgiSetVariable("ERROR", cgiText(_("Missing form variable!")));
3314 cgiStartHTML(cgiText(_("Set Publishing")));
3315 cgiCopyTemplateLang("error.tmpl");
3321 * Build a CUPS-Add-Printer/CUPS-Add-Class request, which requires the
3322 * following attributes:
3324 * attributes-charset
3325 * attributes-natural-language
3330 request
= ippNewRequest(is_class
? CUPS_ADD_CLASS
: CUPS_ADD_PRINTER
);
3332 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
3333 "localhost", 0, is_class
? "/classes/%s" : "/printers/%s",
3335 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
3338 ippAddBoolean(request
, IPP_TAG_OPERATION
, "printer-is-shared", atoi(shared
));
3341 * Do the request and get back a response...
3344 if ((response
= cupsDoRequest(http
, request
, "/admin/")) != NULL
)
3346 cgiSetIPPVars(response
, NULL
, NULL
, NULL
, 0);
3348 ippDelete(response
);
3351 if (cupsLastError() == IPP_NOT_AUTHORIZED
)
3353 puts("Status: 401\n");
3356 else if (cupsLastError() > IPP_OK_CONFLICT
)
3358 cgiStartHTML(cgiText(_("Set Publishing")));
3359 cgiShowIPPError(_("Unable to change printer-is-shared attribute:"));
3364 * Redirect successful updates back to the printer page...
3367 char url
[1024], /* Printer/class URL */
3368 refresh
[1024]; /* Refresh URL */
3371 cgiRewriteURL(uri
, url
, sizeof(url
), NULL
);
3372 cgiFormEncode(uri
, url
, sizeof(uri
));
3373 snprintf(refresh
, sizeof(refresh
), "5;URL=/admin/?OP=redirect&URL=%s", uri
);
3374 cgiSetVariable("refresh_page", refresh
);
3376 cgiStartHTML(cgiText(_("Set Publishing")));
3377 cgiCopyTemplateLang(is_class
? "class-modified.tmpl" :
3378 "printer-modified.tmpl");
3386 * 'match_string()' - Return the number of matching characters.
3389 static int /* O - Number of matching characters */
3390 match_string(const char *a
, /* I - First string */
3391 const char *b
) /* I - Second string */
3393 int count
; /* Number of matching characters */
3397 * Loop through both strings until we hit the end of either or we find
3398 * a non-matching character. For the purposes of comparison, we ignore
3399 * whitespace and do a case-insensitive comparison so that we have a
3400 * better chance of finding a match...
3403 for (count
= 0; *a
&& *b
; a
++, b
++, count
++)
3406 * Skip leading whitespace characters...
3409 while (isspace(*a
& 255))
3412 while (isspace(*b
& 255))
3416 * Break out if we run out of characters...
3423 * Do a case-insensitive comparison of the next two chars...
3426 if (tolower(*a
& 255) != tolower(*b
& 255))
3435 * End of "$Id: admin.c 7012 2007-10-10 21:22:45Z mike $".