/* * Administration utility API definitions for CUPS. * * Copyright 2007-2016 by Apple Inc. * Copyright 2001-2007 by Easy Software Products. * * These coded instructions, statements, and computer programs are the * property of Apple Inc. and are protected by Federal copyright * law. Distribution and use rights are outlined in the file "LICENSE.txt" * which should have been included with this file. If this file is * missing or damaged, see the license at "http://www.cups.org/". * * This file is subject to the Apple OS-Developed Software exception. */ /* * Include necessary headers... */ #include "cups-private.h" #include "ppd.h" #include "adminutil.h" #include <fcntl.h> #include <sys/stat.h> #ifdef WIN32 #else # include <unistd.h> # include <sys/wait.h> #endif /* WIN32 */ /* * Local functions... */ static int do_samba_command(const char *command, const char *address, const char *subcommand, const char *authfile, FILE *logfile); 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); static void write_option(cups_file_t *dstfp, int order, const char *name, const char *text, const char *attrname, ipp_attribute_t *suppattr, ipp_attribute_t *defattr, int defval, int valcount); /* * '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 */ { const char *src; /* Source PPD filename */ cups_file_t *srcfp, /* Source PPD file */ *dstfp; /* Destination PPD file */ ipp_t *request, /* IPP request */ *response; /* IPP response */ ipp_attribute_t *suppattr, /* IPP -supported attribute */ *defattr; /* IPP -default attribute */ cups_lang_t *language; /* Current language */ char line[256], /* Line from PPD file */ junk[256], /* Extra junk to throw away */ *ptr, /* Pointer into line */ uri[1024], /* Printer URI */ 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 */ static const char * const pattrs[] = /* Printer attributes we want */ { "job-hold-until-supported", "job-hold-until-default", "job-sheets-supported", "job-sheets-default", "job-priority-supported", "job-priority-default" }; /* * Range check the input... */ if (buffer) *buffer = '\0'; if (!http) http = _cupsConnect(); if (!http || !dest || !buffer || bufsize < 2) return (NULL); /* * Get the PPD file... */ if ((src = cupsGetPPD2(http, dest)) == NULL) return (NULL); /* * Get the supported banner pages, etc. for the printer... */ request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES); httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", 0, "/printers/%s", dest); ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]), NULL, pattrs); /* * Do the request and get back a response... */ response = cupsDoRequest(http, request, "/"); if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING) { unlink(src); return (NULL); } /* * Open the original PPD file... */ if ((srcfp = cupsFileOpen(src, "rb")) == NULL) return (NULL); /* * Create a temporary output file using the destination buffer... */ if ((dstfp = cupsTempFile2(buffer, bufsize)) == NULL) { cupsFileClose(srcfp); unlink(src); return (NULL); } /* * Write a new header explaining that this isn't the original PPD... */ cupsFilePuts(dstfp, "*PPD-Adobe: \"4.3\"\n"); curtime = time(NULL); curdate = gmtime(&curtime); cupsFilePrintf(dstfp, "*%% Modified on %04d%02d%02d%02d%02d%02d+0000 " "for CUPS Windows Driver\n", curdate->tm_year + 1900, curdate->tm_mon + 1, curdate->tm_mday, curdate->tm_hour, curdate->tm_min, curdate->tm_sec); /* * Read the existing PPD file, converting all PJL commands to CUPS * job ticket comments... */ jcloption = 0; jclorder = 0; linenum = 0; language = cupsLangDefault(); while (cupsFileGets(srcfp, line, sizeof(line))) { linenum ++; if (!strncmp(line, "*PPD-Adobe:", 11)) { /* * Already wrote the PPD header... */ continue; } else if (!strncmp(line, "*JCLBegin:", 10) || !strncmp(line, "*JCLToPSInterpreter:", 20) || !strncmp(line, "*JCLEnd:", 8) || !strncmp(line, "*Protocols:", 11)) { /* * Don't use existing JCL keywords; we'll create our own, below... */ cupsFilePrintf(dstfp, "*%% Commented out for CUPS Windows Driver...\n" "*%%%s\n", line + 1); continue; } else if (!strncmp(line, "*JCLOpenUI", 10)) { jcloption = 1; cupsFilePrintf(dstfp, "%s\n", line); } else if (!strncmp(line, "*JCLCloseUI", 11)) { jcloption = 0; cupsFilePrintf(dstfp, "%s\n", line); } else if (jcloption && !strncmp(line, "*OrderDependency:", 17)) { for (ptr = line + 17; _cups_isspace(*ptr); 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)) { if ((ptr = strchr(line, ':')) == NULL) { snprintf(line, sizeof(line), _cupsLangString(language, _("Missing value on line %d.")), linenum); _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, line, 0); cupsFileClose(srcfp); cupsFileClose(dstfp); unlink(src); unlink(buffer); *buffer = '\0'; return (NULL); } if ((ptr = strchr(ptr, '\"')) == NULL) { snprintf(line, sizeof(line), _cupsLangString(language, _("Missing double quote on line %d.")), linenum); _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, line, 0); cupsFileClose(srcfp); cupsFileClose(dstfp); unlink(src); unlink(buffer); *buffer = '\0'; return (NULL); } if (sscanf(line, "*%40s%*[ \t]%40[^:/]", option, choice) != 2) { snprintf(line, sizeof(line), _cupsLangString(language, _("Bad option + choice on line %d.")), linenum); _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, line, 0); cupsFileClose(srcfp); cupsFileClose(dstfp); unlink(src); unlink(buffer); *buffer = '\0'; return (NULL); } if (strchr(ptr + 1, '\"') == NULL) { /* * Skip remaining... */ while (cupsFileGets(srcfp, junk, sizeof(junk)) != NULL) { linenum ++; if (!strncmp(junk, "*End", 4)) break; } } snprintf(ptr + 1, sizeof(line) - (size_t)(ptr - line + 1), "%%cupsJobTicket: %s=%s\n\"\n*End", option, choice); cupsFilePrintf(dstfp, "*%% Changed for CUPS Windows Driver...\n%s\n", line); } else cupsFilePrintf(dstfp, "%s\n", line); } cupsFileClose(srcfp); unlink(src); if (linenum == 0) { _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_FORMAT_ERROR, _("Empty PPD file."), 1); cupsFileClose(dstfp); unlink(buffer); *buffer = '\0'; return (NULL); } /* * Now add the CUPS-specific attributes and options... */ cupsFilePuts(dstfp, "\n*% CUPS Job Ticket support and options...\n"); cupsFilePuts(dstfp, "*Protocols: PJL\n"); cupsFilePuts(dstfp, "*JCLBegin: \"%!PS-Adobe-3.0<0A>\"\n"); cupsFilePuts(dstfp, "*JCLToPSInterpreter: \"\"\n"); cupsFilePuts(dstfp, "*JCLEnd: \"\"\n"); cupsFilePuts(dstfp, "\n*OpenGroup: CUPS/CUPS Options\n\n"); if ((defattr = ippFindAttribute(response, "job-hold-until-default", IPP_TAG_ZERO)) != NULL && (suppattr = ippFindAttribute(response, "job-hold-until-supported", IPP_TAG_ZERO)) != NULL) 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, 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, jclorder ++, "cupsJobSheetsStart", "Start Banner", "job-sheets", suppattr, defattr, 0, 2); write_option(dstfp, jclorder, "cupsJobSheetsEnd", "End Banner", "job-sheets", suppattr, defattr, 1, 2); } cupsFilePuts(dstfp, "*CloseGroup: CUPS\n"); cupsFileClose(dstfp); ippDelete(response); return (buffer); } /* * '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 */ { int status; /* Status of Samba commands */ int have_drivers; /* Have drivers? */ char file[1024], /* File to test for */ authfile[1024], /* Temporary authentication file */ address[1024], /* Address for command */ subcmd[1024], /* Sub-command */ message[1024]; /* Error message */ cups_file_t *fp; /* Authentication file */ cups_lang_t *language; /* Current language */ _cups_globals_t *cg = _cupsGlobals(); /* Global data */ /* * Range check input... */ if (!dest || !ppd || !samba_server || !samba_user || !samba_password) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); return (0); } /* * Create a temporary authentication file for Samba... */ if ((fp = cupsTempFile2(authfile, sizeof(authfile))) == NULL) { _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0); return (0); } cupsFilePrintf(fp, "username = %s\n", samba_user); cupsFilePrintf(fp, "password = %s\n", samba_password); cupsFileClose(fp); /* * See which drivers are available; the new CUPS v6 and Adobe drivers * depend on the Windows 2k PS driver, so copy that driver first: * * Files: * * ps5ui.dll * pscript.hlp * pscript.ntf * pscript5.dll */ have_drivers = 0; language = cupsLangDefault(); snprintf(file, sizeof(file), "%s/drivers/pscript5.dll", cg->cups_datadir); if (!access(file, 0)) { have_drivers |= 1; /* * Windows 2k driver is installed; do the smbclient commands needed * to copy the Win2k drivers over... */ snprintf(address, sizeof(address), "//%s/print$", samba_server); snprintf(subcmd, sizeof(subcmd), "mkdir W32X86;" "put %s W32X86/%s.ppd;" "put %s/drivers/ps5ui.dll W32X86/ps5ui.dll;" "put %s/drivers/pscript.hlp W32X86/pscript.hlp;" "put %s/drivers/pscript.ntf W32X86/pscript.ntf;" "put %s/drivers/pscript5.dll W32X86/pscript5.dll", ppd, dest, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir); if ((status = do_samba_command("smbclient", address, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to copy Windows 2000 printer " "driver files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } /* * See if we also have the CUPS driver files; if so, use them! */ snprintf(file, sizeof(file), "%s/drivers/cupsps6.dll", cg->cups_datadir); if (!access(file, 0)) { /* * Copy the CUPS driver files over... */ snprintf(subcmd, sizeof(subcmd), "put %s/drivers/cups6.ini W32X86/cups6.ini;" "put %s/drivers/cupsps6.dll W32X86/cupsps6.dll;" "put %s/drivers/cupsui6.dll W32X86/cupsui6.dll", cg->cups_datadir, cg->cups_datadir, cg->cups_datadir); if ((status = do_samba_command("smbclient", address, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to copy CUPS printer driver " "files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } /* * Do the rpcclient command needed for the CUPS drivers... */ snprintf(subcmd, sizeof(subcmd), "adddriver \"Windows NT x86\" \"%s:" "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:" "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf," "cups6.ini,cupsps6.dll,cupsui6.dll\"", dest, dest, dest); } else { /* * Don't have the CUPS drivers, so just use the standard Windows * drivers... */ snprintf(subcmd, sizeof(subcmd), "adddriver \"Windows NT x86\" \"%s:" "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:" "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf\"", dest, dest, dest); } if ((status = do_samba_command("rpcclient", samba_server, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to install Windows 2000 printer " "driver files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } } /* * See if we have the Win9x PS driver... */ snprintf(file, sizeof(file), "%s/drivers/ADOBEPS4.DRV", cg->cups_datadir); if (!access(file, 0)) { have_drivers |= 2; /* * Do the smbclient commands needed for the Adobe Win9x drivers... */ snprintf(address, sizeof(address), "//%s/print$", samba_server); snprintf(subcmd, sizeof(subcmd), "mkdir WIN40;" "put %s WIN40/%s.PPD;" "put %s/drivers/ADFONTS.MFM WIN40/ADFONTS.MFM;" "put %s/drivers/ADOBEPS4.DRV WIN40/ADOBEPS4.DRV;" "put %s/drivers/ADOBEPS4.HLP WIN40/ADOBEPS4.HLP;" "put %s/drivers/ICONLIB.DLL WIN40/ICONLIB.DLL;" "put %s/drivers/PSMON.DLL WIN40/PSMON.DLL;", ppd, dest, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir); if ((status = do_samba_command("smbclient", address, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to copy Windows 9x printer " "driver files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } /* * Do the rpcclient commands needed for the Adobe Win9x drivers... */ snprintf(subcmd, sizeof(subcmd), "adddriver \"Windows 4.0\" \"%s:ADOBEPS4.DRV:%s.PPD:NULL:" "ADOBEPS4.HLP:PSMON.DLL:RAW:" "ADOBEPS4.DRV,%s.PPD,ADOBEPS4.HLP,PSMON.DLL,ADFONTS.MFM," "ICONLIB.DLL\"", dest, dest, dest); if ((status = do_samba_command("rpcclient", samba_server, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to install Windows 9x printer " "driver files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } } /* * See if we have the 64-bit Windows PS driver... * * Files: * * x64/ps5ui.dll * x64/pscript.hlp * x64/pscript.ntf * x64/pscript5.dll */ snprintf(file, sizeof(file), "%s/drivers/x64/pscript5.dll", cg->cups_datadir); if (!access(file, 0)) { have_drivers |= 4; /* * 64-bit Windows driver is installed; do the smbclient commands needed * to copy the Win64 drivers over... */ snprintf(address, sizeof(address), "//%s/print$", samba_server); snprintf(subcmd, sizeof(subcmd), "mkdir x64;" "put %s x64/%s.ppd;" "put %s/drivers/x64/ps5ui.dll x64/ps5ui.dll;" "put %s/drivers/x64/pscript.hlp x64/pscript.hlp;" "put %s/drivers/x64/pscript.ntf x64/pscript.ntf;" "put %s/drivers/x64/pscript5.dll x64/pscript5.dll", ppd, dest, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir, cg->cups_datadir); if ((status = do_samba_command("smbclient", address, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to copy 64-bit Windows printer " "driver files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } /* * See if we also have the CUPS driver files; if so, use them! */ snprintf(file, sizeof(file), "%s/drivers/x64/cupsps6.dll", cg->cups_datadir); if (!access(file, 0)) { /* * Copy the CUPS driver files over... */ snprintf(subcmd, sizeof(subcmd), "put %s/drivers/x64/cups6.ini x64/cups6.ini;" "put %s/drivers/x64/cupsps6.dll x64/cupsps6.dll;" "put %s/drivers/x64/cupsui6.dll x64/cupsui6.dll", cg->cups_datadir, cg->cups_datadir, cg->cups_datadir); if ((status = do_samba_command("smbclient", address, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to copy 64-bit CUPS printer driver " "files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } /* * Do the rpcclient command needed for the CUPS drivers... */ snprintf(subcmd, sizeof(subcmd), "adddriver \"Windows x64\" \"%s:" "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:" "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf," "cups6.ini,cupsps6.dll,cupsui6.dll\"", dest, dest, dest); } else { /* * Don't have the CUPS drivers, so just use the standard Windows * drivers... */ snprintf(subcmd, sizeof(subcmd), "adddriver \"Windows x64\" \"%s:" "pscript5.dll:%s.ppd:ps5ui.dll:pscript.hlp:NULL:RAW:" "pscript5.dll,%s.ppd,ps5ui.dll,pscript.hlp,pscript.ntf\"", dest, dest, dest); } if ((status = do_samba_command("rpcclient", samba_server, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to install Windows 2000 printer " "driver files (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } } if (logfile && !(have_drivers & 1)) { if (!have_drivers) strlcpy(message, _cupsLangString(language, _("No Windows printer drivers are installed.")), sizeof(message)); else strlcpy(message, _cupsLangString(language, _("Warning, no Windows 2000 printer drivers " "are installed.")), sizeof(message)); _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, message, 0); _cupsLangPuts(logfile, message); } if (have_drivers == 0) { _cupsSetError(IPP_STATUS_ERROR_NOT_FOUND, message, 0); unlink(authfile); return (0); } /* * Finally, associate the drivers we just added with the queue... */ snprintf(subcmd, sizeof(subcmd), "setdriver %s %s", dest, dest); if ((status = do_samba_command("rpcclient", samba_server, subcmd, authfile, logfile)) != 0) { snprintf(message, sizeof(message), _cupsLangString(language, _("Unable to set Windows printer driver (%d).")), status); _cupsSetError(IPP_STATUS_ERROR_INTERNAL, message, 0); if (logfile) _cupsLangPuts(logfile, message); unlink(authfile); return (0); } unlink(authfile); return (1); } /* * '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) { int new_share_printers = (share_printers > 0 || (share_printers == -1 && old_share_printers > 0)); wrote_browsing = 1; if (new_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) && (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); } /* * 'do_samba_command()' - Do a SAMBA command. */ static int /* O - Status of command */ do_samba_command(const char *command, /* I - Command to run */ const char *address, /* I - Address for command */ const char *subcmd, /* I - Sub-command */ const char *authfile, /* I - Samba authentication file */ FILE *logfile) /* I - Optional log file */ { #ifdef WIN32 return (1); /* Always fail on Windows... */ #else int status; /* Status of command */ int pid; /* Process ID of child */ if (logfile) _cupsLangPrintf(logfile, _("Running command: %s %s -N -A %s -c \'%s\'"), command, address, authfile, subcmd); if ((pid = fork()) == 0) { /* * Child goes here, redirect stdin/out/err and execute the command... */ int fd = open("/dev/null", O_RDONLY); if (fd > 0) { dup2(fd, 0); close(fd); } if (logfile) dup2(fileno(logfile), 1); else if ((fd = open("/dev/null", O_WRONLY)) > 1) { dup2(fd, 1); close(fd); } dup2(1, 2); execlp(command, command, address, "-N", "-A", authfile, "-c", subcmd, (char *)0); exit(errno); } else if (pid < 0) { status = -1; if (logfile) _cupsLangPrintf(logfile, _("Unable to run \"%s\": %s"), command, strerror(errno)); } else { /* * Wait for the process to complete... */ while (wait(&status) != pid); } if (logfile) _cupsLangPuts(logfile, ""); DEBUG_printf(("9do_samba_command: status=%d", status)); if (WIFEXITED(status)) return (WEXITSTATUS(status)); else return (-WTERMSIG(status)); #endif /* WIN32 */ } /* * '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; } /* * 'write_option()' - Write a CUPS option to a PPD file. */ static void write_option(cups_file_t *dstfp, /* I - PPD file */ int order, /* I - Order dependency */ const char *name, /* I - Option name */ const char *text, /* I - Option text */ const char *attrname, /* I - Attribute name */ ipp_attribute_t *suppattr, /* I - IPP -supported attribute */ ipp_attribute_t *defattr, /* I - IPP -default attribute */ int defval, /* I - Default value number */ int valcount) /* I - Number of values */ { int i; /* Looping var */ cupsFilePrintf(dstfp, "*JCLOpenUI *%s/%s: PickOne\n" "*OrderDependency: %d JCLSetup *%s\n", name, text, order, name); if (defattr->value_tag == IPP_TAG_INTEGER) { /* * Do numeric options with a range or list... */ cupsFilePrintf(dstfp, "*Default%s: %d\n", name, defattr->values[defval].integer); if (suppattr->value_tag == IPP_TAG_RANGE) { /* * List each number in the range... */ for (i = suppattr->values[0].range.lower; i <= suppattr->values[0].range.upper; i ++) { cupsFilePrintf(dstfp, "*%s %d: \"", name, i); if (valcount == 1) cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname, i); else if (defval == 0) cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\"\n", attrname, i); else if (defval < (valcount - 1)) cupsFilePrintf(dstfp, ",%d\"\n", i); else cupsFilePrintf(dstfp, ",%d\n\"\n*End\n", i); } } else { /* * List explicit numbers... */ for (i = 0; i < suppattr->num_values; i ++) { cupsFilePrintf(dstfp, "*%s %d: \"", name, suppattr->values[i].integer); if (valcount == 1) cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\n\"\n*End\n", attrname, suppattr->values[i].integer); else if (defval == 0) cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%d\"\n", attrname, suppattr->values[i].integer); else if (defval < (valcount - 1)) cupsFilePrintf(dstfp, ",%d\"\n", suppattr->values[i].integer); else cupsFilePrintf(dstfp, ",%d\n\"\n*End\n", suppattr->values[i].integer); } } } else { /* * Do text options with a list... */ cupsFilePrintf(dstfp, "*Default%s: %s\n", name, defattr->values[defval].string.text); for (i = 0; i < suppattr->num_values; i ++) { cupsFilePrintf(dstfp, "*%s %s: \"", name, suppattr->values[i].string.text); if (valcount == 1) cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%s\n\"\n*End\n", attrname, suppattr->values[i].string.text); else if (defval == 0) cupsFilePrintf(dstfp, "%%cupsJobTicket: %s=%s\"\n", attrname, suppattr->values[i].string.text); else if (defval < (valcount - 1)) cupsFilePrintf(dstfp, ",%s\"\n", suppattr->values[i].string.text); else cupsFilePrintf(dstfp, ",%s\n\"\n*End\n", suppattr->values[i].string.text); } } cupsFilePrintf(dstfp, "*JCLCloseUI: *%s\n\n", name); }