From: jlovell Date: Thu, 5 Apr 2007 18:24:21 +0000 (+0000) Subject: Load cups into easysw/current. X-Git-Tag: release-1.6.3~216 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=323c5de1e804061e344172cc9a9551bb0ee71005;p=thirdparty%2Fcups.git Load cups into easysw/current. git-svn-id: svn+ssh://src.apple.com/svn/cups/easysw/current@308 a1ca3aef-8c08-0410-bb20-df032aa958be --- diff --git a/CHANGES-1.2.txt b/CHANGES-1.2.txt index f7e03a8f32..a6b1ba0d02 100644 --- a/CHANGES-1.2.txt +++ b/CHANGES-1.2.txt @@ -3,6 +3,7 @@ CHANGES-1.2.txt CHANGES IN CUPS V1.2.11 + - The USB backend did not work on NetBSD (STR #2324) - The printer-state-reasons attribute was incorrectly cleared after a job completed (STR #2323) - The scheduler did not set the printer operation policy diff --git a/CHANGES.txt b/CHANGES.txt index 98e0328a0e..45602a84df 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,9 +1,27 @@ -CHANGES.txt - 2007-03-26 +CHANGES.txt - 2007-04-04 ------------------------ CHANGES IN CUPS V1.3 - Documentation updates (STR #1775, STR #2130, STR #2131) + - The cupstestppd program now tests for existing filters, + icons, profiles, and dialog extensions (STR #2326) + - The web interface no longer lists new printers on the + main administration page. Instead, a new "List Available + Printers" button is provided that shows a separate page + with the list of printers. + - The web interface now supports setting the banner and + policy options on raw printers and classes (STR #2238) + - The socket backend now reads any pending back-channel + data before shutting down the socket (STR #2325) + - Added a new ErrorPolicy directive in the cupsd.conf + file (STR #1871) + - Printers that use JCL options are now exported to Samba + correctly (STR #1985) + - The IPP backend now relays printer-state-message values + from the server to the client (STR #2109) + - Added support for the PWG printer-alert and + printer-alert-description attributes (STR #2088) - Added support for LPD "stream" mode (STR #2036) - The scheduler now reports the PostScript product string from PPD files in CUPS-Get-PPDs responses (STR #1900) diff --git a/backend/ipp.c b/backend/ipp.c index 616c18c626..fba56dbfe0 100644 --- a/backend/ipp.c +++ b/backend/ipp.c @@ -1,5 +1,5 @@ /* - * "$Id: ipp.c 6422 2007-03-30 20:49:37Z mike $" + * "$Id: ipp.c 6434 2007-04-02 22:07:10Z mike $" * * IPP backend for the Common UNIX Printing System (CUPS). * @@ -150,6 +150,7 @@ main(int argc, /* I - Number of command-line args */ "document-format-supported", "printer-is-accepting-jobs", "printer-state", + "printer-state-message", "printer-state-reasons", }; static const char * const jattrs[] = @@ -1286,6 +1287,11 @@ check_printer_state( { ipp_t *request, /* IPP request */ *response; /* IPP response */ + static const char * const attrs[] = /* Attributes we want */ + { + "printer-state-message", + "printer-state-reasons" + }; /* @@ -1302,8 +1308,9 @@ check_printer_state( ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, user); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", NULL, "printer-state-reasons"); + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs); /* * Do the request... @@ -1437,7 +1444,8 @@ report_printer_state(ipp_t *ipp) /* I - IPP response */ { int i; /* Looping var */ int count; /* Count of reasons shown... */ - ipp_attribute_t *reasons; /* printer-state-reasons */ + ipp_attribute_t *psm, /* pritner-state-message */ + *reasons; /* printer-state-reasons */ const char *reason; /* Current reason */ const char *message; /* Message to show */ char unknown[1024]; /* Unknown message string */ @@ -1445,6 +1453,10 @@ report_printer_state(ipp_t *ipp) /* I - IPP response */ char state[1024]; /* State string */ + if ((psm = ippFindAttribute(ipp, "printer-state-message", + IPP_TAG_TEXT)) != NULL) + fprintf(stderr, "INFO: %s\n", psm->values[0].string.text); + if ((reasons = ippFindAttribute(ipp, "printer-state-reasons", IPP_TAG_KEYWORD)) == NULL) return (0); @@ -1749,5 +1761,5 @@ sigterm_handler(int sig) /* I - Signal */ /* - * End of "$Id: ipp.c 6422 2007-03-30 20:49:37Z mike $". + * End of "$Id: ipp.c 6434 2007-04-02 22:07:10Z mike $". */ diff --git a/backend/scsi.c b/backend/scsi.c index 2bb03f84de..91544b8b95 100644 --- a/backend/scsi.c +++ b/backend/scsi.c @@ -1,5 +1,5 @@ /* - * "$Id: scsi.c 6411 2007-03-28 23:02:51Z mike $" + * "$Id: scsi.c 6432 2007-04-02 21:50:28Z mike $" * * SCSI printer backend for the Common UNIX Printing System (CUPS). * @@ -55,6 +55,7 @@ #include #include +#include #include #include #include @@ -222,5 +223,5 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ /* - * End of "$Id: scsi.c 6411 2007-03-28 23:02:51Z mike $". + * End of "$Id: scsi.c 6432 2007-04-02 21:50:28Z mike $". */ diff --git a/backend/socket.c b/backend/socket.c index 4c779ec7d7..19994f0421 100644 --- a/backend/socket.c +++ b/backend/socket.c @@ -1,5 +1,5 @@ /* - * "$Id: socket.c 6403 2007-03-27 16:00:56Z mike $" + * "$Id: socket.c 6438 2007-04-03 17:52:41Z mike $" * * AppSocket backend for the Common UNIX Printing System (CUPS). * @@ -27,6 +27,7 @@ * * main() - Send a file to the printer or server. * side_cb() - Handle side-channel requests... + * wait_bc() - Wait for back-channel data... */ /* @@ -56,6 +57,7 @@ */ static void side_cb(int print_fd, int device_fd, int use_bc); +static int wait_bc(int device_fd, int secs); /* @@ -93,9 +95,6 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ *addr; /* Connected address */ char addrname[256]; /* Address name */ ssize_t tbytes; /* Total number of bytes written */ - struct timeval timeout; /* Timeout for select() */ - fd_set input; /* Input set for select() */ - ssize_t bc_bytes; /* Number of back-channel bytes read */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) struct sigaction action; /* Actions for POSIX signals */ #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ @@ -387,6 +386,12 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ CUPS_LLCAST tbytes); } + /* + * Get any pending back-channel data... + */ + + while (wait_bc(device_fd, 5) > 0); + if (waiteof) { /* @@ -398,37 +403,7 @@ main(int argc, /* I - Number of command-line arguments (6 or 7) */ shutdown(device_fd, 1); - for (;;) - { - /* - * Wait a maximum of 90 seconds for backchannel data or a closed - * connection... - */ - - timeout.tv_sec = 90; - timeout.tv_usec = 0; - - FD_ZERO(&input); - FD_SET(device_fd, &input); - - if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0) - { - /* - * Grab the data coming back and spit it out to stderr... - */ - - if ((bc_bytes = read(device_fd, resource, sizeof(resource))) > 0) - { - fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n", - (int)bc_bytes); - cupsBackChannelWrite(resource, bc_bytes, 1.0); - } - else - break; - } - else - break; - } + while (wait_bc(device_fd, 90) > 0); } /* @@ -503,5 +478,49 @@ side_cb(int print_fd, /* I - Print file */ /* - * End of "$Id: socket.c 6403 2007-03-27 16:00:56Z mike $". + * 'wait_bc()' - Wait for back-channel data... + */ + +static int /* O - # bytes read or -1 on error */ +wait_bc(int device_fd, /* I - Socket */ + int secs) /* I - Seconds to wait */ +{ + struct timeval timeout; /* Timeout for select() */ + fd_set input; /* Input set for select() */ + ssize_t bytes; /* Number of back-channel bytes read */ + char buffer[1024]; /* Back-channel buffer */ + + + /* + * Wait up to "secs" seconds for backchannel data... + */ + + timeout.tv_sec = secs; + timeout.tv_usec = 0; + + FD_ZERO(&input); + FD_SET(device_fd, &input); + + if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0) + { + /* + * Grab the data coming back and spit it out to stderr... + */ + + if ((bytes = read(device_fd, buffer, sizeof(buffer))) > 0) + { + fprintf(stderr, "DEBUG: Received %d bytes of back-channel data!\n", + (int)bytes); + cupsBackChannelWrite(buffer, bytes, 1.0); + } + + return (bytes); + } + else + return (-1); +} + + +/* + * End of "$Id: socket.c 6438 2007-04-03 17:52:41Z mike $". */ diff --git a/backend/usb-darwin.c b/backend/usb-darwin.c index 4b3cc47b19..d140769617 100644 --- a/backend/usb-darwin.c +++ b/backend/usb-darwin.c @@ -1,5 +1,5 @@ /* - * "$Id: usb-darwin.c 6422 2007-03-30 20:49:37Z mike $" + * "$Id: usb-darwin.c 6432 2007-04-02 21:50:28Z mike $" * * Copyright © 2005-2007 Apple Inc. All rights reserved. * @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -1728,5 +1729,5 @@ static void usbGetDevState(printer_data_t *userData, cups_sc_status_t *status, c } /* - * End of "$Id: usb-darwin.c 6422 2007-03-30 20:49:37Z mike $". + * End of "$Id: usb-darwin.c 6432 2007-04-02 21:50:28Z mike $". */ diff --git a/backend/usb-unix.c b/backend/usb-unix.c index 0f58098bbc..feddc046a9 100644 --- a/backend/usb-unix.c +++ b/backend/usb-unix.c @@ -1,5 +1,5 @@ /* - * "$Id: usb-unix.c 6422 2007-03-30 20:49:37Z mike $" + * "$Id: usb-unix.c 6437 2007-04-03 17:38:10Z mike $" * * USB port backend for the Common UNIX Printing System (CUPS). * @@ -80,6 +80,15 @@ print_device(const char *uri, /* I - Device URI */ do { +#ifdef __NetBSD__ + /* + * NetBSD's ulpt driver currently does not support the + * back-channel... + */ + + use_bc = 0; + +#else /* * Disable backchannel data when printing to Brother, Canon, or * Minolta USB printers - apparently these printers will return @@ -91,6 +100,7 @@ print_device(const char *uri, /* I - Device URI */ strcasecmp(hostname, "Canon") && strcasecmp(hostname, "Konica Minolta") && strcasecmp(hostname, "Minolta"); +#endif /* __NetBSD__ */ if ((device_fd = open_device(uri, &use_bc)) == -1) { @@ -519,7 +529,12 @@ open_device(const char *uri, /* I - Device URI */ } #else { - if ((fd = open(uri + 4, O_RDWR | O_EXCL)) < 0) + if (use_bc) + fd = open(uri + 4, O_RDWR | O_EXCL); + else + fd = -1; + + if (fd < 0) { fd = open(uri + 4, O_WRONLY | O_EXCL); *use_bc = 0; @@ -602,5 +617,5 @@ side_cb(int print_fd, /* I - Print file */ /* - * End of "$Id: usb-unix.c 6422 2007-03-30 20:49:37Z mike $". + * End of "$Id: usb-unix.c 6437 2007-04-03 17:38:10Z mike $". */ diff --git a/cgi-bin/admin.c b/cgi-bin/admin.c index f8c0d55e47..ca0987660d 100644 --- a/cgi-bin/admin.c +++ b/cgi-bin/admin.c @@ -1,5 +1,5 @@ /* - * "$Id: admin.c 6378 2007-03-21 07:18:18Z mike $" + * "$Id: admin.c 6440 2007-04-03 23:17:17Z mike $" * * Administration CGI for the Common UNIX Printing System (CUPS). * @@ -28,14 +28,15 @@ * do_am_class() - Add or modify a class. * do_am_printer() - Add or modify a printer. * do_cancel_subscription() - Cancel a subscription. - * do_config_printer() - Configure the default options for a printer. * do_config_server() - Configure server settings. * do_delete_class() - Delete a class... * do_delete_printer() - Delete a printer... * do_export() - Export printers to Samba... + * do_list_printers() - List available printers... * do_menu() - Show the main menu... * do_printer_op() - Do a printer operation. * do_set_allowed_users() - Set the allowed/denied users for a queue. + * do_set_options() - Configure the default options for a queue. * do_set_sharing() - Set printer-is-shared value... * match_string() - Return the number of matching characters. */ @@ -61,11 +62,12 @@ static void do_add_rss_subscription(http_t *http); static void do_am_class(http_t *http, int modify); static void do_am_printer(http_t *http, int modify); static void do_cancel_subscription(http_t *http); -static void do_config_printer(http_t *http); +static void do_set_options(http_t *http, int is_class); static void do_config_server(http_t *http); static void do_delete_class(http_t *http); static void do_delete_printer(http_t *http); static void do_export(http_t *http); +static void do_list_printers(http_t *http); static void do_menu(http_t *http); static void do_printer_op(http_t *http, ipp_op_t op, const char *title); @@ -172,6 +174,8 @@ main(int argc, /* I - Number of command-line arguments */ do_printer_op(http, CUPS_SET_DEFAULT, cgiText(_("Set As Default"))); else if (!strcmp(op, "set-sharing")) do_set_sharing(http); + else if (!strcmp(op, "list-available-printers")) + do_list_printers(http); else if (!strcmp(op, "add-class")) do_am_class(http, 0); else if (!strcmp(op, "add-printer")) @@ -184,8 +188,10 @@ main(int argc, /* I - Number of command-line arguments */ do_delete_class(http); else if (!strcmp(op, "delete-printer")) do_delete_printer(http); + else if (!strcmp(op, "set-class-options")) + do_set_options(http, 1); else if (!strcmp(op, "set-printer-options")) - do_config_printer(http); + do_set_options(http, 0); else if (!strcmp(op, "config-server")) do_config_server(http); else if (!strcmp(op, "export-samba")) @@ -1295,972 +1301,1018 @@ do_cancel_subscription(http_t *http)/* I - HTTP connection */ /* - * 'do_config_printer()' - Configure the default options for a printer. + * 'do_config_server()' - Configure server settings. */ static void -do_config_printer(http_t *http) /* I - HTTP connection */ +do_config_server(http_t *http) /* I - HTTP connection */ { - int i, j, k, m; /* Looping vars */ - int have_options; /* Have options? */ - ipp_t *request, /* IPP request */ - *response; /* IPP response */ - ipp_attribute_t *attr; /* IPP attribute */ - char uri[HTTP_MAX_URI]; /* Job URI */ - const char *var; /* Variable value */ - const char *printer; /* Printer printer name */ - const char *filename; /* PPD filename */ - char tempfile[1024]; /* Temporary filename */ - cups_file_t *in, /* Input file */ - *out; /* Output file */ - char line[1024]; /* Line from PPD file */ - char keyword[1024], /* Keyword from Default line */ - *keyptr; /* Pointer into keyword... */ - ppd_file_t *ppd; /* PPD file */ - ppd_group_t *group; /* Option group */ - ppd_option_t *option; /* Option */ - ppd_attr_t *protocol; /* cupsProtocol attribute */ - const char *title; /* Page title */ - - - title = cgiText(_("Set Printer Options")); - - fprintf(stderr, "DEBUG: do_config_printer(http=%p)\n", http); - - /* - * Get the printer name... - */ - - if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - else + if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF")) { - cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); - cgiStartHTML(title); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - return; - } + /* + * Save basic setting changes... + */ - fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri); + int num_settings; /* Number of server settings */ + cups_option_t *settings; /* Server settings */ + const char *debug_logging, /* DEBUG_LOGGING value */ + *remote_admin, /* REMOTE_ADMIN value */ + *remote_any, /* REMOTE_ANY value */ + *remote_printers, + /* REMOTE_PRINTERS value */ + *share_printers,/* SHARE_PRINTERS value */ +#ifdef HAVE_GSSAPI + *default_auth_type, + /* DefaultAuthType value */ +#endif /* HAVE_GSSAPI */ + *user_cancel_any; + /* USER_CANCEL_ANY value */ - /* - * Get the PPD file... - */ - if ((filename = cupsGetPPD2(http, printer)) == NULL) - { - fputs("DEBUG: No PPD file!?!\n", stderr); + /* + * Get the checkbox values from the form... + */ - cgiStartHTML(title); - cgiShowIPPError(_("Unable to get PPD file!")); - cgiEndHTML(); - return; - } + debug_logging = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0"; + remote_admin = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0"; + remote_any = cgiGetVariable("REMOTE_ANY") ? "1" : "0"; + remote_printers = cgiGetVariable("REMOTE_PRINTERS") ? "1" : "0"; + share_printers = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0"; + user_cancel_any = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0"; +#ifdef HAVE_GSSAPI + default_auth_type = cgiGetVariable("KERBEROS") ? "Negotiate" : "Basic"; - fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename); + fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type); +#endif /* HAVE_GSSAPI */ - if ((ppd = ppdOpenFile(filename)) == NULL) - { - cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i))); - cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:"))); - cgiStartHTML(title); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - return; - } + /* + * Get the current server settings... + */ - if (cgiGetVariable("job_sheets_start") != NULL || - cgiGetVariable("job_sheets_end") != NULL) - have_options = 1; - else - have_options = 0; + if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) + { + cgiStartHTML(cgiText(_("Change Settings"))); + cgiSetVariable("MESSAGE", + cgiText(_("Unable to change server settings:"))); + cgiSetVariable("ERROR", cupsLastErrorString()); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + return; + } - ppdMarkDefaults(ppd); + /* + * See if the settings have changed... + */ + + if (strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, + num_settings, settings)) || + strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, + num_settings, settings)) || + strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY, + num_settings, settings)) || + strcmp(remote_printers, cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, + num_settings, settings)) || + strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, + num_settings, settings)) || +#ifdef HAVE_GSSAPI + !cupsGetOption("DefaultAuthType", num_settings, settings) || + strcmp(default_auth_type, cupsGetOption("DefaultAuthType", + num_settings, settings)) || +#endif /* HAVE_GSSAPI */ + strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, + num_settings, settings))) + { + /* + * Settings *have* changed, so save the changes... + */ - DEBUG_printf(("

