You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1449 lines
40 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

/*
* Administration utility API definitions for CUPS.
*
* Copyright © 2007-2019 by Apple Inc.
* Copyright © 2001-2007 by Easy Software Products.
*
* Licensed under Apache License v2.0. See the file "LICENSE" for more
* information.
*/
/*
* Include necessary headers...
*/
#include "cups-private.h"
#include "debug-internal.h"
#include "ppd.h"
#include "adminutil.h"
#include <fcntl.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
# include <sys/wait.h>
#endif /* !_WIN32 */
/*
* Local functions...
*/
static http_status_t get_cupsd_conf(http_t *http, _cups_globals_t *cg,
time_t last_update, char *name,
size_t namelen, int *remote);
static void invalidate_cupsd_cache(_cups_globals_t *cg);
/*
* 'cupsAdminCreateWindowsPPD()' - Create the Windows PPD file for a printer.
*
* @deprecated@
*/
char * /* O - PPD file or NULL */
cupsAdminCreateWindowsPPD(
http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
const char *dest, /* I - Printer or class */
char *buffer, /* I - Filename buffer */
int bufsize) /* I - Size of filename buffer */
{
(void)http;
(void)dest;
(void)bufsize;
if (buffer)
*buffer = '\0';
return (NULL);
}
/*
* 'cupsAdminExportSamba()' - Export a printer to Samba.
*
* @deprecated@
*/
int /* O - 1 on success, 0 on failure */
cupsAdminExportSamba(
const char *dest, /* I - Destination to export */
const char *ppd, /* I - PPD file */
const char *samba_server, /* I - Samba server */
const char *samba_user, /* I - Samba username */
const char *samba_password, /* I - Samba password */
FILE *logfile) /* I - Log file, if any */
{
(void)dest;
(void)ppd;
(void)samba_server;
(void)samba_user;
(void)samba_password;
(void)logfile;
return (0);
}
/*
* 'cupsAdminGetServerSettings()' - Get settings from the server.
*
* The returned settings should be freed with cupsFreeOptions() when
* you are done with them.
*
* @since CUPS 1.3/macOS 10.5@
*/
int /* O - 1 on success, 0 on failure */
cupsAdminGetServerSettings(
http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
int *num_settings, /* O - Number of settings */
cups_option_t **settings) /* O - Settings */
{
int i; /* Looping var */
cups_file_t *cupsd; /* cupsd.conf file */
char cupsdconf[1024]; /* cupsd.conf filename */
int remote; /* Remote cupsd.conf file? */
http_status_t status; /* Status of getting cupsd.conf */
char line[1024], /* Line from cupsd.conf file */
*value; /* Value on line */
cups_option_t *setting; /* Current setting */
_cups_globals_t *cg = _cupsGlobals(); /* Global data */
/*
* Range check input...
*/
if (!http)
{
/*
* See if we are connected to the same server...
*/
if (cg->http)
{
/*
* Compare the connection hostname, port, and encryption settings to
* the cached defaults; these were initialized the first time we
* connected...
*/
if (strcmp(cg->http->hostname, cg->server) ||
cg->ipp_port != httpAddrPort(cg->http->hostaddr) ||
(cg->http->encryption != cg->encryption &&
cg->http->encryption == HTTP_ENCRYPTION_NEVER))
{
/*
* Need to close the current connection because something has changed...
*/
httpClose(cg->http);
cg->http = NULL;
}
}
/*
* (Re)connect as needed...
*/
if (!cg->http)
{
if ((cg->http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
cupsEncryption(), 1, 0, NULL)) == NULL)
{
if (errno)
_cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE, NULL, 0);
else
_cupsSetError(IPP_STATUS_ERROR_SERVICE_UNAVAILABLE,
_("Unable to connect to host."), 1);
if (num_settings)
*num_settings = 0;
if (settings)
*settings = NULL;
return (0);
}
}
http = cg->http;
}
if (!http || !num_settings || !settings)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
if (num_settings)
*num_settings = 0;
if (settings)
*settings = NULL;
return (0);
}
*num_settings = 0;
*settings = NULL;
/*
* Get the cupsd.conf file...
*/
if ((status = get_cupsd_conf(http, cg, cg->cupsd_update, cupsdconf,
sizeof(cupsdconf), &remote)) == HTTP_STATUS_OK)
{
if ((cupsd = cupsFileOpen(cupsdconf, "r")) == NULL)
{
char message[1024]; /* Message string */
snprintf(message, sizeof(message),
_cupsLangString(cupsLangDefault(), _("Open of %s failed: %s")),
cupsdconf, strerror(errno));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
}
}
else
cupsd = NULL;
if (cupsd)
{
/*
* Read the file, keeping track of what settings are enabled...
*/
int remote_access = 0, /* Remote access allowed? */
remote_admin = 0, /* Remote administration allowed? */
remote_any = 0, /* Remote access from anywhere allowed? */
browsing = 1, /* Browsing enabled? */
cancel_policy = 1, /* Cancel-job policy set? */
debug_logging = 0; /* LogLevel debug set? */
int linenum = 0, /* Line number in file */
in_location = 0, /* In a location section? */
in_policy = 0, /* In a policy section? */
in_cancel_job = 0, /* In a cancel-job section? */
in_admin_location = 0; /* In the /admin location? */
invalidate_cupsd_cache(cg);
cg->cupsd_update = time(NULL);
httpGetHostname(http, cg->cupsd_hostname, sizeof(cg->cupsd_hostname));
while (cupsFileGetConf(cupsd, line, sizeof(line), &value, &linenum))
{
if (!value && strncmp(line, "</", 2))
value = line + strlen(line);
if ((!_cups_strcasecmp(line, "Port") || !_cups_strcasecmp(line, "Listen")) && value)
{
char *port; /* Pointer to port number, if any */
if ((port = strrchr(value, ':')) != NULL)
*port = '\0';
else if (isdigit(*value & 255))
{
/*
* Listen on a port number implies remote access...
*/
remote_access = 1;
continue;
}
if (_cups_strcasecmp(value, "localhost") && strcmp(value, "127.0.0.1")
#ifdef AF_LOCAL
&& *value != '/'
#endif /* AF_LOCAL */
#ifdef AF_INET6
&& strcmp(value, "[::1]")
#endif /* AF_INET6 */
)
remote_access = 1;
}
else if (!_cups_strcasecmp(line, "Browsing"))
{
browsing = !_cups_strcasecmp(value, "yes") ||
!_cups_strcasecmp(value, "on") ||
!_cups_strcasecmp(value, "true");
}
else if (!_cups_strcasecmp(line, "LogLevel"))
{
debug_logging = !_cups_strncasecmp(value, "debug", 5);
}
else if (!_cups_strcasecmp(line, "<Policy") &&
!_cups_strcasecmp(value, "default"))
{
in_policy = 1;
}
else if (!_cups_strcasecmp(line, "</Policy>"))
{
in_policy = 0;
}
else if (!_cups_strcasecmp(line, "<Limit") && in_policy && value)
{
/*
* See if the policy limit is for the Cancel-Job operation...
*/
char *valptr; /* Pointer into value */
while (*value)
{
for (valptr = value; *valptr && !_cups_isspace(*valptr); valptr ++);
if (*valptr)
*valptr++ = '\0';
if (!_cups_strcasecmp(value, "cancel-job") ||
!_cups_strcasecmp(value, "all"))
{
in_cancel_job = 1;
break;
}
for (value = valptr; _cups_isspace(*value); value ++);
}
}
else if (!_cups_strcasecmp(line, "</Limit>"))
{
in_cancel_job = 0;
}
else if (!_cups_strcasecmp(line, "Require") && in_cancel_job)
{
cancel_policy = 0;
}
else if (!_cups_strcasecmp(line, "<Location") && value)
{
in_admin_location = !_cups_strcasecmp(value, "/admin");
in_location = 1;
}
else if (!_cups_strcasecmp(line, "</Location>"))
{
in_admin_location = 0;
in_location = 0;
}
else if (!_cups_strcasecmp(line, "Allow") && value &&
_cups_strcasecmp(value, "localhost") &&
_cups_strcasecmp(value, "127.0.0.1")
#ifdef AF_LOCAL
&& *value != '/'
#endif /* AF_LOCAL */
#ifdef AF_INET6
&& strcmp(value, "::1")
#endif /* AF_INET6 */
)
{
if (in_admin_location)
remote_admin = 1;
else if (!_cups_strcasecmp(value, "all"))
remote_any = 1;
}
else if (line[0] != '<' && !in_location && !in_policy &&
_cups_strcasecmp(line, "Allow") &&
_cups_strcasecmp(line, "AuthType") &&
_cups_strcasecmp(line, "Deny") &&
_cups_strcasecmp(line, "Order") &&
_cups_strcasecmp(line, "Require") &&
_cups_strcasecmp(line, "Satisfy"))
cg->cupsd_num_settings = cupsAddOption(line, value,
cg->cupsd_num_settings,
&(cg->cupsd_settings));
}
cupsFileClose(cupsd);
cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
debug_logging ? "1" : "0",
cg->cupsd_num_settings,
&(cg->cupsd_settings));
cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
(remote_access && remote_admin) ?
"1" : "0",
cg->cupsd_num_settings,
&(cg->cupsd_settings));
cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
remote_any ? "1" : "0",
cg->cupsd_num_settings,
&(cg->cupsd_settings));
cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
(remote_access && browsing) ? "1" :
"0",
cg->cupsd_num_settings,
&(cg->cupsd_settings));
cg->cupsd_num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
cancel_policy ? "1" : "0",
cg->cupsd_num_settings,
&(cg->cupsd_settings));
}
else if (status != HTTP_STATUS_NOT_MODIFIED)
invalidate_cupsd_cache(cg);
/*
* Remove any temporary files and copy the settings array...
*/
if (remote)
unlink(cupsdconf);
for (i = cg->cupsd_num_settings, setting = cg->cupsd_settings;
i > 0;
i --, setting ++)
*num_settings = cupsAddOption(setting->name, setting->value,
*num_settings, settings);
return (cg->cupsd_num_settings > 0);
}
/*
* 'cupsAdminSetServerSettings()' - Set settings on the server.
*
* @since CUPS 1.3/macOS 10.5@
*/
int /* O - 1 on success, 0 on failure */
cupsAdminSetServerSettings(
http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
int num_settings, /* I - Number of settings */
cups_option_t *settings) /* I - Settings */
{
int i; /* Looping var */
http_status_t status; /* GET/PUT status */
const char *server_port_env; /* SERVER_PORT env var */
int server_port; /* IPP port for server */
cups_file_t *cupsd; /* cupsd.conf file */
char cupsdconf[1024]; /* cupsd.conf filename */
int remote; /* Remote cupsd.conf file? */
char tempfile[1024]; /* Temporary new cupsd.conf */
cups_file_t *temp; /* Temporary file */
char line[1024], /* Line from cupsd.conf file */
*value; /* Value on line */
int linenum, /* Line number in file */
in_location, /* In a location section? */
in_policy, /* In a policy section? */
in_default_policy, /* In the default policy section? */
in_cancel_job, /* In a cancel-job section? */
in_admin_location, /* In the /admin location? */
in_conf_location, /* In the /admin/conf location? */
in_log_location, /* In the /admin/log location? */
in_root_location; /* In the / location? */
const char *val; /* Setting value */
int share_printers, /* Share local printers */
remote_admin, /* Remote administration allowed? */
remote_any, /* Remote access from anywhere? */
user_cancel_any, /* Cancel-job policy set? */
debug_logging; /* LogLevel debug set? */
int wrote_port_listen, /* Wrote the port/listen lines? */
wrote_browsing, /* Wrote the browsing lines? */
wrote_policy, /* Wrote the policy? */
wrote_loglevel, /* Wrote the LogLevel line? */
wrote_admin_location, /* Wrote the /admin location? */
wrote_conf_location, /* Wrote the /admin/conf location? */
wrote_log_location, /* Wrote the /admin/log location? */
wrote_root_location; /* Wrote the / location? */
int indent; /* Indentation */
int cupsd_num_settings; /* New number of settings */
int old_share_printers, /* Share local printers */
old_remote_admin, /* Remote administration allowed? */
old_remote_any, /* Remote access from anywhere? */
old_user_cancel_any, /* Cancel-job policy set? */
old_debug_logging; /* LogLevel debug set? */
cups_option_t *cupsd_settings, /* New settings */
*setting; /* Current setting */
_cups_globals_t *cg = _cupsGlobals(); /* Global data */
/*
* Range check input...
*/
if (!http)
http = _cupsConnect();
if (!http || !num_settings || !settings)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
return (0);
}
/*
* Get the cupsd.conf file...
*/
if (get_cupsd_conf(http, cg, 0, cupsdconf, sizeof(cupsdconf),
&remote) == HTTP_STATUS_OK)
{
if ((cupsd = cupsFileOpen(cupsdconf, "r")) == NULL)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
return (0);
}
}
else
return (0);
/*
* Get current settings...
*/
if (!cupsAdminGetServerSettings(http, &cupsd_num_settings,
&cupsd_settings))
return (0);
if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, cupsd_num_settings,
cupsd_settings)) != NULL)
old_debug_logging = atoi(val);
else
old_debug_logging = 0;
DEBUG_printf(("1cupsAdminSetServerSettings: old debug_logging=%d",
old_debug_logging));
if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, cupsd_num_settings,
cupsd_settings)) != NULL)
old_remote_admin = atoi(val);
else
old_remote_admin = 0;
DEBUG_printf(("1cupsAdminSetServerSettings: old remote_admin=%d",
old_remote_admin));
if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, cupsd_num_settings,
cupsd_settings)) != NULL)
old_remote_any = atoi(val);
else
old_remote_any = 0;
DEBUG_printf(("1cupsAdminSetServerSettings: old remote_any=%d",
old_remote_any));
if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, cupsd_num_settings,
cupsd_settings)) != NULL)
old_share_printers = atoi(val);
else
old_share_printers = 0;
DEBUG_printf(("1cupsAdminSetServerSettings: old share_printers=%d",
old_share_printers));
if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, cupsd_num_settings,
cupsd_settings)) != NULL)
old_user_cancel_any = atoi(val);
else
old_user_cancel_any = 0;
DEBUG_printf(("1cupsAdminSetServerSettings: old user_cancel_any=%d",
old_user_cancel_any));
cupsFreeOptions(cupsd_num_settings, cupsd_settings);
/*
* Get basic settings...
*/
if ((val = cupsGetOption(CUPS_SERVER_DEBUG_LOGGING, num_settings,
settings)) != NULL)
{
debug_logging = atoi(val);
if (debug_logging == old_debug_logging)
{
/*
* No change to this setting...
*/
debug_logging = -1;
}
}
else
debug_logging = -1;
DEBUG_printf(("1cupsAdminSetServerSettings: debug_logging=%d",
debug_logging));
if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ANY, num_settings, settings)) != NULL)
{
remote_any = atoi(val);
if (remote_any == old_remote_any)
{
/*
* No change to this setting...
*/
remote_any = -1;
}
}
else
remote_any = -1;
DEBUG_printf(("1cupsAdminSetServerSettings: remote_any=%d", remote_any));
if ((val = cupsGetOption(CUPS_SERVER_REMOTE_ADMIN, num_settings,
settings)) != NULL)
{
remote_admin = atoi(val);
if (remote_admin == old_remote_admin)
{
/*
* No change to this setting...
*/
remote_admin = -1;
}
}
else
remote_admin = -1;
DEBUG_printf(("1cupsAdminSetServerSettings: remote_admin=%d",
remote_admin));
if ((val = cupsGetOption(CUPS_SERVER_SHARE_PRINTERS, num_settings,
settings)) != NULL)
{
share_printers = atoi(val);
if (share_printers == old_share_printers)
{
/*
* No change to this setting...
*/
share_printers = -1;
}
}
else
share_printers = -1;
DEBUG_printf(("1cupsAdminSetServerSettings: share_printers=%d",
share_printers));
if ((val = cupsGetOption(CUPS_SERVER_USER_CANCEL_ANY, num_settings,
settings)) != NULL)
{
user_cancel_any = atoi(val);
if (user_cancel_any == old_user_cancel_any)
{
/*
* No change to this setting...
*/
user_cancel_any = -1;
}
}
else
user_cancel_any = -1;
DEBUG_printf(("1cupsAdminSetServerSettings: user_cancel_any=%d",
user_cancel_any));
/*
* Create a temporary file for the new cupsd.conf file...
*/
if ((temp = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
{
cupsFileClose(cupsd);
if (remote)
unlink(cupsdconf);
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
return (0);
}
/*
* Copy the old file to the new, making changes along the way...
*/
cupsd_num_settings = 0;
in_admin_location = 0;
in_cancel_job = 0;
in_conf_location = 0;
in_default_policy = 0;
in_location = 0;
in_log_location = 0;
in_policy = 0;
in_root_location = 0;
linenum = 0;
wrote_admin_location = 0;
wrote_browsing = 0;
wrote_conf_location = 0;
wrote_log_location = 0;
wrote_loglevel = 0;
wrote_policy = 0;
wrote_port_listen = 0;
wrote_root_location = 0;
indent = 0;
if ((server_port_env = getenv("SERVER_PORT")) != NULL)
{
if ((server_port = atoi(server_port_env)) <= 0)
server_port = ippPort();
}
else
server_port = ippPort();
if (server_port <= 0)
server_port = IPP_PORT;
while (cupsFileGetConf(cupsd, line, sizeof(line), &value, &linenum))
{
if ((!_cups_strcasecmp(line, "Port") || !_cups_strcasecmp(line, "Listen")) &&
(remote_admin >= 0 || remote_any >= 0 || share_printers >= 0))
{
if (!wrote_port_listen)
{
wrote_port_listen = 1;
if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
{
cupsFilePuts(temp, "# Allow remote access\n");
cupsFilePrintf(temp, "Port %d\n", server_port);
}
else
{
cupsFilePuts(temp, "# Only listen for connections from the local "
"machine.\n");
cupsFilePrintf(temp, "Listen localhost:%d\n", server_port);
}
#ifdef CUPS_DEFAULT_DOMAINSOCKET
if ((!value || strcmp(CUPS_DEFAULT_DOMAINSOCKET, value)) &&
!access(CUPS_DEFAULT_DOMAINSOCKET, 0))
cupsFilePuts(temp, "Listen " CUPS_DEFAULT_DOMAINSOCKET "\n");
#endif /* CUPS_DEFAULT_DOMAINSOCKET */
}
else if (value && value[0] == '/'
#ifdef CUPS_DEFAULT_DOMAINSOCKET
&& strcmp(CUPS_DEFAULT_DOMAINSOCKET, value)
#endif /* CUPS_DEFAULT_DOMAINSOCKET */
)
cupsFilePrintf(temp, "Listen %s\n", value);
}
else if ((!_cups_strcasecmp(line, "Browsing") ||
!_cups_strcasecmp(line, "BrowseLocalProtocols")) &&
share_printers >= 0)
{
if (!wrote_browsing)
{
wrote_browsing = 1;
if (share_printers)
{
const char *localp = cupsGetOption("BrowseLocalProtocols",
num_settings, settings);
if (!localp || !localp[0])
localp = cupsGetOption("BrowseLocalProtocols", cupsd_num_settings,
cupsd_settings);
cupsFilePuts(temp, "# Share local printers on the local network.\n");
cupsFilePuts(temp, "Browsing On\n");
if (!localp)
localp = CUPS_DEFAULT_BROWSE_LOCAL_PROTOCOLS;
cupsFilePrintf(temp, "BrowseLocalProtocols %s\n", localp);
cupsd_num_settings = cupsAddOption("BrowseLocalProtocols", localp,
cupsd_num_settings,
&cupsd_settings);
}
else
{
cupsFilePuts(temp, "# Disable printer sharing.\n");
cupsFilePuts(temp, "Browsing Off\n");
}
}
}
else if (!_cups_strcasecmp(line, "LogLevel") && debug_logging >= 0)
{
wrote_loglevel = 1;
if (debug_logging)
{
cupsFilePuts(temp,
"# Show troubleshooting information in error_log.\n");
cupsFilePuts(temp, "LogLevel debug\n");
}
else
{
cupsFilePuts(temp, "# Show general information in error_log.\n");
cupsFilePuts(temp, "LogLevel " CUPS_DEFAULT_LOG_LEVEL "\n");
}
}
else if (!_cups_strcasecmp(line, "<Policy"))
{
in_default_policy = !_cups_strcasecmp(value, "default");
in_policy = 1;
cupsFilePrintf(temp, "%s %s>\n", line, value);
indent += 2;
}
else if (!_cups_strcasecmp(line, "</Policy>"))
{
indent -= 2;
if (!wrote_policy && in_default_policy)
{
wrote_policy = 1;
if (!user_cancel_any)
cupsFilePuts(temp, " # Only the owner or an administrator can "
"cancel a job...\n"
" <Limit Cancel-Job>\n"
" Order deny,allow\n"
" Require user @OWNER "
CUPS_DEFAULT_PRINTOPERATOR_AUTH "\n"
" </Limit>\n");
}
in_policy = 0;
in_default_policy = 0;
cupsFilePuts(temp, "</Policy>\n");
}
else if (!_cups_strcasecmp(line, "<Location"))
{
in_location = 1;
indent += 2;
if (!strcmp(value, "/admin"))
in_admin_location = 1;
else if (!strcmp(value, "/admin/conf"))
in_conf_location = 1;
else if (!strcmp(value, "/admin/log"))
in_log_location = 1;
else if (!strcmp(value, "/"))
in_root_location = 1;
cupsFilePrintf(temp, "%s %s>\n", line, value);
}
else if (!_cups_strcasecmp(line, "</Location>"))
{
in_location = 0;
indent -= 2;
if (in_admin_location && remote_admin >= 0)
{
wrote_admin_location = 1;
if (remote_admin)
cupsFilePuts(temp, " # Allow remote administration...\n");
else if (remote_admin == 0)
cupsFilePuts(temp, " # Restrict access to the admin pages...\n");
cupsFilePuts(temp, " Order allow,deny\n");
if (remote_admin)
cupsFilePrintf(temp, " Allow %s\n",
remote_any > 0 ? "all" : "@LOCAL");
}
else if (in_conf_location && remote_admin >= 0)
{
wrote_conf_location = 1;
if (remote_admin)
cupsFilePuts(temp, " # Allow remote access to the configuration "
"files...\n");
else
cupsFilePuts(temp, " # Restrict access to the configuration "
"files...\n");
cupsFilePuts(temp, " Order allow,deny\n");
if (remote_admin)
cupsFilePrintf(temp, " Allow %s\n",
remote_any > 0 ? "all" : "@LOCAL");
}
else if (in_log_location && remote_admin >= 0)
{
wrote_log_location = 1;
if (remote_admin)
cupsFilePuts(temp, " # Allow remote access to the log "
"files...\n");
else
cupsFilePuts(temp, " # Restrict access to the log "
"files...\n");
cupsFilePuts(temp, " Order allow,deny\n");
if (remote_admin)
cupsFilePrintf(temp, " Allow %s\n",
remote_any > 0 ? "all" : "@LOCAL");
}
else if (in_root_location &&
(remote_admin >= 0 || remote_any >= 0 || share_printers >= 0))
{
wrote_root_location = 1;
if (remote_admin > 0 && share_printers > 0)
cupsFilePuts(temp, " # Allow shared printing and remote "
"administration...\n");
else if (remote_admin > 0)
cupsFilePuts(temp, " # Allow remote administration...\n");
else if (share_printers > 0)
cupsFilePuts(temp, " # Allow shared printing...\n");
else if (remote_any > 0)
cupsFilePuts(temp, " # Allow remote access...\n");
else
cupsFilePuts(temp, " # Restrict access to the server...\n");
cupsFilePuts(temp, " Order allow,deny\n");
if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
cupsFilePrintf(temp, " Allow %s\n",
remote_any > 0 ? "all" : "@LOCAL");
}
in_admin_location = 0;
in_conf_location = 0;
in_log_location = 0;
in_root_location = 0;
cupsFilePuts(temp, "</Location>\n");
}
else if (!_cups_strcasecmp(line, "<Limit"))
{
if (in_default_policy)
{
/*
* See if the policy limit is for the Cancel-Job operation...
*/
char *valptr; /* Pointer into value */
if (!_cups_strcasecmp(value, "cancel-job") && user_cancel_any >= 0)
{
/*
* Don't write anything for this limit section...
*/
in_cancel_job = 2;
}
else
{
cupsFilePrintf(temp, "%*s%s", indent, "", line);
while (*value)
{
for (valptr = value; *valptr && !_cups_isspace(*valptr); valptr ++);
if (*valptr)
*valptr++ = '\0';
if (!_cups_strcasecmp(value, "cancel-job") && user_cancel_any >= 0)
{
/*
* Write everything except for this definition...
*/
in_cancel_job = 1;
}
else
cupsFilePrintf(temp, " %s", value);
for (value = valptr; _cups_isspace(*value); value ++);
}
cupsFilePuts(temp, ">\n");
}
}
else
cupsFilePrintf(temp, "%*s%s %s>\n", indent, "", line, value);
indent += 2;
}
else if (!_cups_strcasecmp(line, "</Limit>") && in_cancel_job)
{
indent -= 2;
if (in_cancel_job == 1)
cupsFilePuts(temp, " </Limit>\n");
wrote_policy = 1;
if (!user_cancel_any)
cupsFilePuts(temp, " # Only the owner or an administrator can cancel "
"a job...\n"
" <Limit Cancel-Job>\n"
" Order deny,allow\n"
" Require user @OWNER "
CUPS_DEFAULT_PRINTOPERATOR_AUTH "\n"
" </Limit>\n");
in_cancel_job = 0;
}
else if ((((in_admin_location || in_conf_location || in_root_location || in_log_location) &&
(remote_admin >= 0 || remote_any >= 0)) ||
(in_root_location && share_printers >= 0)) &&
(!_cups_strcasecmp(line, "Allow") || !_cups_strcasecmp(line, "Deny") ||
!_cups_strcasecmp(line, "Order")))
continue;
else if (in_cancel_job == 2)
continue;
else if (line[0] == '<')
{
if (value)
{
cupsFilePrintf(temp, "%*s%s %s>\n", indent, "", line, value);
indent += 2;
}
else
{
if (line[1] == '/')
indent -= 2;
cupsFilePrintf(temp, "%*s%s\n", indent, "", line);
}
}
else if (!in_policy && !in_location &&
(val = cupsGetOption(line, num_settings, settings)) != NULL)
{
/*
* Replace this directive's value with the new one...
*/
cupsd_num_settings = cupsAddOption(line, val, cupsd_num_settings,
&cupsd_settings);
/*
* Write the new value in its place, without indentation since we
* only support setting root directives, not in sections...
*/
cupsFilePrintf(temp, "%s %s\n", line, val);
}
else if (value)
{
if (!in_policy && !in_location)
{
/*
* Record the non-policy, non-location directives that we find
* in the server settings, since we cache this info and record it
* in cupsAdminGetServerSettings()...
*/
cupsd_num_settings = cupsAddOption(line, value, cupsd_num_settings,
&cupsd_settings);
}
cupsFilePrintf(temp, "%*s%s %s\n", indent, "", line, value);
}
else
cupsFilePrintf(temp, "%*s%s\n", indent, "", line);
}
/*
* Write any missing info...
*/
if (!wrote_browsing && share_printers >= 0)
{
if (share_printers > 0)
{
cupsFilePuts(temp, "# Share local printers on the local network.\n");
cupsFilePuts(temp, "Browsing On\n");
}
else
{
cupsFilePuts(temp, "# Disable printer sharing and shared printers.\n");
cupsFilePuts(temp, "Browsing Off\n");
}
}
if (!wrote_loglevel && debug_logging >= 0)
{
if (debug_logging)
{
cupsFilePuts(temp, "# Show troubleshooting information in error_log.\n");
cupsFilePuts(temp, "LogLevel debug\n");
}
else
{
cupsFilePuts(temp, "# Show general information in error_log.\n");
cupsFilePuts(temp, "LogLevel " CUPS_DEFAULT_LOG_LEVEL "\n");
}
}
if (!wrote_port_listen &&
(remote_admin >= 0 || remote_any >= 0 || share_printers >= 0))
{
if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
{
cupsFilePuts(temp, "# Allow remote access\n");
cupsFilePrintf(temp, "Port %d\n", ippPort());
}
else
{
cupsFilePuts(temp,
"# Only listen for connections from the local machine.\n");
cupsFilePrintf(temp, "Listen localhost:%d\n", ippPort());
}
#ifdef CUPS_DEFAULT_DOMAINSOCKET
if (!access(CUPS_DEFAULT_DOMAINSOCKET, 0))
cupsFilePuts(temp, "Listen " CUPS_DEFAULT_DOMAINSOCKET "\n");
#endif /* CUPS_DEFAULT_DOMAINSOCKET */
}
if (!wrote_root_location &&
(remote_admin >= 0 || remote_any >= 0 || share_printers >= 0))
{
if (remote_admin > 0 && share_printers > 0)
cupsFilePuts(temp,
"# Allow shared printing and remote administration...\n");
else if (remote_admin > 0)
cupsFilePuts(temp, "# Allow remote administration...\n");
else if (share_printers > 0)
cupsFilePuts(temp, "# Allow shared printing...\n");
else if (remote_any > 0)
cupsFilePuts(temp, "# Allow remote access...\n");
else
cupsFilePuts(temp, "# Restrict access to the server...\n");
cupsFilePuts(temp, "<Location />\n"
" Order allow,deny\n");
if (remote_admin > 0 || remote_any > 0 || share_printers > 0)
cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
cupsFilePuts(temp, "</Location>\n");
}
if (!wrote_admin_location && remote_admin >= 0)
{
if (remote_admin)
cupsFilePuts(temp, "# Allow remote administration...\n");
else
cupsFilePuts(temp, "# Restrict access to the admin pages...\n");
cupsFilePuts(temp, "<Location /admin>\n"
" Order allow,deny\n");
if (remote_admin)
cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
cupsFilePuts(temp, "</Location>\n");
}
if (!wrote_conf_location && remote_admin >= 0)
{
if (remote_admin)
cupsFilePuts(temp,
"# Allow remote access to the configuration files...\n");
else
cupsFilePuts(temp, "# Restrict access to the configuration files...\n");
cupsFilePuts(temp, "<Location /admin/conf>\n"
" AuthType Default\n"
" Require user @SYSTEM\n"
" Order allow,deny\n");
if (remote_admin)
cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
cupsFilePuts(temp, "</Location>\n");
}
if (!wrote_log_location && remote_admin >= 0)
{
if (remote_admin)
cupsFilePuts(temp,
"# Allow remote access to the log files...\n");
else
cupsFilePuts(temp, "# Restrict access to the log files...\n");
cupsFilePuts(temp, "<Location /admin/log>\n"
" AuthType Default\n"
" Require user @SYSTEM\n"
" Order allow,deny\n");
if (remote_admin)
cupsFilePrintf(temp, " Allow %s\n", remote_any > 0 ? "all" : "@LOCAL");
cupsFilePuts(temp, "</Location>\n");
}
if (!wrote_policy && user_cancel_any >= 0)
{
cupsFilePuts(temp, "<Policy default>\n"
" # Job-related operations must be done by the owner "
"or an administrator...\n"
" <Limit Send-Document Send-URI Hold-Job Release-Job "
"Restart-Job Purge-Jobs Set-Job-Attributes "
"Create-Job-Subscription Renew-Subscription "
"Cancel-Subscription Get-Notifications Reprocess-Job "
"Cancel-Current-Job Suspend-Current-Job Resume-Job "
"CUPS-Move-Job>\n"
" Require user @OWNER @SYSTEM\n"
" Order deny,allow\n"
" </Limit>\n"
" # All administration operations require an "
"administrator to authenticate...\n"
" <Limit Pause-Printer Resume-Printer "
"Set-Printer-Attributes Enable-Printer "
"Disable-Printer Pause-Printer-After-Current-Job "
"Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer "
"Activate-Printer Restart-Printer Shutdown-Printer "
"Startup-Printer Promote-Job Schedule-Job-After "
"CUPS-Add-Printer CUPS-Delete-Printer "
"CUPS-Add-Class CUPS-Delete-Class "
"CUPS-Accept-Jobs CUPS-Reject-Jobs "
"CUPS-Set-Default CUPS-Add-Device CUPS-Delete-Device>\n"
" AuthType Default\n"
" Require user @SYSTEM\n"
" Order deny,allow\n"
"</Limit>\n");
if (!user_cancel_any)
cupsFilePuts(temp, " # Only the owner or an administrator can cancel "
"a job...\n"
" <Limit Cancel-Job>\n"
" Order deny,allow\n"
" Require user @OWNER "
CUPS_DEFAULT_PRINTOPERATOR_AUTH "\n"
" </Limit>\n");
cupsFilePuts(temp, " <Limit All>\n"
" Order deny,allow\n"
" </Limit>\n"
"</Policy>\n");
}
for (i = num_settings, setting = settings; i > 0; i --, setting ++)
if (setting->name[0] != '_' &&
_cups_strcasecmp(setting->name, "Listen") &&
_cups_strcasecmp(setting->name, "Port") &&
!cupsGetOption(setting->name, cupsd_num_settings, cupsd_settings))
{
/*
* Add this directive to the list of directives we have written...
*/
cupsd_num_settings = cupsAddOption(setting->name, setting->value,
cupsd_num_settings, &cupsd_settings);
/*
* Write the new value, without indentation since we only support
* setting root directives, not in sections...
*/
cupsFilePrintf(temp, "%s %s\n", setting->name, setting->value);
}
cupsFileClose(cupsd);
cupsFileClose(temp);
/*
* Upload the configuration file to the server...
*/
status = cupsPutFile(http, "/admin/conf/cupsd.conf", tempfile);
if (status == HTTP_STATUS_CREATED)
{
/*
* Updated OK, add the basic settings...
*/
if (debug_logging >= 0)
cupsd_num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
debug_logging ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
else
cupsd_num_settings = cupsAddOption(CUPS_SERVER_DEBUG_LOGGING,
old_debug_logging ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
if (remote_admin >= 0)
cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
remote_admin ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
else
cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ADMIN,
old_remote_admin ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
if (remote_any >= 0)
cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
remote_any ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
else
cupsd_num_settings = cupsAddOption(CUPS_SERVER_REMOTE_ANY,
old_remote_any ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
if (share_printers >= 0)
cupsd_num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
share_printers ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
else
cupsd_num_settings = cupsAddOption(CUPS_SERVER_SHARE_PRINTERS,
old_share_printers ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
if (user_cancel_any >= 0)
cupsd_num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
user_cancel_any ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
else
cupsd_num_settings = cupsAddOption(CUPS_SERVER_USER_CANCEL_ANY,
old_user_cancel_any ? "1" : "0",
cupsd_num_settings, &cupsd_settings);
/*
* Save the new values...
*/
invalidate_cupsd_cache(cg);
cg->cupsd_num_settings = cupsd_num_settings;
cg->cupsd_settings = cupsd_settings;
cg->cupsd_update = time(NULL);
httpGetHostname(http, cg->cupsd_hostname, sizeof(cg->cupsd_hostname));
}
else
cupsFreeOptions(cupsd_num_settings, cupsd_settings);
/*
* Remote our temp files and return...
*/
if (remote)
unlink(cupsdconf);
unlink(tempfile);
return (status == HTTP_STATUS_CREATED);
}
/*
* 'get_cupsd_conf()' - Get the current cupsd.conf file.
*/
static http_status_t /* O - Status of request */
get_cupsd_conf(
http_t *http, /* I - Connection to server */
_cups_globals_t *cg, /* I - Global data */
time_t last_update, /* I - Last update time for file */
char *name, /* I - Filename buffer */
size_t namesize, /* I - Size of filename buffer */
int *remote) /* O - Remote file? */
{
int fd; /* Temporary file descriptor */
#ifndef _WIN32
struct stat info; /* cupsd.conf file information */
#endif /* _WIN32 */
http_status_t status; /* Status of getting cupsd.conf */
char host[HTTP_MAX_HOST]; /* Hostname for connection */
/*
* See if we already have the data we need...
*/
httpGetHostname(http, host, sizeof(host));
if (_cups_strcasecmp(cg->cupsd_hostname, host))
invalidate_cupsd_cache(cg);
snprintf(name, namesize, "%s/cupsd.conf", cg->cups_serverroot);
*remote = 0;
#ifndef _WIN32
if (!_cups_strcasecmp(host, "localhost") && !access(name, R_OK))
{
/*
* Read the local file rather than using HTTP...
*/
if (stat(name, &info))
{
char message[1024]; /* Message string */
snprintf(message, sizeof(message),
_cupsLangString(cupsLangDefault(), _("stat of %s failed: %s")),
name, strerror(errno));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0);
*name = '\0';
return (HTTP_STATUS_SERVER_ERROR);
}
else if (last_update && info.st_mtime <= last_update)
status = HTTP_STATUS_NOT_MODIFIED;
else
status = HTTP_STATUS_OK;
}
else
#endif /* !_WIN32 */
{
/*
* Read cupsd.conf via a HTTP GET request...
*/
if ((fd = cupsTempFd(name, (int)namesize)) < 0)
{
*name = '\0';
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
invalidate_cupsd_cache(cg);
return (HTTP_STATUS_SERVER_ERROR);
}
*remote = 1;
httpClearFields(http);
if (last_update)
httpSetField(http, HTTP_FIELD_IF_MODIFIED_SINCE,
httpGetDateString(last_update));
status = cupsGetFd(http, "/admin/conf/cupsd.conf", fd);
close(fd);
if (status != HTTP_STATUS_OK)
{
unlink(name);
*name = '\0';
}
}
return (status);
}
/*
* 'invalidate_cupsd_cache()' - Invalidate the cached cupsd.conf settings.
*/
static void
invalidate_cupsd_cache(
_cups_globals_t *cg) /* I - Global data */
{
cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings);
cg->cupsd_hostname[0] = '\0';
cg->cupsd_update = 0;
cg->cupsd_num_settings = 0;
cg->cupsd_settings = NULL;
}