ppd->num_groups = %d\n" - "

    \n", ppd->num_groups)); + cupsFreeOptions(num_settings, settings); - for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) - { - DEBUG_printf(("
  • %s
      \n", group->text)); + num_settings = 0; + num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING, + debug_logging, num_settings, &settings); + num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN, + remote_admin, num_settings, &settings); + num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY, + remote_any, num_settings, &settings); + num_settings = cupsAddOption(CUPS_SERVER_REMOTE_PRINTERS, + remote_printers, num_settings, &settings); + num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS, + share_printers, num_settings, &settings); + num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY, + user_cancel_any, num_settings, &settings); +#ifdef HAVE_GSSAPI + num_settings = cupsAddOption("DefaultAuthType", default_auth_type, + num_settings, &settings); +#endif /* HAVE_GSSAPI */ - for (j = group->num_options, option = group->options; - j > 0; - j --, option ++) - if ((var = cgiGetVariable(option->keyword)) != NULL) + if (!cupsAdminSetServerSettings(http, num_settings, settings)) { - DEBUG_printf(("
    • %s = \"%s\"
    • \n", option->keyword, var)); - have_options = 1; - ppdMarkOption(ppd, option->keyword, var); + cgiStartHTML(cgiText(_("Change Settings"))); + cgiSetVariable("MESSAGE", + cgiText(_("Unable to change server settings:"))); + cgiSetVariable("ERROR", cupsLastErrorString()); + cgiCopyTemplateLang("error.tmpl"); } -#ifdef DEBUG else - printf("
    • %s not defined!
    • \n", option->keyword); -#endif /* DEBUG */ + { + cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); + cgiStartHTML(cgiText(_("Change Settings"))); + cgiCopyTemplateLang("restart.tmpl"); + } + } + else + { + /* + * No changes... + */ - DEBUG_puts("
  • "); - } + cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); + cgiStartHTML(cgiText(_("Change Settings"))); + cgiCopyTemplateLang("norestart.tmpl"); + } - DEBUG_printf(("
\n" - "

ppdConflicts(ppd) = %d\n", ppdConflicts(ppd))); + cupsFreeOptions(num_settings, settings); - if (!have_options || ppdConflicts(ppd)) + cgiEndHTML(); + } + else if (cgiIsPOST()) { /* - * Show the options to the user... + * Save hand-edited config file... */ - fputs("DEBUG: Showing options...\n", stderr); + http_status_t status; /* PUT status */ + char tempfile[1024]; /* Temporary new cupsd.conf */ + int tempfd; /* Temporary file descriptor */ + cups_file_t *temp; /* Temporary file */ + const char *start, /* Start of line */ + *end; /* End of line */ - ppdLocalize(ppd); - cgiStartHTML(cgiText(_("Set Printer Options"))); - cgiCopyTemplateLang("set-printer-options-header.tmpl"); + /* + * Create a temporary file for the new cupsd.conf file... + */ - if (ppdConflicts(ppd)) + if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) { - for (i = ppd->num_groups, k = 0, group = ppd->groups; - i > 0; - i --, group ++) - for (j = group->num_options, option = group->options; - j > 0; - j --, option ++) - if (option->conflicted) - { - cgiSetArray("ckeyword", k, option->keyword); - cgiSetArray("ckeytext", k, option->text); - k ++; - } - - cgiCopyTemplateLang("option-conflict.tmpl"); + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:"))); + cgiSetVariable("ERROR", strerror(errno)); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + + perror(tempfile); + return; } - for (i = ppd->num_groups, group = ppd->groups; - i > 0; - i --, group ++) + if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL) { - if (!strcmp(group->name, "InstallableOptions")) - cgiSetVariable("GROUP", cgiText(_("Options Installed"))); - else - cgiSetVariable("GROUP", group->text); - - cgiCopyTemplateLang("option-header.tmpl"); + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:"))); + cgiSetVariable("ERROR", strerror(errno)); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); - for (j = group->num_options, option = group->options; - j > 0; - j --, option ++) - { - if (!strcmp(option->keyword, "PageRegion")) - continue; + perror(tempfile); + close(tempfd); + unlink(tempfile); + return; + } - cgiSetVariable("KEYWORD", option->keyword); - cgiSetVariable("KEYTEXT", option->text); - - if (option->conflicted) - cgiSetVariable("CONFLICTED", "1"); - else - cgiSetVariable("CONFLICTED", "0"); + /* + * Copy the cupsd.conf text from the form variable... + */ - cgiSetSize("CHOICES", 0); - cgiSetSize("TEXT", 0); - for (k = 0, m = 0; k < option->num_choices; k ++) - { - /* - * Hide custom option values... - */ + start = cgiGetVariable("CUPSDCONF"); + while (start) + { + if ((end = strstr(start, "\r\n")) == NULL) + if ((end = strstr(start, "\n")) == NULL) + end = start + strlen(start); - if (!strcmp(option->choices[k].choice, "Custom")) - continue; + cupsFileWrite(temp, start, end - start); + cupsFilePutChar(temp, '\n'); - cgiSetArray("CHOICES", m, option->choices[k].choice); - cgiSetArray("TEXT", m, option->choices[k].text); + if (*end == '\r') + start = end + 2; + else if (*end == '\n') + start = end + 1; + else + start = NULL; + } - m ++; + cupsFileClose(temp); - if (option->choices[k].marked) - cgiSetVariable("DEFCHOICE", option->choices[k].choice); - } + /* + * Upload the configuration file to the server... + */ - switch (option->ui) - { - case PPD_UI_BOOLEAN : - cgiCopyTemplateLang("option-boolean.tmpl"); - break; - case PPD_UI_PICKONE : - cgiCopyTemplateLang("option-pickone.tmpl"); - break; - case PPD_UI_PICKMANY : - cgiCopyTemplateLang("option-pickmany.tmpl"); - break; - } - } + status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile); - cgiCopyTemplateLang("option-trailer.tmpl"); + if (status != HTTP_CREATED) + { + cgiSetVariable("MESSAGE", + cgiText(_("Unable to upload cupsd.conf file:"))); + cgiSetVariable("ERROR", httpStatus(status)); + + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiCopyTemplateLang("error.tmpl"); + } + else + { + cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); + + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiCopyTemplateLang("restart.tmpl"); } + cgiEndHTML(); + + unlink(tempfile); + } + else + { + struct stat info; /* cupsd.conf information */ + cups_file_t *cupsd; /* cupsd.conf file */ + char *buffer; /* Buffer for entire file */ + char filename[1024]; /* Filename */ + const char *server_root; /* Location of config files */ + + /* - * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the - * following attributes: - * - * attributes-charset - * attributes-natural-language - * printer-uri + * Locate the cupsd.conf file... */ - request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); + if ((server_root = getenv("CUPS_SERVERROOT")) == NULL) + server_root = CUPS_SERVERROOT; - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, uri); + snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root); /* - * Do the request and get back a response... + * Figure out the size... */ - if ((response = cupsDoRequest(http, request, "/")) != NULL) + if (stat(filename, &info)) { - if ((attr = ippFindAttribute(response, "job-sheets-supported", - IPP_TAG_ZERO)) != NULL) - { - /* - * Add the job sheets options... - */ + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiSetVariable("MESSAGE", + cgiText(_("Unable to access cupsd.conf file:"))); + cgiSetVariable("ERROR", strerror(errno)); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); - cgiSetVariable("GROUP", cgiText(_("Banners"))); - cgiCopyTemplateLang("option-header.tmpl"); + perror(filename); + return; + } - cgiSetSize("CHOICES", attr->num_values); - cgiSetSize("TEXT", attr->num_values); - for (k = 0; k < attr->num_values; k ++) - { - cgiSetArray("CHOICES", k, attr->values[k].string.text); - cgiSetArray("TEXT", k, attr->values[k].string.text); - } + if (info.st_size > (1024 * 1024)) + { + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiSetVariable("MESSAGE", + cgiText(_("Unable to access cupsd.conf file:"))); + cgiSetVariable("ERROR", + cgiText(_("Unable to edit cupsd.conf files larger than " + "1MB!"))); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); - attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO); + fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename, + (long)info.st_size); + return; + } - cgiSetVariable("KEYWORD", "job_sheets_start"); - cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner"))); - cgiSetVariable("DEFCHOICE", attr == NULL ? - "" : attr->values[0].string.text); + /* + * Open the cupsd.conf file... + */ - cgiCopyTemplateLang("option-pickone.tmpl"); + if ((cupsd = cupsFileOpen(filename, "r")) == NULL) + { + /* + * Unable to open - log an error... + */ - cgiSetVariable("KEYWORD", "job_sheets_end"); - cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner"))); - cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ? - "" : attr->values[1].string.text); + cgiStartHTML(cgiText(_("Edit Configuration File"))); + cgiSetVariable("MESSAGE", + cgiText(_("Unable to access cupsd.conf file:"))); + cgiSetVariable("ERROR", strerror(errno)); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); - cgiCopyTemplateLang("option-pickone.tmpl"); + perror(filename); + return; + } - cgiCopyTemplateLang("option-trailer.tmpl"); - } + /* + * Allocate memory and load the file into a string buffer... + */ - if (ippFindAttribute(response, "printer-error-policy-supported", - IPP_TAG_ZERO) || - ippFindAttribute(response, "printer-op-policy-supported", - IPP_TAG_ZERO)) - { - /* - * Add the error and operation policy options... - */ + buffer = calloc(1, info.st_size + 1); - cgiSetVariable("GROUP", cgiText(_("Policies"))); - cgiCopyTemplateLang("option-header.tmpl"); + cupsFileRead(cupsd, buffer, info.st_size); + cupsFileClose(cupsd); - /* - * Error policy... - */ + cgiSetVariable("CUPSDCONF", buffer); + free(buffer); - attr = ippFindAttribute(response, "printer-error-policy-supported", - IPP_TAG_ZERO); + /* + * Show the current config file... + */ - if (attr) - { - cgiSetSize("CHOICES", attr->num_values); - cgiSetSize("TEXT", attr->num_values); - for (k = 0; k < attr->num_values; k ++) - { - cgiSetArray("CHOICES", k, attr->values[k].string.text); - cgiSetArray("TEXT", k, attr->values[k].string.text); - } + cgiStartHTML(cgiText(_("Edit Configuration File"))); - attr = ippFindAttribute(response, "printer-error-policy", - IPP_TAG_ZERO); + printf("\n", filename); - cgiSetVariable("KEYWORD", "printer_error_policy"); - cgiSetVariable("KEYTEXT", cgiText(_("Error Policy"))); - cgiSetVariable("DEFCHOICE", attr == NULL ? - "" : attr->values[0].string.text); - } + cgiCopyTemplateLang("edit-config.tmpl"); - cgiCopyTemplateLang("option-pickone.tmpl"); + cgiEndHTML(); + } +} - /* - * Operation policy... - */ - attr = ippFindAttribute(response, "printer-op-policy-supported", - IPP_TAG_ZERO); +/* + * 'do_delete_class()' - Delete a class... + */ - if (attr) - { - cgiSetSize("CHOICES", attr->num_values); - cgiSetSize("TEXT", attr->num_values); - for (k = 0; k < attr->num_values; k ++) - { - cgiSetArray("CHOICES", k, attr->values[k].string.text); - cgiSetArray("TEXT", k, attr->values[k].string.text); - } +static void +do_delete_class(http_t *http) /* I - HTTP connection */ +{ + ipp_t *request; /* IPP request */ + char uri[HTTP_MAX_URI]; /* Job URI */ + const char *pclass; /* Printer class name */ - attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO); - cgiSetVariable("KEYWORD", "printer_op_policy"); - cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy"))); - cgiSetVariable("DEFCHOICE", attr == NULL ? - "" : attr->values[0].string.text); + /* + * Get form variables... + */ - cgiCopyTemplateLang("option-pickone.tmpl"); - } + if (cgiGetVariable("CONFIRM") == NULL) + { + cgiStartHTML(cgiText(_("Delete Class"))); + cgiCopyTemplateLang("class-confirm.tmpl"); + cgiEndHTML(); + return; + } - cgiCopyTemplateLang("option-trailer.tmpl"); - } + if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL) + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, "/classes/%s", pclass); + else + { + cgiStartHTML(cgiText(_("Delete Class"))); + cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + return; + } - ippDelete(response); - } + /* + * Build a CUPS_DELETE_CLASS request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + request = ippNewRequest(CUPS_DELETE_CLASS); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, uri); + + /* + * Do the request and get back a response... + */ + + ippDelete(cupsDoRequest(http, request, "/admin/")); + /* + * Show the results... + */ + + if (cupsLastError() <= IPP_OK_CONFLICT) + { /* - * Binary protocol support... + * Redirect successful updates back to the classes page... */ - if (ppd->protocols && strstr(ppd->protocols, "BCP")) - { - protocol = ppdFindAttr(ppd, "cupsProtocol", NULL); + cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes"); + } - cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol"))); - cgiCopyTemplateLang("option-header.tmpl"); + cgiStartHTML(cgiText(_("Delete Class"))); - cgiSetSize("CHOICES", 2); - cgiSetSize("TEXT", 2); - cgiSetArray("CHOICES", 0, "None"); - cgiSetArray("TEXT", 0, cgiText(_("None"))); + if (cupsLastError() > IPP_OK_CONFLICT) + cgiShowIPPError(_("Unable to delete class:")); + else + cgiCopyTemplateLang("class-deleted.tmpl"); - if (strstr(ppd->protocols, "TBCP")) - { - cgiSetArray("CHOICES", 1, "TBCP"); - cgiSetArray("TEXT", 1, "TBCP"); - } - else - { - cgiSetArray("CHOICES", 1, "BCP"); - cgiSetArray("TEXT", 1, "BCP"); - } + cgiEndHTML(); +} - cgiSetVariable("KEYWORD", "protocol"); - cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol"))); - cgiSetVariable("DEFCHOICE", protocol ? protocol->value : "None"); - cgiCopyTemplateLang("option-pickone.tmpl"); +/* + * 'do_delete_printer()' - Delete a printer... + */ - cgiCopyTemplateLang("option-trailer.tmpl"); - } +static void +do_delete_printer(http_t *http) /* I - HTTP connection */ +{ + ipp_t *request; /* IPP request */ + char uri[HTTP_MAX_URI]; /* Job URI */ + const char *printer; /* Printer printer name */ - cgiCopyTemplateLang("set-printer-options-trailer.tmpl"); + + /* + * Get form variables... + */ + + if (cgiGetVariable("CONFIRM") == NULL) + { + cgiStartHTML(cgiText(_("Delete Printer"))); + cgiCopyTemplateLang("printer-confirm.tmpl"); cgiEndHTML(); + return; } + + if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, "/printers/%s", printer); else + { + cgiStartHTML(cgiText(_("Delete Printer"))); + cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + return; + } + + /* + * Build a CUPS_DELETE_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + request = ippNewRequest(CUPS_DELETE_PRINTER); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, uri); + + /* + * Do the request and get back a response... + */ + + ippDelete(cupsDoRequest(http, request, "/admin/")); + + /* + * Show the results... + */ + + if (cupsLastError() <= IPP_OK_CONFLICT) { /* - * Set default options... + * Redirect successful updates back to the printers page... */ - fputs("DEBUG: Setting options...\n", stderr); + cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers"); + } - out = cupsTempFile2(tempfile, sizeof(tempfile)); - in = cupsFileOpen(filename, "r"); + cgiStartHTML(cgiText(_("Delete Printer"))); - if (!in || !out) - { - cgiSetVariable("ERROR", strerror(errno)); - cgiStartHTML(cgiText(_("Set Printer Options"))); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); + if (cupsLastError() > IPP_OK_CONFLICT) + cgiShowIPPError(_("Unable to delete printer:")); + else + cgiCopyTemplateLang("printer-deleted.tmpl"); - if (in) - cupsFileClose(in); + cgiEndHTML(); +} - if (out) - { - cupsFileClose(out); - unlink(tempfile); - } - unlink(filename); - return; - } +/* + * 'do_export()' - Export printers to Samba... + */ - while (cupsFileGets(in, line, sizeof(line))) - { - if (!strncmp(line, "*cupsProtocol:", 14) && cgiGetVariable("protocol")) - continue; - else if (strncmp(line, "*Default", 8)) - cupsFilePrintf(out, "%s\n", line); - else - { - /* - * Get default option name... - */ +static void +do_export(http_t *http) /* I - HTTP connection */ +{ + int i, j; /* Looping vars */ + ipp_t *request, /* IPP request */ + *response; /* IPP response */ + const char *username, /* Samba username */ + *password, /* Samba password */ + *export_all; /* Export all printers? */ + int export_count, /* Number of printers to export */ + printer_count; /* Number of available printers */ + const char *name, /* What name to pull */ + *dest; /* Current destination */ + char ppd[1024]; /* PPD file */ - strlcpy(keyword, line + 8, sizeof(keyword)); - for (keyptr = keyword; *keyptr; keyptr ++) - if (*keyptr == ':' || isspace(*keyptr & 255)) - break; + /* + * Get form data... + */ - *keyptr = '\0'; + username = cgiGetVariable("USERNAME"); + password = cgiGetVariable("PASSWORD"); + export_all = cgiGetVariable("EXPORT_ALL"); + export_count = cgiGetSize("EXPORT_NAME"); - if (!strcmp(keyword, "PageRegion") || - !strcmp(keyword, "PaperDimension") || - !strcmp(keyword, "ImageableArea")) - var = cgiGetVariable("PageSize"); - else - var = cgiGetVariable(keyword); + /* + * Get list of available printers... + */ - if (var != NULL) - cupsFilePrintf(out, "*Default%s: %s\n", keyword, var); - else - cupsFilePrintf(out, "%s\n", line); - } - } + cgiSetSize("PRINTER_NAME", 0); + cgiSetSize("PRINTER_EXPORT", 0); - if ((var = cgiGetVariable("protocol")) != NULL) - cupsFilePrintf(out, "*cupsProtocol: %s\n", cgiGetVariable("protocol")); + request = ippNewRequest(CUPS_GET_PRINTERS); - cupsFileClose(in); - cupsFileClose(out); + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, + "printer-type", 0); - /* - * Build a CUPS_ADD_PRINTER request, which requires the following - * attributes: - * - * attributes-charset - * attributes-natural-language - * printer-uri - * job-sheets-default - * [ppd file] - */ + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, + "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE | + CUPS_PRINTER_IMPLICIT); - request = ippNewRequest(CUPS_ADD_PRINTER); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", NULL, "printer-name"); - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", - cgiGetVariable("PRINTER_NAME")); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, uri); + if ((response = cupsDoRequest(http, request, "/")) != NULL) + { + cgiSetIPPVars(response, NULL, NULL, NULL, 0); + ippDelete(response); - attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, - "job-sheets-default", 2, NULL, NULL); - attr->values[0].string.text = strdup(cgiGetVariable("job_sheets_start")); - attr->values[1].string.text = strdup(cgiGetVariable("job_sheets_end")); + if (!export_all) + { + printer_count = cgiGetSize("PRINTER_NAME"); - if ((var = cgiGetVariable("printer_error_policy")) != NULL) - attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, - "printer-error-policy", NULL, var); + for (i = 0; i < printer_count; i ++) + { + dest = cgiGetArray("PRINTER_NAME", i); - if ((var = cgiGetVariable("printer_op_policy")) != NULL) - attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, - "printer-op-policy", NULL, var); + for (j = 0; j < export_count; j ++) + if (!strcasecmp(dest, cgiGetArray("EXPORT_NAME", j))) + break; + + cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : ""); + } + } + } + + /* + * Export or get the printers to export... + */ + if (username && *username && password && *password && + (export_all || export_count > 0)) + { /* - * Do the request and get back a response... + * Do export... */ - ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile)); + fputs("DEBUG: Export printers...\n", stderr); - if (cupsLastError() > IPP_OK_CONFLICT) + if (export_all) { - cgiStartHTML(title); - cgiShowIPPError(_("Unable to set options:")); + name = "PRINTER_NAME"; + export_count = cgiGetSize("PRINTER_NAME"); } else - { - /* - * Redirect successful updates back to the printer page... - */ + name = "EXPORT_NAME"; - char refresh[1024]; /* Refresh URL */ + for (i = 0; i < export_count; i ++) + { + dest = cgiGetArray(name, i); + if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd))) + break; - cgiFormEncode(uri, printer, sizeof(uri)); - snprintf(refresh, sizeof(refresh), - "5;URL=/admin/?OP=redirect&URL=/printers/%s", uri); - cgiSetVariable("refresh_page", refresh); + j = cupsAdminExportSamba(dest, ppd, "localhost", username, password, + stderr); - cgiStartHTML(title); + unlink(ppd); - cgiCopyTemplateLang("printer-configured.tmpl"); + if (!j) + break; } - cgiEndHTML(); - - unlink(tempfile); + if (i < export_count) + cgiSetVariable("ERROR", cupsLastErrorString()); + else + { + cgiStartHTML(cgiText(_("Export Printers to Samba"))); + cgiCopyTemplateLang("samba-exported.tmpl"); + cgiEndHTML(); + return; + } } + else if (username && !*username) + cgiSetVariable("ERROR", + cgiText(_("A Samba username is required to export " + "printer drivers!"))); + else if (username && (!password || !*password)) + cgiSetVariable("ERROR", + cgiText(_("A Samba password is required to export " + "printer drivers!"))); + + /* + * Show form... + */ - unlink(filename); + cgiStartHTML(cgiText(_("Export Printers to Samba"))); + cgiCopyTemplateLang("samba-export.tmpl"); + cgiEndHTML(); } /* - * 'do_config_server()' - Configure server settings. + * 'do_list_printers()' - List available printers... */ static void -do_config_server(http_t *http) /* I - HTTP connection */ +do_list_printers(http_t *http) /* I - HTTP connection */ { - if (cgiIsPOST() && !cgiGetVariable("CUPSDCONF")) - { - /* - * Save basic setting changes... - */ + ipp_t *request, /* IPP request */ + *response; /* IPP response */ + ipp_attribute_t *attr; /* IPP attribute */ - int num_settings; /* Number of server settings */ - cups_option_t *settings; /* Server settings */ - const char *debug_logging, /* DEBUG_LOGGING value */ - *remote_admin, /* REMOTE_ADMIN value */ - *remote_any, /* REMOTE_ANY value */ - *remote_printers, - /* REMOTE_PRINTERS value */ - *share_printers,/* SHARE_PRINTERS value */ -#ifdef HAVE_GSSAPI - *default_auth_type, - /* DefaultAuthType value */ -#endif /* HAVE_GSSAPI */ - *user_cancel_any; - /* USER_CANCEL_ANY value */ + cgiStartHTML(cgiText(_("List Available Printers"))); + fflush(stdout); + + /* + * Get the list of printers and their devices... + */ + + request = ippNewRequest(CUPS_GET_PRINTERS); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", NULL, "device-uri"); + + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", + CUPS_PRINTER_LOCAL); + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", + CUPS_PRINTER_LOCAL); + if ((response = cupsDoRequest(http, request, "/")) != NULL) + { /* - * Get the checkbox values from the form... + * Got the printer list, now load the devices... */ - debug_logging = cgiGetVariable("DEBUG_LOGGING") ? "1" : "0"; - remote_admin = cgiGetVariable("REMOTE_ADMIN") ? "1" : "0"; - remote_any = cgiGetVariable("REMOTE_ANY") ? "1" : "0"; - remote_printers = cgiGetVariable("REMOTE_PRINTERS") ? "1" : "0"; - share_printers = cgiGetVariable("SHARE_PRINTERS") ? "1" : "0"; - user_cancel_any = cgiGetVariable("USER_CANCEL_ANY") ? "1" : "0"; -#ifdef HAVE_GSSAPI - default_auth_type = cgiGetVariable("KERBEROS") ? "Negotiate" : "Basic"; + int i; /* Looping var */ + cups_array_t *printer_devices; /* Printer devices for local printers */ + char *printer_device; /* Current printer device */ - fprintf(stderr, "DEBUG: DefaultAuthType %s\n", default_auth_type); -#endif /* HAVE_GSSAPI */ /* - * Get the current server settings... + * Allocate an array and copy the device strings... */ - if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) + printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL); + + for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI); + attr; + attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI)) { - cgiStartHTML(cgiText(_("Change Settings"))); - cgiSetVariable("MESSAGE", - cgiText(_("Unable to change server settings:"))); - cgiSetVariable("ERROR", cupsLastErrorString()); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - return; + cupsArrayAdd(printer_devices, strdup(attr->values[0].string.text)); } /* - * See if the settings have changed... + * Free the printer list and get the device list... */ - if (strcmp(debug_logging, cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, - num_settings, settings)) || - strcmp(remote_admin, cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, - num_settings, settings)) || - strcmp(remote_any, cupsGetOption(CUPS_SERVER_REMOTE_ANY, - num_settings, settings)) || - strcmp(remote_printers, cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, - num_settings, settings)) || - strcmp(share_printers, cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, - num_settings, settings)) || -#ifdef HAVE_GSSAPI - !cupsGetOption("DefaultAuthType", num_settings, settings) || - strcmp(default_auth_type, cupsGetOption("DefaultAuthType", - num_settings, settings)) || -#endif /* HAVE_GSSAPI */ - strcmp(user_cancel_any, cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, - num_settings, settings))) - { - /* - * Settings *have* changed, so save the changes... - */ - - cupsFreeOptions(num_settings, settings); + ippDelete(response); - num_settings = 0; - num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING, - debug_logging, num_settings, &settings); - num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN, - remote_admin, num_settings, &settings); - num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY, - remote_any, num_settings, &settings); - num_settings = cupsAddOption(CUPS_SERVER_REMOTE_PRINTERS, - remote_printers, num_settings, &settings); - num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS, - share_printers, num_settings, &settings); - num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY, - user_cancel_any, num_settings, &settings); -#ifdef HAVE_GSSAPI - num_settings = cupsAddOption("DefaultAuthType", default_auth_type, - num_settings, &settings); -#endif /* HAVE_GSSAPI */ + request = ippNewRequest(CUPS_GET_DEVICES); - if (!cupsAdminSetServerSettings(http, num_settings, settings)) - { - cgiStartHTML(cgiText(_("Change Settings"))); - cgiSetVariable("MESSAGE", - cgiText(_("Unable to change server settings:"))); - cgiSetVariable("ERROR", cupsLastErrorString()); - cgiCopyTemplateLang("error.tmpl"); - } - else - { - cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); - cgiStartHTML(cgiText(_("Change Settings"))); - cgiCopyTemplateLang("restart.tmpl"); - } - } - else + if ((response = cupsDoRequest(http, request, "/")) != NULL) { /* - * No changes... + * Got the device list, let's parse it... */ - cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); - cgiStartHTML(cgiText(_("Change Settings"))); - cgiCopyTemplateLang("norestart.tmpl"); - } + const char *device_uri, /* device-uri attribute value */ + *device_make_and_model, /* device-make-and-model value */ + *device_info; /* device-info value */ - cupsFreeOptions(num_settings, settings); - cgiEndHTML(); - } - else if (cgiIsPOST()) - { - /* - * Save hand-edited config file... - */ + for (i = 0, attr = response->attrs; attr; attr = attr->next) + { + /* + * Skip leading attributes until we hit a device... + */ - http_status_t status; /* PUT status */ - char tempfile[1024]; /* Temporary new cupsd.conf */ - int tempfd; /* Temporary file descriptor */ - cups_file_t *temp; /* Temporary file */ - const char *start, /* Start of line */ - *end; /* End of line */ + while (attr && attr->group_tag != IPP_TAG_PRINTER) + attr = attr->next; + if (!attr) + break; - /* - * Create a temporary file for the new cupsd.conf file... - */ + /* + * Pull the needed attributes from this device... + */ - if ((tempfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) - { - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:"))); - cgiSetVariable("ERROR", strerror(errno)); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - - perror(tempfile); - return; - } + device_info = NULL; + device_make_and_model = NULL; + device_uri = NULL; - if ((temp = cupsFileOpenFd(tempfd, "w")) == NULL) - { - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiSetVariable("MESSAGE", cgiText(_("Unable to create temporary file:"))); - cgiSetVariable("ERROR", strerror(errno)); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - - perror(tempfile); - close(tempfd); - unlink(tempfile); - return; - } + while (attr && attr->group_tag == IPP_TAG_PRINTER) + { + if (!strcmp(attr->name, "device-info") && + attr->value_tag == IPP_TAG_TEXT) + device_info = attr->values[0].string.text; - /* - * Copy the cupsd.conf text from the form variable... - */ + if (!strcmp(attr->name, "device-make-and-model") && + attr->value_tag == IPP_TAG_TEXT) + device_make_and_model = attr->values[0].string.text; - start = cgiGetVariable("CUPSDCONF"); - while (start) - { - if ((end = strstr(start, "\r\n")) == NULL) - if ((end = strstr(start, "\n")) == NULL) - end = start + strlen(start); + if (!strcmp(attr->name, "device-uri") && + attr->value_tag == IPP_TAG_URI) + device_uri = attr->values[0].string.text; - cupsFileWrite(temp, start, end - start); - cupsFilePutChar(temp, '\n'); + attr = attr->next; + } - if (*end == '\r') - start = end + 2; - else if (*end == '\n') - start = end + 1; - else - start = NULL; - } + /* + * See if we have everything needed... + */ - cupsFileClose(temp); + if (device_info && device_make_and_model && device_uri && + strcasecmp(device_make_and_model, "unknown") && + strchr(device_uri, ':')) + { + /* + * Yes, now see if there is already a printer for this + * device... + */ - /* - * Upload the configuration file to the server... - */ + if (!cupsArrayFind(printer_devices, (void *)device_uri)) + { + /* + * Not found, so it must be a new printer... + */ - status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile); + char options[1024], /* Form variables for this device */ + *options_ptr; /* Pointer into string */ + const char *ptr; /* Pointer into device string */ - if (status != HTTP_CREATED) - { - cgiSetVariable("MESSAGE", - cgiText(_("Unable to upload cupsd.conf file:"))); - cgiSetVariable("ERROR", httpStatus(status)); - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiCopyTemplateLang("error.tmpl"); - } - else - { - cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect"); + /* + * Format the printer name variable for this device... + * + * We use the device-info string first, then device-uri, + * and finally device-make-and-model to come up with a + * suitable name. + */ - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiCopyTemplateLang("restart.tmpl"); - } + strcpy(options, "TEMPLATE_NAME="); + options_ptr = options + strlen(options); - cgiEndHTML(); + if (strncasecmp(device_info, "unknown", 7)) + ptr = device_info; + else if ((ptr = strstr(device_uri, "://")) != NULL) + ptr += 3; + else + ptr = device_make_and_model; - unlink(tempfile); - } - else - { - struct stat info; /* cupsd.conf information */ - cups_file_t *cupsd; /* cupsd.conf file */ - char *buffer; /* Buffer for entire file */ - char filename[1024]; /* Filename */ - const char *server_root; /* Location of config files */ + for (; + options_ptr < (options + sizeof(options) - 1) && *ptr; + ptr ++) + if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' || + *ptr == '.') + *options_ptr++ = *ptr; + else if ((*ptr == ' ' || *ptr == '/') && options_ptr[-1] != '_') + *options_ptr++ = '_'; + else if (*ptr == '?' || *ptr == '(') + break; + /* + * Then add the make and model in the printer info, so + * that MacOS clients see something reasonable... + */ - /* - * Locate the cupsd.conf file... - */ + strlcpy(options_ptr, "&PRINTER_LOCATION=Local+Printer" + "&PRINTER_INFO=", + sizeof(options) - (options_ptr - options)); + options_ptr += strlen(options_ptr); - if ((server_root = getenv("CUPS_SERVERROOT")) == NULL) - server_root = CUPS_SERVERROOT; + cgiFormEncode(options_ptr, device_make_and_model, + sizeof(options) - (options_ptr - options)); + options_ptr += strlen(options_ptr); - snprintf(filename, sizeof(filename), "%s/cupsd.conf", server_root); + /* + * Then copy the device URI... + */ - /* - * Figure out the size... - */ + strlcpy(options_ptr, "&DEVICE_URI=", + sizeof(options) - (options_ptr - options)); + options_ptr += strlen(options_ptr); - if (stat(filename, &info)) - { - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiSetVariable("MESSAGE", - cgiText(_("Unable to access cupsd.conf file:"))); - cgiSetVariable("ERROR", strerror(errno)); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); + cgiFormEncode(options_ptr, device_uri, + sizeof(options) - (options_ptr - options)); + options_ptr += strlen(options_ptr); - perror(filename); - return; - } + if (options_ptr < (options + sizeof(options) - 1)) + { + *options_ptr++ = '|'; + cgiFormEncode(options_ptr, device_make_and_model, + sizeof(options) - (options_ptr - options)); + } - if (info.st_size > (1024 * 1024)) - { - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiSetVariable("MESSAGE", - cgiText(_("Unable to access cupsd.conf file:"))); - cgiSetVariable("ERROR", - cgiText(_("Unable to edit cupsd.conf files larger than " - "1MB!"))); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); + /* + * Finally, set the form variables for this printer... + */ - fprintf(stderr, "ERROR: \"%s\" too large (%ld) to edit!\n", filename, - (long)info.st_size); - return; - } + cgiSetArray("device_info", i, device_info); + cgiSetArray("device_make_and_model", i, device_make_and_model); + cgiSetArray("device_options", i, options); + cgiSetArray("device_uri", i, device_uri); + i ++; + } + } - /* - * Open the cupsd.conf file... - */ + if (!attr) + break; + } + + ippDelete(response); - if ((cupsd = cupsFileOpen(filename, "r")) == NULL) - { /* - * Unable to open - log an error... + * Free the device list... */ - cgiStartHTML(cgiText(_("Edit Configuration File"))); - cgiSetVariable("MESSAGE", - cgiText(_("Unable to access cupsd.conf file:"))); - cgiSetVariable("ERROR", strerror(errno)); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); + for (printer_device = (char *)cupsArrayFirst(printer_devices); + printer_device; + printer_device = (char *)cupsArrayNext(printer_devices)) + free(printer_device); - perror(filename); - return; + cupsArrayDelete(printer_devices); } + } - /* - * Allocate memory and load the file into a string buffer... - */ - - buffer = calloc(1, info.st_size + 1); - - cupsFileRead(cupsd, buffer, info.st_size); - cupsFileClose(cupsd); - - cgiSetVariable("CUPSDCONF", buffer); - free(buffer); - - /* - * Show the current config file... - */ - - cgiStartHTML(cgiText(_("Edit Configuration File"))); - - printf("\n", filename); + /* + * Finally, show the printer list... + */ - cgiCopyTemplateLang("edit-config.tmpl"); + cgiCopyTemplateLang("list-available-printers.tmpl"); - cgiEndHTML(); - } + cgiEndHTML(); } /* - * 'do_delete_class()' - Delete a class... + * 'do_menu()' - Show the main menu... */ static void -do_delete_class(http_t *http) /* I - HTTP connection */ +do_menu(http_t *http) /* I - HTTP connection */ { - ipp_t *request; /* IPP request */ - char uri[HTTP_MAX_URI]; /* Job URI */ - const char *pclass; /* Printer class name */ + int num_settings; /* Number of server settings */ + cups_option_t *settings; /* Server settings */ + const char *val; /* Setting value */ + char filename[1024]; /* Temporary filename */ + const char *datadir; /* Location of data files */ + ipp_t *request, /* IPP request */ + *response; /* IPP response */ /* - * Get form variables... + * Get the current server settings... */ - if (cgiGetVariable("CONFIRM") == NULL) + if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) { - cgiStartHTML(cgiText(_("Delete Class"))); - cgiCopyTemplateLang("class-confirm.tmpl"); - cgiEndHTML(); - return; + cgiSetVariable("SETTINGS_MESSAGE", + cgiText(_("Unable to open cupsd.conf file:"))); + cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString()); } - if ((pclass = cgiGetVariable("PRINTER_NAME")) != NULL) - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/classes/%s", pclass); - else - { - cgiStartHTML(cgiText(_("Delete Class"))); - cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - return; - } + if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings, + settings)) != NULL && atoi(val)) + cgiSetVariable("DEBUG_LOGGING", "CHECKED"); - /* - * Build a CUPS_DELETE_CLASS request, which requires the following - * attributes: - * - * attributes-charset - * attributes-natural-language - * printer-uri - */ + if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings, + settings)) != NULL && atoi(val)) + cgiSetVariable("REMOTE_ADMIN", "CHECKED"); - request = ippNewRequest(CUPS_DELETE_CLASS); + if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings, + settings)) != NULL && atoi(val)) + cgiSetVariable("REMOTE_ANY", "CHECKED"); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, uri); + if ((val = cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, num_settings, + settings)) != NULL && atoi(val)) + cgiSetVariable("REMOTE_PRINTERS", "CHECKED"); - /* - * Do the request and get back a response... - */ + if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings, + settings)) != NULL && atoi(val)) + cgiSetVariable("SHARE_PRINTERS", "CHECKED"); - ippDelete(cupsDoRequest(http, request, "/admin/")); + if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings, + settings)) != NULL && atoi(val)) + cgiSetVariable("USER_CANCEL_ANY", "CHECKED"); + +#ifdef HAVE_GSSAPI + cgiSetVariable("HAVE_GSSAPI", "1"); + + if ((val = cupsGetOption("DefaultAuthType", num_settings, + settings)) != NULL && !strcasecmp(val, "Negotiate")) + cgiSetVariable("KERBEROS", "CHECKED"); +#endif /* HAVE_GSSAPI */ + + cupsFreeOptions(num_settings, settings); /* - * Show the results... + * See if Samba and the Windows drivers are installed... */ - if (cupsLastError() <= IPP_OK_CONFLICT) + if ((datadir = getenv("CUPS_DATADIR")) == NULL) + datadir = CUPS_DATADIR; + + snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir); + if (!access(filename, R_OK)) { /* - * Redirect successful updates back to the classes page... + * Found Windows 2000 driver file, see if we have smbclient and + * rpcclient... */ - cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/classes"); + if (cupsFileFind("smbclient", getenv("PATH"), 1, filename, + sizeof(filename)) && + cupsFileFind("rpcclient", getenv("PATH"), 1, filename, + sizeof(filename))) + cgiSetVariable("HAVE_SAMBA", "Y"); + else + { + if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename, + sizeof(filename))) + fputs("ERROR: smbclient not found!\n", stderr); + + if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename, + sizeof(filename))) + fputs("ERROR: rpcclient not found!\n", stderr); + } } + else + perror(filename); - cgiStartHTML(cgiText(_("Delete Class"))); + /* + * Subscriptions... + */ - if (cupsLastError() > IPP_OK_CONFLICT) - cgiShowIPPError(_("Unable to delete class:")); - else - cgiCopyTemplateLang("class-deleted.tmpl"); + request = ippNewRequest(IPP_GET_SUBSCRIPTIONS); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, "ipp://localhost/"); + + if ((response = cupsDoRequest(http, request, "/")) != NULL) + { + cgiSetIPPVars(response, NULL, NULL, NULL, 0); + ippDelete(response); + } + + /* + * Finally, show the main menu template... + */ + + cgiStartHTML(cgiText(_("Administration"))); + + cgiCopyTemplateLang("admin.tmpl"); cgiEndHTML(); } /* - * 'do_delete_printer()' - Delete a printer... + * 'do_printer_op()' - Do a printer operation. */ static void -do_delete_printer(http_t *http) /* I - HTTP connection */ +do_printer_op(http_t *http, /* I - HTTP connection */ + ipp_op_t op, /* I - Operation to perform */ + const char *title) /* I - Title of page */ { ipp_t *request; /* IPP request */ - char uri[HTTP_MAX_URI]; /* Job URI */ - const char *printer; /* Printer printer name */ - + char uri[HTTP_MAX_URI]; /* Printer URI */ + const char *printer, /* Printer name (purge-jobs) */ + *is_class; /* Is a class? */ - /* - * Get form variables... - */ - if (cgiGetVariable("CONFIRM") == NULL) - { - cgiStartHTML(cgiText(_("Delete Printer"))); - cgiCopyTemplateLang("printer-confirm.tmpl"); - cgiEndHTML(); - return; - } + is_class = cgiGetVariable("IS_CLASS"); + printer = cgiGetVariable("PRINTER_NAME"); - if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, "/printers/%s", printer); - else + if (!printer) { - cgiStartHTML(cgiText(_("Delete Printer"))); cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); + cgiStartHTML(title); cgiCopyTemplateLang("error.tmpl"); cgiEndHTML(); return; } /* - * Build a CUPS_DELETE_PRINTER request, which requires the following + * Build a printer request, which requires the following * attributes: * * attributes-charset @@ -2268,8 +2320,11 @@ do_delete_printer(http_t *http) /* I - HTTP connection */ * printer-uri */ - request = ippNewRequest(CUPS_DELETE_PRINTER); + request = ippNewRequest(op); + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", + printer); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); @@ -2279,812 +2334,812 @@ do_delete_printer(http_t *http) /* I - HTTP connection */ ippDelete(cupsDoRequest(http, request, "/admin/")); - /* - * Show the results... - */ - - if (cupsLastError() <= IPP_OK_CONFLICT) + if (cupsLastError() > IPP_OK_CONFLICT) + { + cgiStartHTML(title); + cgiShowIPPError(_("Unable to change printer:")); + } + else { /* - * Redirect successful updates back to the printers page... + * Redirect successful updates back to the printer page... */ - cgiSetVariable("refresh_page", "5;URL=/admin/?OP=redirect&URL=/printers"); - } + char url[1024], /* Printer/class URL */ + refresh[1024]; /* Refresh URL */ - cgiStartHTML(cgiText(_("Delete Printer"))); - if (cupsLastError() > IPP_OK_CONFLICT) - cgiShowIPPError(_("Unable to delete printer:")); - else - cgiCopyTemplateLang("printer-deleted.tmpl"); + cgiRewriteURL(uri, url, sizeof(url), NULL); + cgiFormEncode(uri, url, sizeof(uri)); + snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri); + cgiSetVariable("refresh_page", refresh); + + cgiStartHTML(title); + + if (op == IPP_PAUSE_PRINTER) + cgiCopyTemplateLang("printer-stop.tmpl"); + else if (op == IPP_RESUME_PRINTER) + cgiCopyTemplateLang("printer-start.tmpl"); + else if (op == CUPS_ACCEPT_JOBS) + cgiCopyTemplateLang("printer-accept.tmpl"); + else if (op == CUPS_REJECT_JOBS) + cgiCopyTemplateLang("printer-reject.tmpl"); + else if (op == IPP_PURGE_JOBS) + cgiCopyTemplateLang("printer-purge.tmpl"); + else if (op == CUPS_SET_DEFAULT) + cgiCopyTemplateLang("printer-default.tmpl"); + } cgiEndHTML(); } /* - * 'do_export()' - Export printers to Samba... + * 'do_set_allowed_users()' - Set the allowed/denied users for a queue. */ static void -do_export(http_t *http) /* I - HTTP connection */ +do_set_allowed_users(http_t *http) /* I - HTTP connection */ { - int i, j; /* Looping vars */ + int i; /* Looping var */ ipp_t *request, /* IPP request */ *response; /* IPP response */ - const char *username, /* Samba username */ - *password, /* Samba password */ - *export_all; /* Export all printers? */ - int export_count, /* Number of printers to export */ - printer_count; /* Number of available printers */ - const char *name, /* What name to pull */ - *dest; /* Current destination */ - char ppd[1024]; /* PPD file */ - + char uri[HTTP_MAX_URI]; /* Printer URI */ + const char *printer, /* Printer name (purge-jobs) */ + *is_class, /* Is a class? */ + *users, /* List of users or groups */ + *type; /* Allow/deny type */ + int num_users; /* Number of users */ + char *ptr, /* Pointer into users string */ + *end, /* Pointer to end of users string */ + quote; /* Quote character */ + ipp_attribute_t *attr; /* Attribute */ + static const char * const attrs[] = /* Requested attributes */ + { + "requesting-user-name-allowed", + "requesting-user-name-denied" + }; - /* - * Get form data... - */ - username = cgiGetVariable("USERNAME"); - password = cgiGetVariable("PASSWORD"); - export_all = cgiGetVariable("EXPORT_ALL"); - export_count = cgiGetSize("EXPORT_NAME"); + is_class = cgiGetVariable("IS_CLASS"); + printer = cgiGetVariable("PRINTER_NAME"); - /* - * Get list of available printers... - */ + if (!printer) + { + cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); + cgiStartHTML(cgiText(_("Set Allowed Users"))); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + return; + } - cgiSetSize("PRINTER_NAME", 0); - cgiSetSize("PRINTER_EXPORT", 0); + users = cgiGetVariable("users"); + type = cgiGetVariable("type"); - request = ippNewRequest(CUPS_GET_PRINTERS); + if (!users || !type || + (strcmp(type, "requesting-user-name-allowed") && + strcmp(type, "requesting-user-name-denied"))) + { + /* + * Build a Get-Printer-Attributes request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requested-attributes + */ - ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, - "printer-type", 0); + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); - ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, - "printer-type-mask", CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE | - CUPS_PRINTER_IMPLICIT); + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", + printer); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, uri); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", NULL, "printer-name"); + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs); - if ((response = cupsDoRequest(http, request, "/")) != NULL) - { - cgiSetIPPVars(response, NULL, NULL, NULL, 0); - ippDelete(response); + /* + * Do the request and get back a response... + */ - if (!export_all) + if ((response = cupsDoRequest(http, request, "/")) != NULL) { - printer_count = cgiGetSize("PRINTER_NAME"); - - for (i = 0; i < printer_count; i ++) - { - dest = cgiGetArray("PRINTER_NAME", i); - - for (j = 0; j < export_count; j ++) - if (!strcasecmp(dest, cgiGetArray("EXPORT_NAME", j))) - break; + cgiSetIPPVars(response, NULL, NULL, NULL, 0); - cgiSetArray("PRINTER_EXPORT", i, j < export_count ? "Y" : ""); - } + ippDelete(response); } - } - /* - * Export or get the printers to export... - */ + cgiStartHTML(cgiText(_("Set Allowed Users"))); - if (username && *username && password && *password && - (export_all || export_count > 0)) + if (cupsLastError() > IPP_OK_CONFLICT) + cgiShowIPPError(_("Unable to get printer attributes:")); + else + cgiCopyTemplateLang("users.tmpl"); + + cgiEndHTML(); + } + else { /* - * Do export... + * Save the changes... */ - fputs("DEBUG: Export printers...\n", stderr); - - if (export_all) + for (num_users = 0, ptr = (char *)users; *ptr; num_users ++) { - name = "PRINTER_NAME"; - export_count = cgiGetSize("PRINTER_NAME"); - } - else - name = "EXPORT_NAME"; - - for (i = 0; i < export_count; i ++) - { - dest = cgiGetArray(name, i); - - if (!cupsAdminCreateWindowsPPD(http, dest, ppd, sizeof(ppd))) - break; - - j = cupsAdminExportSamba(dest, ppd, "localhost", username, password, - stderr); - - unlink(ppd); - - if (!j) - break; - } - - if (i < export_count) - cgiSetVariable("ERROR", cupsLastErrorString()); - else - { - cgiStartHTML(cgiText(_("Export Printers to Samba"))); - cgiCopyTemplateLang("samba-exported.tmpl"); - cgiEndHTML(); - return; - } - } - else if (username && !*username) - cgiSetVariable("ERROR", - cgiText(_("A Samba username is required to export " - "printer drivers!"))); - else if (username && (!password || !*password)) - cgiSetVariable("ERROR", - cgiText(_("A Samba password is required to export " - "printer drivers!"))); - - /* - * Show form... - */ + /* + * Skip whitespace and commas... + */ - cgiStartHTML(cgiText(_("Export Printers to Samba"))); - cgiCopyTemplateLang("samba-export.tmpl"); - cgiEndHTML(); -} + while (*ptr == ',' || isspace(*ptr & 255)) + ptr ++; + if (*ptr == '\'' || *ptr == '\"') + { + /* + * Scan quoted name... + */ -/* - * 'do_menu()' - Show the main menu... - */ + quote = *ptr++; -static void -do_menu(http_t *http) /* I - HTTP connection */ -{ - int num_settings; /* Number of server settings */ - cups_option_t *settings; /* Server settings */ - const char *val; /* Setting value */ - char filename[1024]; /* Temporary filename */ - const char *datadir; /* Location of data files */ - ipp_t *request, /* IPP request */ - *response; /* IPP response */ - ipp_attribute_t *attr; /* IPP attribute */ + for (end = ptr; *end; end ++) + if (*end == quote) + break; + } + else + { + /* + * Scan space or comma-delimited name... + */ + for (end = ptr; *end; end ++) + if (isspace(*end & 255) || *end == ',') + break; + } - /* - * Get the current server settings... - */ + /* + * Advance to the next name... + */ - if (!cupsAdminGetServerSettings(http, &num_settings, &settings)) - { - cgiSetVariable("SETTINGS_MESSAGE", - cgiText(_("Unable to open cupsd.conf file:"))); - cgiSetVariable("SETTINGS_ERROR", cupsLastErrorString()); - } + ptr = end; + } - if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings, - settings)) != NULL && atoi(val)) - cgiSetVariable("DEBUG_LOGGING", "CHECKED"); + /* + * Build a CUPS-Add-Printer/Class request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requesting-user-name-{allowed,denied} + */ - if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings, - settings)) != NULL && atoi(val)) - cgiSetVariable("REMOTE_ADMIN", "CHECKED"); + request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER); - if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings, - settings)) != NULL && atoi(val)) - cgiSetVariable("REMOTE_ANY", "CHECKED"); + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", + printer); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, uri); - if ((val = cupsGetOption(CUPS_SERVER_REMOTE_PRINTERS, num_settings, - settings)) != NULL && atoi(val)) - cgiSetVariable("REMOTE_PRINTERS", "CHECKED"); + if (num_users == 0) + ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, + "requesting-user-name-allowed", NULL, "all"); + else + { + attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, + type, num_users, NULL, NULL); - if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings, - settings)) != NULL && atoi(val)) - cgiSetVariable("SHARE_PRINTERS", "CHECKED"); + for (i = 0, ptr = (char *)users; *ptr; i ++) + { + /* + * Skip whitespace and commas... + */ - if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings, - settings)) != NULL && atoi(val)) - cgiSetVariable("USER_CANCEL_ANY", "CHECKED"); + while (*ptr == ',' || isspace(*ptr & 255)) + ptr ++; -#ifdef HAVE_GSSAPI - cgiSetVariable("HAVE_GSSAPI", "1"); + if (*ptr == '\'' || *ptr == '\"') + { + /* + * Scan quoted name... + */ - if ((val = cupsGetOption("DefaultAuthType", num_settings, - settings)) != NULL && !strcasecmp(val, "Negotiate")) - cgiSetVariable("KERBEROS", "CHECKED"); -#endif /* HAVE_GSSAPI */ + quote = *ptr++; - cupsFreeOptions(num_settings, settings); + for (end = ptr; *end; end ++) + if (*end == quote) + break; + } + else + { + /* + * Scan space or comma-delimited name... + */ - /* - * Get the list of printers and their devices... - */ + for (end = ptr; *end; end ++) + if (isspace(*end & 255) || *end == ',') + break; + } - request = ippNewRequest(CUPS_GET_PRINTERS); + /* + * Terminate the name... + */ - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", NULL, "device-uri"); + if (*end) + *end++ = '\0'; - ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type", - CUPS_PRINTER_LOCAL); - ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, "printer-type-mask", - CUPS_PRINTER_LOCAL); + /* + * Add the name... + */ - if ((response = cupsDoRequest(http, request, "/")) != NULL) - { - /* - * Got the printer list, now load the devices... - */ + attr->values[i].string.text = strdup(ptr); - int i; /* Looping var */ - cups_array_t *printer_devices; /* Printer devices for local printers */ - char *printer_device; /* Current printer device */ + /* + * Advance to the next name... + */ + ptr = end; + } + } /* - * Allocate an array and copy the device strings... + * Do the request and get back a response... */ - printer_devices = cupsArrayNew((cups_array_func_t)strcmp, NULL); + ippDelete(cupsDoRequest(http, request, "/admin/")); - for (attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI); - attr; - attr = ippFindNextAttribute(response, "device-uri", IPP_TAG_URI)) + if (cupsLastError() > IPP_OK_CONFLICT) { - cupsArrayAdd(printer_devices, strdup(attr->values[0].string.text)); + cgiStartHTML(cgiText(_("Set Allowed Users"))); + cgiShowIPPError(_("Unable to change printer:")); } - - /* - * Free the printer list and get the device list... - */ - - ippDelete(response); - - request = ippNewRequest(CUPS_GET_DEVICES); - - if ((response = cupsDoRequest(http, request, "/")) != NULL) + else { /* - * Got the device list, let's parse it... + * Redirect successful updates back to the printer page... */ - const char *device_uri, /* device-uri attribute value */ - *device_make_and_model, /* device-make-and-model value */ - *device_info; /* device-info value */ + char url[1024], /* Printer/class URL */ + refresh[1024]; /* Refresh URL */ - for (i = 0, attr = response->attrs; attr; attr = attr->next) - { - /* - * Skip leading attributes until we hit a device... - */ + cgiRewriteURL(uri, url, sizeof(url), NULL); + cgiFormEncode(uri, url, sizeof(uri)); + snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", + uri); + cgiSetVariable("refresh_page", refresh); - while (attr && attr->group_tag != IPP_TAG_PRINTER) - attr = attr->next; + cgiStartHTML(cgiText(_("Set Allowed Users"))); - if (!attr) - break; + cgiCopyTemplateLang(is_class ? "class-modified.tmpl" : + "printer-modified.tmpl"); + } - /* - * Pull the needed attributes from this device... - */ + cgiEndHTML(); + } +} - device_info = NULL; - device_make_and_model = NULL; - device_uri = NULL; - - while (attr && attr->group_tag == IPP_TAG_PRINTER) - { - if (!strcmp(attr->name, "device-info") && - attr->value_tag == IPP_TAG_TEXT) - device_info = attr->values[0].string.text; - if (!strcmp(attr->name, "device-make-and-model") && - attr->value_tag == IPP_TAG_TEXT) - device_make_and_model = attr->values[0].string.text; - - if (!strcmp(attr->name, "device-uri") && - attr->value_tag == IPP_TAG_URI) - device_uri = attr->values[0].string.text; - - attr = attr->next; - } - - /* - * See if we have everything needed... - */ - - if (device_info && device_make_and_model && device_uri && - strcasecmp(device_make_and_model, "unknown") && - strchr(device_uri, ':')) - { - /* - * Yes, now see if there is already a printer for this - * device... - */ +/* + * 'do_set_options()' - Configure the default options for a queue. + */ - if (!cupsArrayFind(printer_devices, (void *)device_uri)) - { - /* - * Not found, so it must be a new printer... - */ +static void +do_set_options(http_t *http, /* I - HTTP connection */ + int is_class) /* I - Set options for class? */ +{ + int i, j, k, m; /* Looping vars */ + int have_options; /* Have options? */ + ipp_t *request, /* IPP request */ + *response; /* IPP response */ + ipp_attribute_t *attr; /* IPP attribute */ + char uri[HTTP_MAX_URI]; /* Job URI */ + const char *var; /* Variable value */ + const char *printer; /* Printer printer name */ + const char *filename; /* PPD filename */ + char tempfile[1024]; /* Temporary filename */ + cups_file_t *in, /* Input file */ + *out; /* Output file */ + char line[1024]; /* Line from PPD file */ + char keyword[1024], /* Keyword from Default line */ + *keyptr; /* Pointer into keyword... */ + ppd_file_t *ppd; /* PPD file */ + ppd_group_t *group; /* Option group */ + ppd_option_t *option; /* Option */ + ppd_attr_t *protocol; /* cupsProtocol attribute */ + const char *title; /* Page title */ - char options[1024], /* Form variables for this device */ - *options_ptr; /* Pointer into string */ - const char *ptr; /* Pointer into device string */ + title = cgiText(is_class ? _("Set Class Options") : _("Set Printer Options")); - /* - * Format the printer name variable for this device... - * - * We use the device-info string first, then device-uri, - * and finally device-make-and-model to come up with a - * suitable name. - */ + fprintf(stderr, "DEBUG: do_set_options(http=%p, is_class=%d)\n", http, + is_class); - strcpy(options, "TEMPLATE_NAME="); - options_ptr = options + strlen(options); + /* + * Get the printer name... + */ - if (strncasecmp(device_info, "unknown", 7)) - ptr = device_info; - else if ((ptr = strstr(device_uri, "://")) != NULL) - ptr += 3; - else - ptr = device_make_and_model; + if ((printer = cgiGetVariable("PRINTER_NAME")) != NULL) + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", + printer); + else + { + cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); + cgiStartHTML(title); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + return; + } - for (; - options_ptr < (options + sizeof(options) - 1) && *ptr; - ptr ++) - if (isalnum(*ptr & 255) || *ptr == '_' || *ptr == '-' || - *ptr == '.') - *options_ptr++ = *ptr; - else if ((*ptr == ' ' || *ptr == '/') && options_ptr[-1] != '_') - *options_ptr++ = '_'; - else if (*ptr == '?' || *ptr == '(') - break; + fprintf(stderr, "DEBUG: printer=\"%s\", uri=\"%s\"...\n", printer, uri); - /* - * Then add the make and model in the printer info, so - * that MacOS clients see something reasonable... - */ + /* + * Get the PPD file... + */ - strlcpy(options_ptr, "&PRINTER_LOCATION=Local+Printer" - "&PRINTER_INFO=", - sizeof(options) - (options_ptr - options)); - options_ptr += strlen(options_ptr); + if (is_class) + filename = NULL; + else + filename = cupsGetPPD2(http, printer); - cgiFormEncode(options_ptr, device_make_and_model, - sizeof(options) - (options_ptr - options)); - options_ptr += strlen(options_ptr); + if (filename) + { + fprintf(stderr, "DEBUG: Got PPD file: \"%s\"\n", filename); - /* - * Then copy the device URI... - */ + if ((ppd = ppdOpenFile(filename)) == NULL) + { + cgiSetVariable("ERROR", ppdErrorString(ppdLastError(&i))); + cgiSetVariable("MESSAGE", cgiText(_("Unable to open PPD file:"))); + cgiStartHTML(title); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); + return; + } + } + else + { + fputs("DEBUG: No PPD file\n", stderr); + ppd = NULL; + } - strlcpy(options_ptr, "&DEVICE_URI=", - sizeof(options) - (options_ptr - options)); - options_ptr += strlen(options_ptr); + if (cgiGetVariable("job_sheets_start") != NULL || + cgiGetVariable("job_sheets_end") != NULL) + have_options = 1; + else + have_options = 0; - cgiFormEncode(options_ptr, device_uri, - sizeof(options) - (options_ptr - options)); - options_ptr += strlen(options_ptr); + if (ppd) + { + ppdMarkDefaults(ppd); - if (options_ptr < (options + sizeof(options) - 1)) - { - *options_ptr++ = '|'; - cgiFormEncode(options_ptr, device_make_and_model, - sizeof(options) - (options_ptr - options)); - } + DEBUG_printf(("

ppd->num_groups = %d\n" + "

    \n", ppd->num_groups)); - /* - * Finally, set the form variables for this printer... - */ + for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) + { + DEBUG_printf(("
  • %s
      \n", group->text)); - cgiSetArray("device_info", i, device_info); - cgiSetArray("device_make_and_model", i, device_make_and_model); - cgiSetArray("device_options", i, options); - cgiSetArray("device_uri", i, device_uri); - i ++; - } + for (j = group->num_options, option = group->options; + j > 0; + j --, option ++) + if ((var = cgiGetVariable(option->keyword)) != NULL) + { + DEBUG_printf(("
    • %s = \"%s\"
    • \n", option->keyword, var)); + have_options = 1; + ppdMarkOption(ppd, option->keyword, var); } +#ifdef DEBUG + else + printf("
    • %s not defined!
    • \n", option->keyword); +#endif /* DEBUG */ - if (!attr) - break; - } - - ippDelete(response); - - /* - * Free the device list... - */ - - for (printer_device = (char *)cupsArrayFirst(printer_devices); - printer_device; - printer_device = (char *)cupsArrayNext(printer_devices)) - free(printer_device); - - cupsArrayDelete(printer_devices); + DEBUG_puts("
  • "); } - } - - /* - * See if Samba and the Windows drivers are installed... - */ - if ((datadir = getenv("CUPS_DATADIR")) == NULL) - datadir = CUPS_DATADIR; + DEBUG_printf(("
\n" + "

ppdConflicts(ppd) = %d\n", ppdConflicts(ppd))); + } - snprintf(filename, sizeof(filename), "%s/drivers/pscript5.dll", datadir); - if (!access(filename, R_OK)) + if (!have_options || ppdConflicts(ppd)) { /* - * Found Windows 2000 driver file, see if we have smbclient and - * rpcclient... + * Show the options to the user... */ - if (cupsFileFind("smbclient", getenv("PATH"), 1, filename, - sizeof(filename)) && - cupsFileFind("rpcclient", getenv("PATH"), 1, filename, - sizeof(filename))) - cgiSetVariable("HAVE_SAMBA", "Y"); - else - { - if (!cupsFileFind("smbclient", getenv("PATH"), 1, filename, - sizeof(filename))) - fputs("ERROR: smbclient not found!\n", stderr); + fputs("DEBUG: Showing options...\n", stderr); - if (!cupsFileFind("rpcclient", getenv("PATH"), 1, filename, - sizeof(filename))) - fputs("ERROR: rpcclient not found!\n", stderr); - } - } - else - perror(filename); + cgiStartHTML(cgiText(_("Set Printer Options"))); + cgiCopyTemplateLang("set-printer-options-header.tmpl"); - /* - * Subscriptions... - */ + if (ppd) + { + ppdLocalize(ppd); - request = ippNewRequest(IPP_GET_SUBSCRIPTIONS); + if (ppdConflicts(ppd)) + { + for (i = ppd->num_groups, k = 0, group = ppd->groups; + i > 0; + i --, group ++) + for (j = group->num_options, option = group->options; + j > 0; + j --, option ++) + if (option->conflicted) + { + cgiSetArray("ckeyword", k, option->keyword); + cgiSetArray("ckeytext", k, option->text); + k ++; + } - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, "ipp://localhost/"); + cgiCopyTemplateLang("option-conflict.tmpl"); + } - if ((response = cupsDoRequest(http, request, "/")) != NULL) - { - cgiSetIPPVars(response, NULL, NULL, NULL, 0); - ippDelete(response); - } + for (i = ppd->num_groups, group = ppd->groups; + i > 0; + i --, group ++) + { + if (!strcmp(group->name, "InstallableOptions")) + cgiSetVariable("GROUP", cgiText(_("Options Installed"))); + else + cgiSetVariable("GROUP", group->text); - /* - * Finally, show the main menu template... - */ + cgiCopyTemplateLang("option-header.tmpl"); + + for (j = group->num_options, option = group->options; + j > 0; + j --, option ++) + { + if (!strcmp(option->keyword, "PageRegion")) + continue; - cgiStartHTML(cgiText(_("Administration"))); + cgiSetVariable("KEYWORD", option->keyword); + cgiSetVariable("KEYTEXT", option->text); + + if (option->conflicted) + cgiSetVariable("CONFLICTED", "1"); + else + cgiSetVariable("CONFLICTED", "0"); + + cgiSetSize("CHOICES", 0); + cgiSetSize("TEXT", 0); + for (k = 0, m = 0; k < option->num_choices; k ++) + { + /* + * Hide custom option values... + */ - cgiCopyTemplateLang("admin.tmpl"); + if (!strcmp(option->choices[k].choice, "Custom")) + continue; - cgiEndHTML(); -} + cgiSetArray("CHOICES", m, option->choices[k].choice); + cgiSetArray("TEXT", m, option->choices[k].text); + m ++; -/* - * 'do_printer_op()' - Do a printer operation. - */ + if (option->choices[k].marked) + cgiSetVariable("DEFCHOICE", option->choices[k].choice); + } -static void -do_printer_op(http_t *http, /* I - HTTP connection */ - ipp_op_t op, /* I - Operation to perform */ - const char *title) /* I - Title of page */ -{ - ipp_t *request; /* IPP request */ - char uri[HTTP_MAX_URI]; /* Printer URI */ - const char *printer, /* Printer name (purge-jobs) */ - *is_class; /* Is a class? */ + switch (option->ui) + { + case PPD_UI_BOOLEAN : + cgiCopyTemplateLang("option-boolean.tmpl"); + break; + case PPD_UI_PICKONE : + cgiCopyTemplateLang("option-pickone.tmpl"); + break; + case PPD_UI_PICKMANY : + cgiCopyTemplateLang("option-pickmany.tmpl"); + break; + } + } + cgiCopyTemplateLang("option-trailer.tmpl"); + } + } - is_class = cgiGetVariable("IS_CLASS"); - printer = cgiGetVariable("PRINTER_NAME"); + /* + * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the + * following attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ - if (!printer) - { - cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); - cgiStartHTML(title); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - return; - } + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); - /* - * Build a printer request, which requires the following - * attributes: - * - * attributes-charset - * attributes-natural-language - * printer-uri - */ + httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, + "localhost", 0, "/printers/%s", printer); + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, uri); - request = ippNewRequest(op); + /* + * Do the request and get back a response... + */ - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", - printer); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, uri); + if ((response = cupsDoRequest(http, request, "/")) != NULL) + { + if ((attr = ippFindAttribute(response, "job-sheets-supported", + IPP_TAG_ZERO)) != NULL) + { + /* + * Add the job sheets options... + */ - /* - * Do the request and get back a response... - */ + cgiSetVariable("GROUP", cgiText(_("Banners"))); + cgiCopyTemplateLang("option-header.tmpl"); - ippDelete(cupsDoRequest(http, request, "/admin/")); + cgiSetSize("CHOICES", attr->num_values); + cgiSetSize("TEXT", attr->num_values); + for (k = 0; k < attr->num_values; k ++) + { + cgiSetArray("CHOICES", k, attr->values[k].string.text); + cgiSetArray("TEXT", k, attr->values[k].string.text); + } - if (cupsLastError() > IPP_OK_CONFLICT) - { - cgiStartHTML(title); - cgiShowIPPError(_("Unable to change printer:")); - } - else - { - /* - * Redirect successful updates back to the printer page... - */ + attr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO); - char url[1024], /* Printer/class URL */ - refresh[1024]; /* Refresh URL */ + cgiSetVariable("KEYWORD", "job_sheets_start"); + cgiSetVariable("KEYTEXT", cgiText(_("Starting Banner"))); + cgiSetVariable("DEFCHOICE", attr == NULL ? + "" : attr->values[0].string.text); + cgiCopyTemplateLang("option-pickone.tmpl"); - cgiRewriteURL(uri, url, sizeof(url), NULL); - cgiFormEncode(uri, url, sizeof(uri)); - snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", uri); - cgiSetVariable("refresh_page", refresh); + cgiSetVariable("KEYWORD", "job_sheets_end"); + cgiSetVariable("KEYTEXT", cgiText(_("Ending Banner"))); + cgiSetVariable("DEFCHOICE", attr == NULL && attr->num_values > 1 ? + "" : attr->values[1].string.text); - cgiStartHTML(title); + cgiCopyTemplateLang("option-pickone.tmpl"); - if (op == IPP_PAUSE_PRINTER) - cgiCopyTemplateLang("printer-stop.tmpl"); - else if (op == IPP_RESUME_PRINTER) - cgiCopyTemplateLang("printer-start.tmpl"); - else if (op == CUPS_ACCEPT_JOBS) - cgiCopyTemplateLang("printer-accept.tmpl"); - else if (op == CUPS_REJECT_JOBS) - cgiCopyTemplateLang("printer-reject.tmpl"); - else if (op == IPP_PURGE_JOBS) - cgiCopyTemplateLang("printer-purge.tmpl"); - else if (op == CUPS_SET_DEFAULT) - cgiCopyTemplateLang("printer-default.tmpl"); - } + cgiCopyTemplateLang("option-trailer.tmpl"); + } - cgiEndHTML(); -} + if (ippFindAttribute(response, "printer-error-policy-supported", + IPP_TAG_ZERO) || + ippFindAttribute(response, "printer-op-policy-supported", + IPP_TAG_ZERO)) + { + /* + * Add the error and operation policy options... + */ + cgiSetVariable("GROUP", cgiText(_("Policies"))); + cgiCopyTemplateLang("option-header.tmpl"); -/* - * 'do_set_allowed_users()' - Set the allowed/denied users for a queue. - */ + /* + * Error policy... + */ -static void -do_set_allowed_users(http_t *http) /* I - HTTP connection */ -{ - int i; /* Looping var */ - ipp_t *request, /* IPP request */ - *response; /* IPP response */ - char uri[HTTP_MAX_URI]; /* Printer URI */ - const char *printer, /* Printer name (purge-jobs) */ - *is_class, /* Is a class? */ - *users, /* List of users or groups */ - *type; /* Allow/deny type */ - int num_users; /* Number of users */ - char *ptr, /* Pointer into users string */ - *end, /* Pointer to end of users string */ - quote; /* Quote character */ - ipp_attribute_t *attr; /* Attribute */ - static const char * const attrs[] = /* Requested attributes */ - { - "requesting-user-name-allowed", - "requesting-user-name-denied" - }; + attr = ippFindAttribute(response, "printer-error-policy-supported", + IPP_TAG_ZERO); + + if (attr) + { + cgiSetSize("CHOICES", attr->num_values); + cgiSetSize("TEXT", attr->num_values); + for (k = 0; k < attr->num_values; k ++) + { + cgiSetArray("CHOICES", k, attr->values[k].string.text); + cgiSetArray("TEXT", k, attr->values[k].string.text); + } + attr = ippFindAttribute(response, "printer-error-policy", + IPP_TAG_ZERO); - is_class = cgiGetVariable("IS_CLASS"); - printer = cgiGetVariable("PRINTER_NAME"); + cgiSetVariable("KEYWORD", "printer_error_policy"); + cgiSetVariable("KEYTEXT", cgiText(_("Error Policy"))); + cgiSetVariable("DEFCHOICE", attr == NULL ? + "" : attr->values[0].string.text); + } - if (!printer) - { - cgiSetVariable("ERROR", cgiText(_("Missing form variable!"))); - cgiStartHTML(cgiText(_("Set Allowed Users"))); - cgiCopyTemplateLang("error.tmpl"); - cgiEndHTML(); - return; - } + cgiCopyTemplateLang("option-pickone.tmpl"); - users = cgiGetVariable("users"); - type = cgiGetVariable("type"); + /* + * Operation policy... + */ - if (!users || !type || - (strcmp(type, "requesting-user-name-allowed") && - strcmp(type, "requesting-user-name-denied"))) - { - /* - * Build a Get-Printer-Attributes request, which requires the following - * attributes: - * - * attributes-charset - * attributes-natural-language - * printer-uri - * requested-attributes - */ + attr = ippFindAttribute(response, "printer-op-policy-supported", + IPP_TAG_ZERO); - request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); + if (attr) + { + cgiSetSize("CHOICES", attr->num_values); + cgiSetSize("TEXT", attr->num_values); + for (k = 0; k < attr->num_values; k ++) + { + cgiSetArray("CHOICES", k, attr->values[k].string.text); + cgiSetArray("TEXT", k, attr->values[k].string.text); + } - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", - printer); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, uri); + attr = ippFindAttribute(response, "printer-op-policy", IPP_TAG_ZERO); - ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, - "requested-attributes", - (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs); + cgiSetVariable("KEYWORD", "printer_op_policy"); + cgiSetVariable("KEYTEXT", cgiText(_("Operation Policy"))); + cgiSetVariable("DEFCHOICE", attr == NULL ? + "" : attr->values[0].string.text); - /* - * Do the request and get back a response... - */ + cgiCopyTemplateLang("option-pickone.tmpl"); + } - if ((response = cupsDoRequest(http, request, "/")) != NULL) - { - cgiSetIPPVars(response, NULL, NULL, NULL, 0); + cgiCopyTemplateLang("option-trailer.tmpl"); + } ippDelete(response); } - cgiStartHTML(cgiText(_("Set Allowed Users"))); - - if (cupsLastError() > IPP_OK_CONFLICT) - cgiShowIPPError(_("Unable to get printer attributes:")); - else - cgiCopyTemplateLang("users.tmpl"); - - cgiEndHTML(); - } - else - { /* - * Save the changes... + * Binary protocol support... */ - for (num_users = 0, ptr = (char *)users; *ptr; num_users ++) + if (ppd->protocols && strstr(ppd->protocols, "BCP")) { - /* - * Skip whitespace and commas... - */ - - while (*ptr == ',' || isspace(*ptr & 255)) - ptr ++; + protocol = ppdFindAttr(ppd, "cupsProtocol", NULL); - if (*ptr == '\'' || *ptr == '\"') - { - /* - * Scan quoted name... - */ + cgiSetVariable("GROUP", cgiText(_("PS Binary Protocol"))); + cgiCopyTemplateLang("option-header.tmpl"); - quote = *ptr++; + cgiSetSize("CHOICES", 2); + cgiSetSize("TEXT", 2); + cgiSetArray("CHOICES", 0, "None"); + cgiSetArray("TEXT", 0, cgiText(_("None"))); - for (end = ptr; *end; end ++) - if (*end == quote) - break; + if (strstr(ppd->protocols, "TBCP")) + { + cgiSetArray("CHOICES", 1, "TBCP"); + cgiSetArray("TEXT", 1, "TBCP"); } else { - /* - * Scan space or comma-delimited name... - */ - - for (end = ptr; *end; end ++) - if (isspace(*end & 255) || *end == ',') - break; + cgiSetArray("CHOICES", 1, "BCP"); + cgiSetArray("TEXT", 1, "BCP"); } - /* - * Advance to the next name... - */ + cgiSetVariable("KEYWORD", "protocol"); + cgiSetVariable("KEYTEXT", cgiText(_("PS Binary Protocol"))); + cgiSetVariable("DEFCHOICE", protocol ? protocol->value : "None"); - ptr = end; + cgiCopyTemplateLang("option-pickone.tmpl"); + + cgiCopyTemplateLang("option-trailer.tmpl"); } + cgiCopyTemplateLang("set-printer-options-trailer.tmpl"); + cgiEndHTML(); + } + else + { /* - * Build a CUPS-Add-Printer/Class request, which requires the following - * attributes: - * - * attributes-charset - * attributes-natural-language - * printer-uri - * requesting-user-name-{allowed,denied} + * Set default options... */ - request = ippNewRequest(is_class ? CUPS_ADD_CLASS : CUPS_ADD_PRINTER); - - httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, - "localhost", 0, is_class ? "/classes/%s" : "/printers/%s", - printer); - ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", - NULL, uri); + fputs("DEBUG: Setting options...\n", stderr); - if (num_users == 0) - ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, - "requesting-user-name-allowed", NULL, "all"); - else + if (filename) { - attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, - type, num_users, NULL, NULL); + out = cupsTempFile2(tempfile, sizeof(tempfile)); + in = cupsFileOpen(filename, "r"); - for (i = 0, ptr = (char *)users; *ptr; i ++) + if (!in || !out) { - /* - * Skip whitespace and commas... - */ + cgiSetVariable("ERROR", strerror(errno)); + cgiStartHTML(cgiText(_("Set Printer Options"))); + cgiCopyTemplateLang("error.tmpl"); + cgiEndHTML(); - while (*ptr == ',' || isspace(*ptr & 255)) - ptr ++; + if (in) + cupsFileClose(in); - if (*ptr == '\'' || *ptr == '\"') + if (out) { - /* - * Scan quoted name... - */ + cupsFileClose(out); + unlink(tempfile); + } - quote = *ptr++; + unlink(filename); + return; + } - for (end = ptr; *end; end ++) - if (*end == quote) - break; - } + while (cupsFileGets(in, line, sizeof(line))) + { + if (!strncmp(line, "*cupsProtocol:", 14) && cgiGetVariable("protocol")) + continue; + else if (strncmp(line, "*Default", 8)) + cupsFilePrintf(out, "%s\n", line); else { /* - * Scan space or comma-delimited name... + * Get default option name... */ - for (end = ptr; *end; end ++) - if (isspace(*end & 255) || *end == ',') + strlcpy(keyword, line + 8, sizeof(keyword)); + + for (keyptr = keyword; *keyptr; keyptr ++) + if (*keyptr == ':' || isspace(*keyptr & 255)) break; - } - /* - * Terminate the name... - */ + *keyptr = '\0'; - if (*end) - *end++ = '\0'; + if (!strcmp(keyword, "PageRegion") || + !strcmp(keyword, "PaperDimension") || + !strcmp(keyword, "ImageableArea")) + var = cgiGetVariable("PageSize"); + else + var = cgiGetVariable(keyword); - /* - * Add the name... - */ + if (var != NULL) + cupsFilePrintf(out, "*Default%s: %s\n", keyword, var); + else + cupsFilePrintf(out, "%s\n", line); + } + } - attr->values[i].string.text = strdup(ptr); + if ((var = cgiGetVariable("protocol")) != NULL) + cupsFilePrintf(out, "*cupsProtocol: %s\n", cgiGetVariable("protocol")); - /* - * Advance to the next name... - */ + cupsFileClose(in); + cupsFileClose(out); + } + else + { + /* + * Make sure temporary filename is cleared when there is no PPD... + */ - ptr = end; - } + tempfile[0] = '\0'; } + /* + * Build a CUPS_ADD_MODIFY_CLASS/PRINTER request, which requires the + * following attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * job-sheets-default + * printer-error-policy + * printer-op-policy + * [ppd file] + */ + + request = ippNewRequest(is_class ? CUPS_ADD_MODIFY_CLASS : + CUPS_ADD_MODIFY_PRINTER); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", + NULL, uri); + + attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_NAME, + "job-sheets-default", 2, NULL, NULL); + attr->values[0].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_start")); + attr->values[1].string.text = _cupsStrAlloc(cgiGetVariable("job_sheets_end")); + + if ((var = cgiGetVariable("printer_error_policy")) != NULL) + attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, + "printer-error-policy", NULL, var); + + if ((var = cgiGetVariable("printer_op_policy")) != NULL) + attr = ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, + "printer-op-policy", NULL, var); + /* * Do the request and get back a response... */ - ippDelete(cupsDoRequest(http, request, "/admin/")); + if (filename) + ippDelete(cupsDoFileRequest(http, request, "/admin/", tempfile)); + else + ippDelete(cupsDoRequest(http, request, "/admin/")); if (cupsLastError() > IPP_OK_CONFLICT) { - cgiStartHTML(cgiText(_("Set Allowed Users"))); - cgiShowIPPError(_("Unable to change printer:")); + cgiStartHTML(title); + cgiShowIPPError(_("Unable to set options:")); } else { @@ -3092,24 +3147,27 @@ do_set_allowed_users(http_t *http) /* I - HTTP connection */ * Redirect successful updates back to the printer page... */ - char url[1024], /* Printer/class URL */ - refresh[1024]; /* Refresh URL */ + char refresh[1024]; /* Refresh URL */ - cgiRewriteURL(uri, url, sizeof(url), NULL); - cgiFormEncode(uri, url, sizeof(uri)); - snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=%s", - uri); + cgiFormEncode(uri, printer, sizeof(uri)); + snprintf(refresh, sizeof(refresh), "5;URL=/admin/?OP=redirect&URL=/%s/%s", + is_class ? "classes" : "printers", uri); cgiSetVariable("refresh_page", refresh); - cgiStartHTML(cgiText(_("Set Allowed Users"))); + cgiStartHTML(title); - cgiCopyTemplateLang(is_class ? "class-modified.tmpl" : - "printer-modified.tmpl"); + cgiCopyTemplateLang("printer-configured.tmpl"); } cgiEndHTML(); + + if (filename) + unlink(tempfile); } + + if (filename) + unlink(filename); } @@ -3251,5 +3309,5 @@ match_string(const char *a, /* I - First string */ /* - * End of "$Id: admin.c 6378 2007-03-21 07:18:18Z mike $". + * End of "$Id: admin.c 6440 2007-04-03 23:17:17Z mike $". */ diff --git a/cups/adminutil.c b/cups/adminutil.c index 2bd6caf5fc..a58f579067 100644 --- a/cups/adminutil.c +++ b/cups/adminutil.c @@ -1,5 +1,5 @@ /* - * "$Id: adminutil.c 6378 2007-03-21 07:18:18Z mike $" + * "$Id: adminutil.c 6435 2007-04-02 22:43:39Z mike $" * * Administration utility API definitions for the Common UNIX Printing * System (CUPS). @@ -108,6 +108,7 @@ cupsAdminCreateWindowsPPD( option[41], /* Option */ choice[41]; /* Choice */ int jcloption, /* In a JCL option? */ + jclorder, /* Next JCL order dependency */ linenum; /* Current line number */ time_t curtime; /* Current time */ struct tm *curdate; /* Current date */ @@ -205,6 +206,7 @@ cupsAdminCreateWindowsPPD( */ jcloption = 0; + jclorder = 0; linenum = 0; language = cupsLangDefault(); @@ -243,10 +245,23 @@ cupsAdminCreateWindowsPPD( jcloption = 0; cupsFilePrintf(dstfp, "%s\n", line); } + else if (jcloption && !strncmp(line, "*OrderDependency:", 17)) + { + for (ptr = line + 17; *ptr && isspace(*ptr & 255); ptr ++); + + ptr = strchr(ptr, ' '); + + if (ptr) + { + cupsFilePrintf(dstfp, "*OrderDependency: %d%s\n", jclorder, ptr); + jclorder ++; + } + else + cupsFilePrintf(dstfp, "%s\n", line); + } else if (jcloption && strncmp(line, "*End", 4) && - strncmp(line, "*Default", 8) && - strncmp(line, "*OrderDependency", 16)) + strncmp(line, "*Default", 8)) { if ((ptr = strchr(line, ':')) == NULL) { @@ -363,24 +378,24 @@ cupsAdminCreateWindowsPPD( IPP_TAG_ZERO)) != NULL && (suppattr = ippFindAttribute(response, "job-hold-until-supported", IPP_TAG_ZERO)) != NULL) - write_option(dstfp, 10, "cupsJobHoldUntil", "Hold Until", "job-hold-until", - suppattr, defattr, 0, 1); + write_option(dstfp, jclorder ++, "cupsJobHoldUntil", "Hold Until", + "job-hold-until", suppattr, defattr, 0, 1); if ((defattr = ippFindAttribute(response, "job-priority-default", IPP_TAG_INTEGER)) != NULL && (suppattr = ippFindAttribute(response, "job-priority-supported", IPP_TAG_RANGE)) != NULL) - write_option(dstfp, 11, "cupsJobPriority", "Priority", "job-priority", - suppattr, defattr, 0, 1); + write_option(dstfp, jclorder ++, "cupsJobPriority", "Priority", + "job-priority", suppattr, defattr, 0, 1); if ((defattr = ippFindAttribute(response, "job-sheets-default", IPP_TAG_ZERO)) != NULL && (suppattr = ippFindAttribute(response, "job-sheets-supported", IPP_TAG_ZERO)) != NULL) { - write_option(dstfp, 20, "cupsJobSheetsStart", "Start Banner", + write_option(dstfp, jclorder ++, "cupsJobSheetsStart", "Start Banner", "job-sheets", suppattr, defattr, 0, 2); - write_option(dstfp, 21, "cupsJobSheetsEnd", "End Banner", + write_option(dstfp, jclorder ++, "cupsJobSheetsEnd", "End Banner", "job-sheets", suppattr, defattr, 1, 2); } @@ -2194,5 +2209,5 @@ write_option(cups_file_t *dstfp, /* I - PPD file */ /* - * End of "$Id: adminutil.c 6378 2007-03-21 07:18:18Z mike $". + * End of "$Id: adminutil.c 6435 2007-04-02 22:43:39Z mike $". */ diff --git a/cups/ppd.c b/cups/ppd.c index 6a6c665ab6..c25e57ce24 100644 --- a/cups/ppd.c +++ b/cups/ppd.c @@ -1,5 +1,5 @@ /* - * "$Id: ppd.c 6187 2007-01-10 16:20:42Z mike $" + * "$Id: ppd.c 6445 2007-04-04 23:43:26Z mike $" * * PPD file routines for the Common UNIX Printing System (CUPS). * @@ -1003,7 +1003,7 @@ ppdOpen2(cups_file_t *fp) /* I - File to read from */ for (i = 0, sptr = string; i < 4; i ++) ppd->custom_margins[i] = (float)_cupsStrScand(sptr, &sptr, loc); } - else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True")) + else if (!strncmp(keyword, "Custom", 6) && !strcmp(name, "True") && !option) { DEBUG_puts("Processing Custom option..."); @@ -1076,6 +1076,34 @@ ppdOpen2(cups_file_t *fp) /* I - File to read from */ */ ppd_add_size(ppd, "Custom"); + + if ((option = ppdFindOption(ppd, "PageRegion")) == NULL) + { + ppd_group_t *gtemp; /* Temporary group */ + + if ((gtemp = ppd_get_group(ppd, "General", _("General"), cg, + encoding)) == NULL) + { + DEBUG_puts("Unable to get general group!"); + + goto error; + } + + option = ppd_get_option(gtemp, "PageRegion"); + } + + if ((choice = ppd_add_choice(option, "Custom")) == NULL) + { + DEBUG_puts("Unable to add Custom choice!"); + + cg->ppd_status = PPD_ALLOC_ERROR; + + goto error; + } + + strlcpy(choice->text, text[0] ? text : _("Custom"), + sizeof(choice->text)); + option = NULL; } } else if (!strcmp(keyword, "LandscapeOrientation")) @@ -1523,10 +1551,10 @@ ppdOpen2(cups_file_t *fp) /* I - File to read from */ !strcmp(keyword, "NonUIConstraints")) { if (ppd->num_consts == 0) - constraint = calloc(1, sizeof(ppd_const_t)); + constraint = calloc(2, sizeof(ppd_const_t)); else constraint = realloc(ppd->consts, - (ppd->num_consts + 1) * sizeof(ppd_const_t)); + (ppd->num_consts + 2) * sizeof(ppd_const_t)); if (constraint == NULL) { @@ -1687,6 +1715,34 @@ ppdOpen2(cups_file_t *fp) /* I - File to read from */ break; } + /* + * For CustomPageSize and InputSlot/ManualFeed, create a duplicate + * constraint for PageRegion... + */ + + if (!strcasecmp(constraint->option1, "CustomPageSize") && + (!strcasecmp(constraint->option2, "InputSlot") || + !strcasecmp(constraint->option2, "ManualFeed"))) + { + ppd->num_consts ++; + + strcpy(constraint[1].option1, "PageRegion"); + strcpy(constraint[1].choice1, "Custom"); + strcpy(constraint[1].option2, constraint->option2); + strcpy(constraint[1].choice2, constraint->choice2); + } + else if (!strcasecmp(constraint->option2, "CustomPageSize") && + (!strcasecmp(constraint->option1, "InputSlot") || + !strcasecmp(constraint->option1, "ManualFeed"))) + { + ppd->num_consts ++; + + strcpy(constraint[1].option1, constraint->option1); + strcpy(constraint[1].choice1, constraint->choice1); + strcpy(constraint[1].option2, "PageRegion"); + strcpy(constraint[1].choice2, "Custom"); + } + /* * Handle CustomFoo option constraints... */ @@ -3046,5 +3102,5 @@ ppd_read(cups_file_t *fp, /* I - File to read from */ /* - * End of "$Id: ppd.c 6187 2007-01-10 16:20:42Z mike $". + * End of "$Id: ppd.c 6445 2007-04-04 23:43:26Z mike $". */ diff --git a/doc/help/ref-cupsd-conf.html b/doc/help/ref-cupsd-conf.html index 8e07d06200..997b479a33 100644 --- a/doc/help/ref-cupsd-conf.html +++ b/doc/help/ref-cupsd-conf.html @@ -1078,6 +1078,41 @@ default error log file is /var/log/cups/error_log.

information to the system log instead of a plain file.

+

CUPS 1.3ErrorPolicy

+ +

Examples

+ +
+ErrorPolicy abort-job
+ErrorPolicy retry-job
+ErrorPolicy stop-printer
+
+ +

Description

+ +

The ErrorPolicy directive defines the default policy that +is used when a backend is unable to send a print job to the +printer.

+ +

The following values are supported:

+ +
    + +
  • abort-job - Abort the job and proceed + with the next job in the queue
  • + +
  • retry-job - Retry the job after waiting + for N seconds; the cupsd.conf JobRetryInterval + directive controls the value of N
  • + +
  • stop-printer - Stop the printer and keep + the job for future printing; this is the default + value
  • + +
+ +

CUPS 1.1.18FileDevice

Examples

diff --git a/scheduler/conf.c b/scheduler/conf.c index 8c12d63227..ad463acb69 100644 --- a/scheduler/conf.c +++ b/scheduler/conf.c @@ -1,5 +1,5 @@ /* - * "$Id: conf.c 6409 2007-03-28 18:02:59Z mike $" + * "$Id: conf.c 6436 2007-04-02 23:24:02Z mike $" * * Configuration routines for the Common UNIX Printing System (CUPS). * @@ -116,6 +116,7 @@ static cupsd_var_t variables[] = { "DefaultShared", &DefaultShared, CUPSD_VARTYPE_BOOLEAN }, { "DocumentRoot", &DocumentRoot, CUPSD_VARTYPE_STRING }, { "ErrorLog", &ErrorLog, CUPSD_VARTYPE_STRING }, + { "ErrorPolicy", &ErrorPolicy, CUPSD_VARTYPE_STRING }, { "FileDevice", &FileDevice, CUPSD_VARTYPE_BOOLEAN }, { "FilterLimit", &FilterLimit, CUPSD_VARTYPE_INTEGER }, { "FilterNice", &FilterNice, CUPSD_VARTYPE_INTEGER }, @@ -431,6 +432,8 @@ cupsdReadConfiguration(void) cupsdClearString(&BrowseLocalOptions); cupsdClearString(&BrowseRemoteOptions); + cupsdSetString(&ErrorPolicy, "stop-printer"); + #ifdef HAVE_LDAP cupsdClearString(&BrowseLDAPBindDN); cupsdClearString(&BrowseLDAPDN); @@ -3349,5 +3352,5 @@ read_policy(cups_file_t *fp, /* I - Configuration file */ /* - * End of "$Id: conf.c 6409 2007-03-28 18:02:59Z mike $". + * End of "$Id: conf.c 6436 2007-04-02 23:24:02Z mike $". */ diff --git a/scheduler/conf.h b/scheduler/conf.h index 0a5e073d69..6bb223ecba 100644 --- a/scheduler/conf.h +++ b/scheduler/conf.h @@ -1,5 +1,5 @@ /* - * "$Id: conf.h 6365 2007-03-19 20:56:57Z mike $" + * "$Id: conf.h 6436 2007-04-02 23:24:02Z mike $" * * Configuration file definitions for the Common UNIX Printing System (CUPS) * scheduler. @@ -97,6 +97,8 @@ VAR char *AccessLog VALUE(NULL), /* Default charset */ *DefaultLocale VALUE(NULL), /* Default locale */ + *ErrorPolicy VALUE(NULL), + /* Default printer-error-policy */ *RIPCache VALUE(NULL), /* Amount of memory for RIPs */ *TempDir VALUE(NULL), @@ -233,5 +235,5 @@ extern int cupsdLogRequest(cupsd_client_t *con, http_status_t code); /* - * End of "$Id: conf.h 6365 2007-03-19 20:56:57Z mike $". + * End of "$Id: conf.h 6436 2007-04-02 23:24:02Z mike $". */ diff --git a/scheduler/ipp.c b/scheduler/ipp.c index bb0645e45e..5a8555ac4e 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -1,5 +1,5 @@ /* - * "$Id: ipp.c 6397 2007-03-25 23:33:32Z mike $" + * "$Id: ipp.c 6433 2007-04-02 21:50:50Z mike $" * * IPP routines for the Common UNIX Printing System (CUPS) scheduler. * @@ -4433,6 +4433,16 @@ copy_printer_attrs( printer->recoverable); #endif /* __APPLE__ */ + if (printer->alert && (!ra || cupsArrayFind(ra, "printer-alert"))) + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_STRING, + "printer-alert", NULL, printer->alert); + + if (printer->alert_description && + (!ra || cupsArrayFind(ra, "printer-alert-description"))) + ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT, + "printer-alert-description", NULL, + printer->alert_description); + if (!ra || cupsArrayFind(ra, "printer-current-time")) ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time", ippTimeToDate(curtime)); @@ -4825,6 +4835,8 @@ create_requested_array(ipp_t *request) /* I - IPP request */ cupsArrayAdd(ra, "pages-per-minute"); cupsArrayAdd(ra, "pages-per-minute-color"); cupsArrayAdd(ra, "pdl-override-supported"); + cupsArrayAdd(ra, "printer-alert"); + cupsArrayAdd(ra, "printer-alert-description"); cupsArrayAdd(ra, "printer-current-time"); cupsArrayAdd(ra, "printer-driver-installer"); cupsArrayAdd(ra, "printer-info"); @@ -9473,5 +9485,5 @@ validate_user(cupsd_job_t *job, /* I - Job */ /* - * End of "$Id: ipp.c 6397 2007-03-25 23:33:32Z mike $". + * End of "$Id: ipp.c 6433 2007-04-02 21:50:50Z mike $". */ diff --git a/scheduler/job.c b/scheduler/job.c index caa3180792..96513a2f55 100644 --- a/scheduler/job.c +++ b/scheduler/job.c @@ -1,5 +1,5 @@ /* - * "$Id: job.c 6424 2007-04-02 13:09:06Z mike $" + * "$Id: job.c 6433 2007-04-02 21:50:50Z mike $" * * Job management routines for the Common UNIX Printing System (CUPS). * @@ -3458,6 +3458,19 @@ update_job(cupsd_job_t *job) /* I - Job to check */ attrs)) != NULL) cupsdSetAuthInfoRequired(job->printer, attr, NULL); + if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL) + { + cupsdSetString(&job->printer->alert, attr); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; + } + + if ((attr = cupsGetOption("printer-alert-description", num_attrs, + attrs)) != NULL) + { + cupsdSetString(&job->printer->alert_description, attr); + event |= CUPSD_EVENT_PRINTER_STATE_CHANGED; + } + cupsFreeOptions(num_attrs, attrs); } #ifdef __APPLE__ @@ -3536,5 +3549,5 @@ update_job(cupsd_job_t *job) /* I - Job to check */ /* - * End of "$Id: job.c 6424 2007-04-02 13:09:06Z mike $". + * End of "$Id: job.c 6433 2007-04-02 21:50:50Z mike $". */ diff --git a/scheduler/printers.c b/scheduler/printers.c index 831d6008db..1934383ec2 100644 --- a/scheduler/printers.c +++ b/scheduler/printers.c @@ -1,5 +1,5 @@ /* - * "$Id: printers.c 6429 2007-04-02 13:44:52Z mike $" + * "$Id: printers.c 6436 2007-04-02 23:24:02Z mike $" * * Printer routines for the Common UNIX Printing System (CUPS). * @@ -124,7 +124,7 @@ cupsdAddPrinter(const char *name) /* I - Name of printer */ cupsdSetString(&p->job_sheets[0], "none"); cupsdSetString(&p->job_sheets[1], "none"); - cupsdSetString(&p->error_policy, "stop-printer"); + cupsdSetString(&p->error_policy, ErrorPolicy); cupsdSetString(&p->op_policy, DefaultPolicy); p->op_policy_ptr = DefaultPolicyPtr; @@ -747,6 +747,9 @@ cupsdDeletePrinter( cupsdClearString(&p->op_policy); cupsdClearString(&p->error_policy); + cupsdClearString(&p->alert); + cupsdClearString(&p->alert_description); + #ifdef HAVE_DNSSD cupsdClearString(&p->product); cupsdClearString(&p->pdl); @@ -3705,5 +3708,5 @@ write_irix_state(cupsd_printer_t *p) /* I - Printer to update */ /* - * End of "$Id: printers.c 6429 2007-04-02 13:44:52Z mike $". + * End of "$Id: printers.c 6436 2007-04-02 23:24:02Z mike $". */ diff --git a/scheduler/printers.h b/scheduler/printers.h index 77782359d2..c822ad157b 100644 --- a/scheduler/printers.h +++ b/scheduler/printers.h @@ -1,5 +1,5 @@ /* - * "$Id: printers.h 6318 2007-03-06 04:36:55Z mike $" + * "$Id: printers.h 6433 2007-04-02 21:50:50Z mike $" * * Printer definitions for the Common UNIX Printing System (CUPS) scheduler. * @@ -93,6 +93,9 @@ typedef struct cupsd_printer_s cups_option_t *options; /* Default options */ int num_auth_info_required; /* Number of required auth fields */ const char *auth_info_required[4]; /* Required authentication fields */ + char *alert, /* PSX printer-alert value */ + *alert_description; /* PSX printer-alert-description value */ + #ifdef __APPLE__ char *recoverable; /* com.apple.print.recoverable-message */ #endif /* __APPLE__ */ @@ -172,5 +175,5 @@ extern void cupsdWritePrintcap(void); /* - * End of "$Id: printers.h 6318 2007-03-06 04:36:55Z mike $". + * End of "$Id: printers.h 6433 2007-04-02 21:50:50Z mike $". */ diff --git a/systemv/cupstestppd.c b/systemv/cupstestppd.c index 474ad02b23..ed1becfb5a 100644 --- a/systemv/cupstestppd.c +++ b/systemv/cupstestppd.c @@ -1,5 +1,5 @@ /* - * "$Id: cupstestppd.c 6430 2007-04-02 14:01:55Z mike $" + * "$Id: cupstestppd.c 6444 2007-04-04 22:13:58Z mike $" * * PPD test program for the Common UNIX Printing System (CUPS). * @@ -42,6 +42,7 @@ #include #include #include +#include /* @@ -84,31 +85,39 @@ int valid_utf8(const char *s); * 'main()' - Main entry for test program. */ -int /* O - Exit status */ -main(int argc, /* I - Number of command-line arguments */ - char *argv[]) /* I - Command-line arguments */ +int /* O - Exit status */ +main(int argc, /* I - Number of command-line args */ + char *argv[]) /* I - Command-line arguments */ { - int i, j, k, m, n; /* Looping vars */ - int len; /* Length of option name */ - char *opt; /* Option character */ - const char *ptr; /* Pointer into string */ - int files; /* Number of files */ - int verbose; /* Want verbose output? */ - int status; /* Exit status */ - int errors; /* Number of conformance errors */ - int ppdversion; /* PPD spec version in PPD file */ - ppd_status_t error; /* Status of ppdOpen*() */ - int line; /* Line number for error */ - int xdpi, /* X resolution */ - ydpi; /* Y resolution */ - ppd_file_t *ppd; /* PPD file record */ - ppd_attr_t *attr; /* PPD attribute */ - ppd_size_t *size; /* Size record */ - ppd_group_t *group; /* UI group */ - ppd_option_t *option; /* Standard UI option */ - ppd_group_t *group2; /* UI group */ - ppd_option_t *option2; /* Standard UI option */ - ppd_choice_t *choice; /* Standard UI option choice */ + int i, j, k, m, n; /* Looping vars */ + int len; /* Length of option name */ + char *opt; /* Option character */ + const char *ptr; /* Pointer into string */ + int files; /* Number of files */ + int verbose; /* Want verbose output? */ + int status; /* Exit status */ + int errors; /* Number of conformance errors */ + int ppdversion; /* PPD spec version in PPD file */ + ppd_status_t error; /* Status of ppdOpen*() */ + int line; /* Line number for error */ + struct stat statbuf; /* File information */ + char super[16], /* Super-type for filter */ + type[256], /* Type for filter */ + program[256], /* Program/filter name */ + pathprog[1024], /* Complete path to program/filter */ + *root; /* Root directory */ + int cost; /* Cost of filter */ + int xdpi, /* X resolution */ + ydpi; /* Y resolution */ + ppd_file_t *ppd; /* PPD file record */ + ppd_attr_t *attr; /* PPD attribute */ + ppd_size_t *size; /* Size record */ + ppd_group_t *group; /* UI group */ + ppd_option_t *option; /* Standard UI option */ + ppd_group_t *group2; /* UI group */ + ppd_option_t *option2; /* Standard UI option */ + ppd_choice_t *choice; /* Standard UI option choice */ + ppd_const_t *c; /* Current constraint */ static char *uis[] = { "BOOLEAN", "PICKONE", "PICKMANY" }; static char *sections[] = { "ANY", "DOCUMENT", "EXIT", "JCL", "PAGE", "PROLOG" }; @@ -126,6 +135,7 @@ main(int argc, /* I - Number of command-line arguments */ ppd = NULL; files = 0; status = ERROR_NONE; + root = ""; for (i = 1; i < argc; i ++) if (argv[i][0] == '-' && argv[i][1]) @@ -133,6 +143,15 @@ main(int argc, /* I - Number of command-line arguments */ for (opt = argv[i] + 1; *opt; opt ++) switch (*opt) { + case 'R' : /* Alternate root directory */ + i ++; + + if (i >= argc) + usage(); + + root = argv[i]; + break; + case 'q' : /* Quiet mode */ if (verbose > 0) { @@ -318,6 +337,7 @@ main(int argc, /* I - Number of command-line arguments */ !strcmp(attr->name, "DefaultImageableArea") || !strcmp(attr->name, "DefaultOutputOrder") || !strcmp(attr->name, "DefaultPaperDimension") || + !strcmp(attr->name, "DefaultResolution") || !strcmp(attr->name, "DefaultTransfer")) continue; @@ -1313,12 +1333,59 @@ main(int argc, /* I - Number of command-line arguments */ attr; attr = ppdFindNextAttr(ppd, "cupsFilter", NULL)) { - char super[16], /* Filter super type */ - type[256], /* Filter type */ - program[256]; /* Filter program */ - int cost; /* Filter cost */ + if (!attr->value || + sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost, + program) != 4) + { + if (verbose >= 0) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, + _(" **FAIL** Bad cupsFilter value \"%s\"!\n"), + attr->value ? attr->value : ""); + } + errors ++; + } + else + { + if (program[0] == '/') + snprintf(pathprog, sizeof(pathprog), "%s%s", root, program); + else + { + if ((ptr = getenv("CUPS_SERVERBIN")) == NULL) + ptr = CUPS_SERVERBIN; + + if (*ptr == '/' || !*root) + snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr, + program); + else + snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr, + program); + } + if (stat(pathprog, &statbuf)) + { + if (verbose >= 0) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, _(" **FAIL** Missing cupsFilter " + "file \"%s\"\n"), program); + } + + errors ++; + } + } + } + + for (attr = ppdFindAttr(ppd, "cupsPreFilter", NULL); + attr; + attr = ppdFindNextAttr(ppd, "cupsPreFilter", NULL)) + { if (!attr->value || sscanf(attr->value, "%15[^/]/%255s%d%255s", super, type, &cost, program) != 4) @@ -1329,12 +1396,43 @@ main(int argc, /* I - Number of command-line arguments */ _cupsLangPuts(stdout, _(" FAIL\n")); _cupsLangPrintf(stdout, - _(" **FAIL** Bad cupsFilter value \"%s\"!\n"), + _(" **FAIL** Bad cupsPreFilter value \"%s\"!\n"), attr->value ? attr->value : ""); } errors ++; } + else + { + if (program[0] == '/') + snprintf(pathprog, sizeof(pathprog), "%s%s", root, program); + else + { + if ((ptr = getenv("CUPS_SERVERBIN")) == NULL) + ptr = CUPS_SERVERBIN; + + if (*ptr == '/' || !*root) + snprintf(pathprog, sizeof(pathprog), "%s%s/filter/%s", root, ptr, + program); + else + snprintf(pathprog, sizeof(pathprog), "%s/%s/filter/%s", root, ptr, + program); + } + + if (stat(pathprog, &statbuf)) + { + if (verbose >= 0) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, _(" **FAIL** Missing cupsPreFilter " + "file \"%s\"\n"), program); + } + + errors ++; + } + } } if ((attr = ppdFindAttr(ppd, "1284DeviceID", NULL)) && @@ -1354,6 +1452,66 @@ main(int argc, /* I - Number of command-line arguments */ errors ++; } + /* + * Check for bad UIConstraints... + */ + + for (j = ppd->num_consts, c = ppd->consts; j > 0; j --, c ++) + { + option = ppdFindOption(ppd, c->option1); + option2 = ppdFindOption(ppd, c->option2); + + if (!option || !option2) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + if (!option) + _cupsLangPrintf(stdout, + _(" **FAIL** Missing option %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + c->option1, + c->option1, c->choice1, c->option2, c->choice2); + + if (!option2) + _cupsLangPrintf(stdout, + _(" **FAIL** Missing option %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + c->option2, + c->option1, c->choice1, c->option2, c->choice2); + + continue; + } + + if (c->choice1[0] && !ppdFindChoice(option, c->choice1)) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, + _(" **FAIL** Missing choice *%s %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + c->option1, c->choice1, + c->option1, c->choice1, c->option2, c->choice2); + } + + if (c->choice2[0] && !ppdFindChoice(option2, c->choice2)) + { + if (!errors && !verbose) + _cupsLangPuts(stdout, _(" FAIL\n")); + + _cupsLangPrintf(stdout, + _(" **FAIL** Missing choice *%s %s in " + "UIConstraint \"*%s %s *%s %s\"!\n"), + c->option2, c->choice2, + c->option1, c->choice1, c->option2, c->choice2); + } + } + + /* + * Final pass/fail notification... + */ + if (errors) status = ERROR_CONFORMANCE; else if (!verbose) @@ -1517,6 +1675,73 @@ main(int argc, /* I - Number of command-line arguments */ } } + /* + * cupsICCProfile + */ + + for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL); + attr != NULL; + attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL)) + { + if (attr->value) + { + if (attr->value[0] == '/') + snprintf(pathprog, sizeof(pathprog), "%s%s", root, attr->value); + else + { + if ((ptr = getenv("CUPS_DATADIR")) == NULL) + ptr = CUPS_DATADIR; + + if (*ptr == '/' || !*root) + snprintf(pathprog, sizeof(pathprog), "%s%s/profiles/%s", root, + ptr, attr->value); + else + snprintf(pathprog, sizeof(pathprog), "%s/%s/profiles/%s", root, + ptr, attr->value); + } + } + + if (!attr->value || !attr->value[0] || stat(pathprog, &statbuf)) + { + if (verbose >= 0) + _cupsLangPrintf(stdout, + _(" WARN Missing cupsICCProfile " + "file \"%s\"\n"), + !attr->value || !attr->value[0] ? "" : + attr->value); + } + } + +#ifdef __APPLE__ + /* + * APDialogExtension + */ + + for (attr = ppdFindAttr(ppd, "APDialogExtension", NULL); + attr != NULL; + attr = ppdFindNextAttr(ppd, "APDialogExtension", NULL)) + { + if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0) + _cupsLangPrintf(stdout, _(" WARN Missing " + "APDialogExtension file \"%s\"\n"), + attr->value ? attr->value : ""); + } + + /* + * APPrinterIconPath + */ + + for (attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL); + attr != NULL; + attr = ppdFindNextAttr(ppd, "APPrinterIconPath", NULL)) + { + if ((!attr->value || stat(attr->value, &statbuf)) && verbose >= 0) + _cupsLangPrintf(stdout, _(" WARN Missing " + "APPrinterIconPath file \"%s\"\n"), + attr->value ? attr->value : ""); + } +#endif /* __APPLE__ */ + if (verbose > 0) { if (errors) @@ -1882,9 +2107,10 @@ void usage(void) { _cupsLangPuts(stdout, - _("Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] " - "[... filenameN.ppd[.gz]]\n" - " program | cupstestppd [-q] [-r] [-v[v]] -\n")); + _("Usage: cupstestppd [-R root-directory] [-q] [-r] [-v[v]] " + "filename1.ppd[.gz] [... filenameN.ppd[.gz]]\n" + " program | cupstestppd [-R root-directory] [-q] [-r] " + "[-v[v]] -\n")); exit(ERROR_USAGE); } @@ -1967,5 +2193,5 @@ valid_utf8(const char *s) /* I - String to check */ /* - * End of "$Id: cupstestppd.c 6430 2007-04-02 14:01:55Z mike $". + * End of "$Id: cupstestppd.c 6444 2007-04-04 22:13:58Z mike $". */ diff --git a/templates/Makefile b/templates/Makefile index 908e622b3c..01722ab661 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -1,9 +1,9 @@ # -# "$Id: Makefile 5877 2006-08-24 15:54:12Z mike $" +# "$Id: Makefile 6440 2007-04-03 23:17:17Z mike $" # # Template makefile for the Common UNIX Printing System (CUPS). # -# Copyright 1993-2006 by Easy Software Products. +# Copyright 1993-2007 by Easy Software Products. # # These coded instructions, statements, and computer programs are the # property of Easy Software Products and are protected by Federal @@ -58,6 +58,7 @@ FILES = \ job-restart.tmpl \ jobs.tmpl \ jobs-header.tmpl \ + list-available-printers.tmpl \ maintenance.tmpl \ modify-class.tmpl \ modify-printer.tmpl \ @@ -156,5 +157,5 @@ uninstall-languages: # -# End of "$Id: Makefile 5877 2006-08-24 15:54:12Z mike $". +# End of "$Id: Makefile 6440 2007-04-03 23:17:17Z mike $". # diff --git a/templates/admin.tmpl b/templates/admin.tmpl index 137e26475b..91aafc4691 100644 --- a/templates/admin.tmpl +++ b/templates/admin.tmpl @@ -6,6 +6,9 @@

Add Printer + Manage Printers {have_samba?Export Printers to Samba:}

-{#device_uri=0?:

New Printers Found:

    {[device_uri] -
  • Add This Printer -{device_make_and_model} ({device_info})
  • -}
} -

Classes

diff --git a/templates/classes.tmpl b/templates/classes.tmpl index bc87f9f58a..8e17c3996e 100644 --- a/templates/classes.tmpl +++ b/templates/classes.tmpl @@ -42,6 +42,8 @@ } Modify Class + +Set Printer Options Delete Class diff --git a/templates/list-available-printers.tmpl b/templates/list-available-printers.tmpl new file mode 100644 index 0000000000..bd7ff88595 --- /dev/null +++ b/templates/list-available-printers.tmpl @@ -0,0 +1,10 @@ +

Available Printers

+ +{#device_uri=0?

No printers found.

+:
} + diff --git a/templates/printer-configured.tmpl b/templates/printer-configured.tmpl index 7ec86879ad..3f6b08b707 100644 --- a/templates/printer-configured.tmpl +++ b/templates/printer-configured.tmpl @@ -1,2 +1,2 @@ -

Printer {printer_name} has +

{OP=set-class-options?Class :Printer }{printer_name} has been configured successfully. diff --git a/tools/buttons.txt b/tools/buttons.txt index 51cc4e4097..de5defd293 100644 --- a/tools/buttons.txt +++ b/tools/buttons.txt @@ -22,6 +22,7 @@ button-manage-printers.gif standard Manage Printers button-manage-server.gif standard Manage Server button-modify-class.gif standard Modify Class button-modify-printer.gif standard Modify Printer +button-list-available-printers.gif standard List Available Printers button-move-job.gif standard Move Job button-move-jobs.gif standard Move All Jobs button-print-self-test-page.gif standard Print Self-Test Page