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.

1686 lines
45 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.

/*
* "lpadmin" command for CUPS.
*
* Copyright © 2007-2019 by Apple Inc.
* Copyright © 1997-2006 by Easy Software Products.
*
* Licensed under Apache License v2.0. See the file "LICENSE" for more
* information.
*/
/*
* Include necessary headers...
*/
#include <cups/cups-private.h>
#include <cups/ppd-private.h>
/*
* Local functions...
*/
static int add_printer_to_class(http_t *http, char *printer, char *pclass);
static int default_printer(http_t *http, char *printer);
static int delete_printer(http_t *http, char *printer);
static int delete_printer_from_class(http_t *http, char *printer,
char *pclass);
static int delete_printer_option(http_t *http, char *printer,
char *option);
static int enable_printer(http_t *http, char *printer);
static char *get_printer_ppd(const char *uri, char *buffer, size_t bufsize, int *num_options, cups_option_t **options);
static cups_ptype_t get_printer_type(http_t *http, char *printer, char *uri,
size_t urisize);
static int set_printer_options(http_t *http, char *printer,
int num_options, cups_option_t *options,
char *file, int enable);
static void usage(void) _CUPS_NORETURN;
static int validate_name(const char *name);
/*
* 'main()' - Parse options and configure the scheduler.
*/
int /* O - Exit status */
main(int argc, /* I - Number of command-line arguments */
char *argv[]) /* I - Command-line arguments */
{
int i; /* Looping var */
http_t *http; /* Connection to server */
char *printer, /* Destination printer */
*pclass, /* Printer class name */
*opt, /* Option pointer */
*val; /* Pointer to allow/deny value */
int enable = 0; /* Enable/resume printer? */
int num_options; /* Number of options */
cups_option_t *options; /* Options */
char *file, /* New PPD file */
evefile[1024] = ""; /* IPP Everywhere PPD */
const char *ppd_name, /* ppd-name value */
*device_uri; /* device-uri value */
_cupsSetLocale(argv);
http = NULL;
printer = NULL;
num_options = 0;
options = NULL;
file = NULL;
for (i = 1; i < argc; i ++)
{
if (!strcmp(argv[i], "--help"))
usage();
else if (argv[i][0] == '-')
{
for (opt = argv[i] + 1; *opt; opt ++)
{
switch (*opt)
{
case 'c' : /* Add printer to class */
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
if (http == NULL)
{
_cupsLangPrintf(stderr, _("lpadmin: Unable to connect to server: %s"), strerror(errno));
return (1);
}
}
if (printer == NULL)
{
_cupsLangPuts(stderr,
_("lpadmin: Unable to add a printer to the class:\n"
" You must specify a printer name first."));
return (1);
}
if (opt[1] != '\0')
{
pclass = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected class name after \"-c\" option."));
usage();
}
pclass = argv[i];
}
if (!validate_name(pclass))
{
_cupsLangPuts(stderr,
_("lpadmin: Class name can only contain printable "
"characters."));
return (1);
}
if (add_printer_to_class(http, printer, pclass))
return (1);
break;
case 'd' : /* Set as default destination */
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
if (http == NULL)
{
_cupsLangPrintf(stderr, _("lpadmin: Unable to connect to server: %s"), strerror(errno));
return (1);
}
}
if (opt[1] != '\0')
{
printer = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected printer name after \"-d\" option."));
usage();
}
printer = argv[i];
}
if (!validate_name(printer))
{
_cupsLangPuts(stderr, _("lpadmin: Printer name can only contain printable characters."));
return (1);
}
if (default_printer(http, printer))
return (1);
i = argc;
break;
case 'h' : /* Connect to host */
if (http)
{
httpClose(http);
http = NULL;
}
if (opt[1] != '\0')
{
cupsSetServer(opt + 1);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected hostname after \"-h\" option."));
usage();
}
cupsSetServer(argv[i]);
}
break;
case 'P' : /* Use the specified PPD file */
case 'i' : /* Use the specified PPD file */
if (opt[1] != '\0')
{
file = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPrintf(stderr, _("lpadmin: Expected PPD after \"-%c\" option."), argv[i - 1][1]);
usage();
}
file = argv[i];
}
if (*opt == 'i')
{
/*
* Check to see that the specified file is, in fact, a PPD...
*/
cups_file_t *fp = cupsFileOpen(file, "r");
char line[256];
if (!cupsFileGets(fp, line, sizeof(line)) || strncmp(line, "*PPD-Adobe", 10))
{
_cupsLangPuts(stderr, _("lpadmin: System V interface scripts are no longer supported for security reasons."));
cupsFileClose(fp);
return (1);
}
cupsFileClose(fp);
}
break;
case 'E' : /* Enable the printer/enable encryption */
if (printer == NULL)
{
#ifdef HAVE_SSL
cupsSetEncryption(HTTP_ENCRYPTION_REQUIRED);
if (http)
httpEncryption(http, HTTP_ENCRYPTION_REQUIRED);
#else
_cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
#endif /* HAVE_SSL */
break;
}
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
if (http == NULL)
{
_cupsLangPrintf(stderr,
_("lpadmin: Unable to connect to server: %s"),
strerror(errno));
return (1);
}
}
enable = 1;
break;
case 'm' : /* Use the specified standard script/PPD file */
if (opt[1] != '\0')
{
num_options = cupsAddOption("ppd-name", opt + 1, num_options, &options);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected model after \"-m\" option."));
usage();
}
num_options = cupsAddOption("ppd-name", argv[i], num_options, &options);
}
break;
case 'o' : /* Set option */
if (opt[1] != '\0')
{
num_options = cupsParseOptions(opt + 1, num_options, &options);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected name=value after \"-o\" option."));
usage();
}
num_options = cupsParseOptions(argv[i], num_options, &options);
}
break;
case 'p' : /* Add/modify a printer */
if (opt[1] != '\0')
{
printer = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected printer after \"-p\" option."));
usage();
}
printer = argv[i];
}
if (!validate_name(printer))
{
_cupsLangPuts(stderr, _("lpadmin: Printer name can only contain printable characters."));
return (1);
}
break;
case 'r' : /* Remove printer from class */
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
if (http == NULL)
{
_cupsLangPrintf(stderr,
_("lpadmin: Unable to connect to server: %s"),
strerror(errno));
return (1);
}
}
if (printer == NULL)
{
_cupsLangPuts(stderr,
_("lpadmin: Unable to remove a printer from the class:\n"
" You must specify a printer name first."));
return (1);
}
if (opt[1] != '\0')
{
pclass = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected class after \"-r\" option."));
usage();
}
pclass = argv[i];
}
if (!validate_name(pclass))
{
_cupsLangPuts(stderr, _("lpadmin: Class name can only contain printable characters."));
return (1);
}
if (delete_printer_from_class(http, printer, pclass))
return (1);
break;
case 'R' : /* Remove option */
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
if (http == NULL)
{
_cupsLangPrintf(stderr, _("lpadmin: Unable to connect to server: %s"), strerror(errno));
return (1);
}
}
if (printer == NULL)
{
_cupsLangPuts(stderr,
_("lpadmin: Unable to delete option:\n"
" You must specify a printer name first."));
return (1);
}
if (opt[1] != '\0')
{
val = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected name after \"-R\" option."));
usage();
}
val = argv[i];
}
if (delete_printer_option(http, printer, val))
return (1);
break;
case 'U' : /* Username */
if (opt[1] != '\0')
{
cupsSetUser(opt + 1);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
usage();
}
cupsSetUser(argv[i]);
}
break;
case 'u' : /* Allow/deny users */
if (opt[1] != '\0')
{
val = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected allow/deny:userlist after \"-u\" option."));
usage();
}
val = argv[i];
}
if (!_cups_strncasecmp(val, "allow:", 6))
num_options = cupsAddOption("requesting-user-name-allowed", val + 6, num_options, &options);
else if (!_cups_strncasecmp(val, "deny:", 5))
num_options = cupsAddOption("requesting-user-name-denied", val + 5, num_options, &options);
else
{
_cupsLangPrintf(stderr, _("lpadmin: Unknown allow/deny option \"%s\"."), val);
return (1);
}
break;
case 'v' : /* Set the device-uri attribute */
if (opt[1] != '\0')
{
num_options = cupsAddOption("device-uri", opt + 1, num_options, &options);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected device URI after \"-v\" option."));
usage();
}
num_options = cupsAddOption("device-uri", argv[i], num_options, &options);
}
break;
case 'x' : /* Delete a printer */
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL);
if (http == NULL)
{
_cupsLangPrintf(stderr,
_("lpadmin: Unable to connect to server: %s"),
strerror(errno));
return (1);
}
}
if (opt[1] != '\0')
{
printer = opt + 1;
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected printer or class after \"-x\" option."));
usage();
}
printer = argv[i];
}
if (!validate_name(printer))
{
_cupsLangPuts(stderr, _("lpadmin: Printer name can only contain printable characters."));
return (1);
}
if (delete_printer(http, printer))
return (1);
i = argc;
break;
case 'D' : /* Set the printer-info attribute */
if (opt[1] != '\0')
{
num_options = cupsAddOption("printer-info", opt + 1, num_options, &options);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected description after \"-D\" option."));
usage();
}
num_options = cupsAddOption("printer-info", argv[i], num_options, &options);
}
break;
case 'I' : /* Set the supported file types (ignored) */
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected file type(s) after \"-I\" option."));
usage();
}
_cupsLangPuts(stderr, _("lpadmin: Warning - content type list ignored."));
break;
case 'L' : /* Set the printer-location attribute */
if (opt[1] != '\0')
{
num_options = cupsAddOption("printer-location", opt + 1, num_options, &options);
opt += strlen(opt) - 1;
}
else
{
i ++;
if (i >= argc)
{
_cupsLangPuts(stderr, _("lpadmin: Expected location after \"-L\" option."));
usage();
}
num_options = cupsAddOption("printer-location", argv[i], num_options, &options);
}
break;
default :
_cupsLangPrintf(stderr, _("lpadmin: Unknown option \"%c\"."), *opt);
usage();
}
}
}
else
{
_cupsLangPrintf(stderr, _("lpadmin: Unknown argument \"%s\"."), argv[i]);
usage();
}
}
/*
* Set options as needed...
*/
ppd_name = cupsGetOption("ppd-name", num_options, options);
device_uri = cupsGetOption("device-uri", num_options, options);
if (ppd_name && !strcmp(ppd_name, "raw"))
{
#ifdef __APPLE__
_cupsLangPuts(stderr, _("lpadmin: Raw queues are no longer supported on macOS."));
#else
_cupsLangPuts(stderr, _("lpadmin: Raw queues are deprecated and will stop working in a future version of CUPS."));
#endif /* __APPLE__ */
if (device_uri && (!strncmp(device_uri, "ipp://", 6) || !strncmp(device_uri, "ipps://", 7)) && strstr(device_uri, "/printers/"))
_cupsLangPuts(stderr, _("lpadmin: Use the 'everywhere' model for shared printers."));
#ifdef __APPLE__
return (1);
#endif /* __APPLE__ */
}
else if (ppd_name && !strcmp(ppd_name, "everywhere") && device_uri)
{
if ((file = get_printer_ppd(device_uri, evefile, sizeof(evefile), &num_options, &options)) == NULL)
return (1);
num_options = cupsRemoveOption("ppd-name", num_options, &options);
}
else if (ppd_name || file)
{
_cupsLangPuts(stderr, _("lpadmin: Printer drivers are deprecated and will stop working in a future version of CUPS."));
}
if (num_options || file)
{
if (printer == NULL)
{
_cupsLangPuts(stderr,
_("lpadmin: Unable to set the printer options:\n"
" You must specify a printer name first."));
return (1);
}
if (!http)
{
http = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC,
cupsEncryption(), 1, 30000, NULL);
if (http == NULL) {
_cupsLangPrintf(stderr, _("lpadmin: Unable to connect to server: %s"),
strerror(errno));
return (1);
}
}
if (set_printer_options(http, printer, num_options, options, file, enable))
return (1);
}
else if (enable && enable_printer(http, printer))
return (1);
if (evefile[0])
unlink(evefile);
if (printer == NULL)
usage();
if (http)
httpClose(http);
return (0);
}
/*
* 'add_printer_to_class()' - Add a printer to a class.
*/
static int /* O - 0 on success, 1 on fail */
add_printer_to_class(http_t *http, /* I - Server connection */
char *printer, /* I - Printer to add */
char *pclass) /* I - Class to add to */
{
int i; /* Looping var */
ipp_t *request, /* IPP Request */
*response; /* IPP Response */
ipp_attribute_t *attr, /* Current attribute */
*members; /* Members in class */
char uri[HTTP_MAX_URI]; /* URI for printer/class */
/*
* Build an IPP_OP_GET_PRINTER_ATTRIBUTES request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
*/
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
"localhost", 0, "/classes/%s", pclass);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
/*
* Do the request and get back a response...
*/
response = cupsDoRequest(http, request, "/");
/*
* Build a CUPS-Add-Modify-Class request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
* member-uris
*/
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_CLASS);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
/*
* See if the printer is already in the class...
*/
if (response != NULL &&
(members = ippFindAttribute(response, "member-names",
IPP_TAG_NAME)) != NULL)
for (i = 0; i < members->num_values; i ++)
if (_cups_strcasecmp(printer, members->values[i].string.text) == 0)
{
_cupsLangPrintf(stderr,
_("lpadmin: Printer %s is already a member of class "
"%s."), printer, pclass);
ippDelete(request);
ippDelete(response);
return (0);
}
/*
* OK, the printer isn't part of the class, so add it...
*/
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
"localhost", 0, "/printers/%s", printer);
if (response != NULL &&
(members = ippFindAttribute(response, "member-uris",
IPP_TAG_URI)) != NULL)
{
/*
* Add the printer to the existing list...
*/
attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
"member-uris", members->num_values + 1, NULL, NULL);
for (i = 0; i < members->num_values; i ++)
attr->values[i].string.text =
_cupsStrAlloc(members->values[i].string.text);
attr->values[i].string.text = _cupsStrAlloc(uri);
}
else
ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "member-uris", NULL,
uri);
/*
* Then send the request...
*/
ippDelete(response);
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
else
return (0);
}
/*
* 'default_printer()' - Set the default printing destination.
*/
static int /* O - 0 on success, 1 on fail */
default_printer(http_t *http, /* I - Server connection */
char *printer) /* I - Printer name */
{
ipp_t *request; /* IPP Request */
char uri[HTTP_MAX_URI]; /* URI for printer/class */
/*
* Build a CUPS-Set-Default request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
*/
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
"localhost", 0, "/printers/%s", printer);
request = ippNewRequest(IPP_OP_CUPS_SET_DEFAULT);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
/*
* Do the request and get back a response...
*/
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
else
return (0);
}
/*
* 'delete_printer()' - Delete a printer from the system...
*/
static int /* O - 0 on success, 1 on fail */
delete_printer(http_t *http, /* I - Server connection */
char *printer) /* I - Printer to delete */
{
ipp_t *request; /* IPP Request */
char uri[HTTP_MAX_URI]; /* URI for printer/class */
/*
* Build a CUPS-Delete-Printer request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
*/
request = ippNewRequest(IPP_OP_CUPS_DELETE_PRINTER);
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);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
/*
* Do the request and get back a response...
*/
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
else
return (0);
}
/*
* 'delete_printer_from_class()' - Delete a printer from a class.
*/
static int /* O - 0 on success, 1 on fail */
delete_printer_from_class(
http_t *http, /* I - Server connection */
char *printer, /* I - Printer to remove */
char *pclass) /* I - Class to remove from */
{
int i, j, k; /* Looping vars */
ipp_t *request, /* IPP Request */
*response; /* IPP Response */
ipp_attribute_t *attr, /* Current attribute */
*members; /* Members in class */
char uri[HTTP_MAX_URI]; /* URI for printer/class */
/*
* Build an IPP_OP_GET_PRINTER_ATTRIBUTES request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
*/
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
"localhost", 0, "/classes/%s", pclass);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
NULL, cupsUser());
/*
* Do the request and get back a response...
*/
if ((response = cupsDoRequest(http, request, "/classes/")) == NULL ||
response->request.status.status_code == IPP_STATUS_ERROR_NOT_FOUND)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
ippDelete(response);
return (1);
}
/*
* See if the printer is already in the class...
*/
if ((members = ippFindAttribute(response, "member-names", IPP_TAG_NAME)) == NULL)
{
_cupsLangPuts(stderr, _("lpadmin: No member names were seen."));
ippDelete(response);
return (1);
}
for (i = 0; i < members->num_values; i ++)
if (!_cups_strcasecmp(printer, members->values[i].string.text))
break;
if (i >= members->num_values)
{
_cupsLangPrintf(stderr,
_("lpadmin: Printer %s is not a member of class %s."),
printer, pclass);
ippDelete(response);
return (1);
}
if (members->num_values == 1)
{
/*
* Build a CUPS-Delete-Class request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
*/
request = ippNewRequest(IPP_OP_CUPS_DELETE_CLASS);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
}
else
{
/*
* Build a IPP_OP_CUPS_ADD_MODIFY_CLASS request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
* member-uris
*/
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_CLASS);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
/*
* Delete the printer from the class...
*/
members = ippFindAttribute(response, "member-uris", IPP_TAG_URI);
attr = ippAddStrings(request, IPP_TAG_PRINTER, IPP_TAG_URI,
"member-uris", members->num_values - 1, NULL, NULL);
for (j = 0, k = 0; j < members->num_values; j ++)
if (j != i)
attr->values[k ++].string.text =
_cupsStrAlloc(members->values[j].string.text);
}
/*
* Then send the request...
*/
ippDelete(response);
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
else
return (0);
}
/*
* 'delete_printer_option()' - Delete a printer option.
*/
static int /* O - 0 on success, 1 on fail */
delete_printer_option(http_t *http, /* I - Server connection */
char *printer, /* I - Printer */
char *option) /* I - Option to delete */
{
ipp_t *request; /* IPP request */
char uri[HTTP_MAX_URI]; /* URI for printer/class */
/*
* Build a IPP_OP_CUPS_ADD_MODIFY_PRINTER or IPP_OP_CUPS_ADD_MODIFY_CLASS request, which
* requires the following attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
* option with deleteAttr tag
*/
if (get_printer_type(http, printer, uri, sizeof(uri)) & CUPS_PRINTER_CLASS)
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_CLASS);
else
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR, option, 0);
/*
* Do the request and get back a response...
*/
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
else
return (0);
}
/*
* 'enable_printer()' - Enable a printer...
*/
static int /* O - 0 on success, 1 on fail */
enable_printer(http_t *http, /* I - Server connection */
char *printer) /* I - Printer to enable */
{
ipp_t *request; /* IPP Request */
char uri[HTTP_MAX_URI]; /* URI for printer/class */
/*
* Send IPP_OP_ENABLE_PRINTER and IPP_OP_RESUME_PRINTER requests, which
* require the following attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
*/
request = ippNewRequest(IPP_OP_ENABLE_PRINTER);
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", ippPort(), "/printers/%s", printer);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
request = ippNewRequest(IPP_OP_RESUME_PRINTER);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
ippDelete(cupsDoRequest(http, request, "/admin/"));
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
return (0);
}
/*
* 'get_printer_ppd()' - Get an IPP Everywhere PPD file for the given URI.
*/
static char * /* O - Filename or NULL */
get_printer_ppd(
const char *uri, /* I - Printer URI */
char *buffer, /* I - Filename buffer */
size_t bufsize, /* I - Size of filename buffer */
int *num_options, /* IO - Number of options */
cups_option_t **options) /* IO - Options */
{
http_t *http; /* Connection to printer */
ipp_t *request, /* Get-Printer-Attributes request */
*response; /* Get-Printer-Attributes response */
ipp_attribute_t *attr; /* Attribute from response */
char resolved[1024], /* Resolved URI */
scheme[32], /* URI scheme */
userpass[256], /* Username:password */
host[256], /* Hostname */
resource[256]; /* Resource path */
int port; /* Port number */
static const char * const pattrs[] = /* Attributes to use */
{
"all",
"media-col-database"
};
/*
* Connect to the printer...
*/
if (strstr(uri, "._tcp"))
{
/*
* Resolve URI...
*/
if (!_httpResolveURI(uri, resolved, sizeof(resolved), _HTTP_RESOLVE_DEFAULT, NULL, NULL))
{
_cupsLangPrintf(stderr, _("%s: Unable to resolve \"%s\"."), "lpadmin", uri);
return (NULL);
}
uri = resolved;
}
if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
{
_cupsLangPrintf(stderr, _("%s: Bad printer URI \"%s\"."), "lpadmin", uri);
return (NULL);
}
http = httpConnect2(host, port, NULL, AF_UNSPEC, !strcmp(scheme, "ipps") ? HTTP_ENCRYPTION_ALWAYS : HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
if (!http)
{
_cupsLangPrintf(stderr, _("%s: Unable to connect to \"%s:%d\": %s"), "lpadmin", host, port, cupsLastErrorString());
return (NULL);
}
/*
* Send a Get-Printer-Attributes request...
*/
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
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);
response = cupsDoRequest(http, request, resource);
if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
{
_cupsLangPrintf(stderr, _("%s: Unable to query printer: %s"), "lpadmin", cupsLastErrorString());
buffer[0] = '\0';
}
else if (_ppdCreateFromIPP(buffer, bufsize, response))
{
if (!cupsGetOption("printer-geo-location", *num_options, *options) && (attr = ippFindAttribute(response, "printer-geo-location", IPP_TAG_URI)) != NULL)
*num_options = cupsAddOption("printer-geo-location", ippGetString(attr, 0, NULL), *num_options, options);
if (!cupsGetOption("printer-info", *num_options, *options) && (attr = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT)) != NULL)
*num_options = cupsAddOption("printer-info", ippGetString(attr, 0, NULL), *num_options, options);
if (!cupsGetOption("printer-location", *num_options, *options) && (attr = ippFindAttribute(response, "printer-location", IPP_TAG_TEXT)) != NULL)
*num_options = cupsAddOption("printer-location", ippGetString(attr, 0, NULL), *num_options, options);
}
else
_cupsLangPrintf(stderr, _("%s: Unable to create PPD file: %s"), "lpadmin", strerror(errno));
ippDelete(response);
httpClose(http);
if (buffer[0])
return (buffer);
else
return (NULL);
}
/*
* 'get_printer_type()' - Determine the printer type and URI.
*/
static cups_ptype_t /* O - printer-type value */
get_printer_type(http_t *http, /* I - Server connection */
char *printer, /* I - Printer name */
char *uri, /* I - URI buffer */
size_t urisize) /* I - Size of URI buffer */
{
ipp_t *request, /* IPP request */
*response; /* IPP response */
ipp_attribute_t *attr; /* printer-type attribute */
cups_ptype_t type; /* printer-type value */
/*
* Build a GET_PRINTER_ATTRIBUTES request, which requires the following
* attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requested-attributes
* requesting-user-name
*/
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, (int)urisize, "ipp", NULL, "localhost", ippPort(), "/printers/%s", printer);
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
"printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", NULL, "printer-type");
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
"requesting-user-name", NULL, cupsUser());
/*
* Do the request...
*/
response = cupsDoRequest(http, request, "/");
if ((attr = ippFindAttribute(response, "printer-type",
IPP_TAG_ENUM)) != NULL)
{
type = (cups_ptype_t)attr->values[0].integer;
if (type & CUPS_PRINTER_CLASS)
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, (int)urisize, "ipp", NULL, "localhost", ippPort(), "/classes/%s", printer);
}
else
type = CUPS_PRINTER_LOCAL;
ippDelete(response);
return (type);
}
/*
* 'set_printer_options()' - Set the printer options.
*/
static int /* O - 0 on success, 1 on fail */
set_printer_options(
http_t *http, /* I - Server connection */
char *printer, /* I - Printer */
int num_options, /* I - Number of options */
cups_option_t *options, /* I - Options */
char *file, /* I - PPD file */
int enable) /* I - Enable printer? */
{
ipp_t *request; /* IPP Request */
const char *ppdfile; /* PPD filename */
int ppdchanged = 0; /* PPD changed? */
ppd_file_t *ppd; /* PPD file */
ppd_choice_t *choice; /* Marked choice */
char uri[HTTP_MAX_URI], /* URI for printer/class */
line[1024], /* Line from PPD file */
keyword[1024], /* Keyword from Default line */
*keyptr, /* Pointer into keyword... */
tempfile[1024]; /* Temporary filename */
cups_file_t *in, /* PPD file */
*out; /* Temporary file */
const char *ppdname, /* ppd-name value */
*protocol, /* Old protocol option */
*customval, /* Custom option value */
*boolval; /* Boolean value */
int wrote_ipp_supplies = 0, /* Wrote cupsIPPSupplies keyword? */
wrote_snmp_supplies = 0,/* Wrote cupsSNMPSupplies keyword? */
copied_options = 0; /* Copied options? */
/*
* Build a CUPS-Add-Modify-Printer or CUPS-Add-Modify-Class request,
* which requires the following attributes:
*
* attributes-charset
* attributes-natural-language
* printer-uri
* requesting-user-name
* other options
*/
if (get_printer_type(http, printer, uri, sizeof(uri)) & CUPS_PRINTER_CLASS)
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_CLASS);
else
request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
/*
* Add the options...
*/
if (file)
ppdfile = file;
else if ((ppdname = cupsGetOption("ppd-name", num_options, options)) != NULL && strcmp(ppdname, "raw") && num_options > 1)
{
if ((ppdfile = cupsGetServerPPD(http, ppdname)) != NULL)
{
/*
* Copy options array and remove ppd-name from it...
*/
cups_option_t *temp = NULL, *optr;
int i, num_temp = 0;
for (i = num_options, optr = options; i > 0; i --, optr ++)
if (strcmp(optr->name, "ppd-name"))
num_temp = cupsAddOption(optr->name, optr->value, num_temp, &temp);
copied_options = 1;
ppdchanged = 1;
num_options = num_temp;
options = temp;
}
}
else if (request->request.op.operation_id == IPP_OP_CUPS_ADD_MODIFY_PRINTER)
ppdfile = cupsGetPPD(printer);
else
ppdfile = NULL;
cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
if (enable)
{
ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", IPP_PSTATE_IDLE);
ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
}
cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
if ((protocol = cupsGetOption("protocol", num_options, options)) != NULL)
{
if (!_cups_strcasecmp(protocol, "bcp"))
ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
NULL, "bcp");
else if (!_cups_strcasecmp(protocol, "tbcp"))
ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_NAME, "port-monitor",
NULL, "tbcp");
}
if (ppdfile)
{
/*
* Set default options in the PPD file...
*/
if ((ppd = ppdOpenFile(ppdfile)) == NULL)
{
int linenum; /* Line number of error */
ppd_status_t status = ppdLastError(&linenum);
/* Status code */
_cupsLangPrintf(stderr, _("lpadmin: Unable to open PPD \"%s\": %s on line %d."), ppdfile, ppdErrorString(status), linenum);
return (1);
}
ppdMarkDefaults(ppd);
cupsMarkOptions(ppd, num_options, options);
if ((out = cupsTempFile2(tempfile, sizeof(tempfile))) == NULL)
{
_cupsLangPrintError(NULL, _("lpadmin: Unable to create temporary file"));
ippDelete(request);
if (ppdfile != file)
unlink(ppdfile);
if (copied_options)
cupsFreeOptions(num_options, options);
return (1);
}
if ((in = cupsFileOpen(ppdfile, "r")) == NULL)
{
_cupsLangPrintf(stderr, _("lpadmin: Unable to open PPD \"%s\": %s"), ppdfile, strerror(errno));
ippDelete(request);
if (ppdfile != file)
unlink(ppdfile);
if (copied_options)
cupsFreeOptions(num_options, options);
cupsFileClose(out);
unlink(tempfile);
return (1);
}
while (cupsFileGets(in, line, sizeof(line)))
{
if (!strncmp(line, "*cupsIPPSupplies:", 17) &&
(boolval = cupsGetOption("cupsIPPSupplies", num_options,
options)) != NULL)
{
ppdchanged = 1;
wrote_ipp_supplies = 1;
cupsFilePrintf(out, "*cupsIPPSupplies: %s\n",
(!_cups_strcasecmp(boolval, "true") ||
!_cups_strcasecmp(boolval, "yes") ||
!_cups_strcasecmp(boolval, "on")) ? "True" : "False");
}
else if (!strncmp(line, "*cupsSNMPSupplies:", 18) &&
(boolval = cupsGetOption("cupsSNMPSupplies", num_options,
options)) != NULL)
{
ppdchanged = 1;
wrote_snmp_supplies = 1;
cupsFilePrintf(out, "*cupsSNMPSupplies: %s\n",
(!_cups_strcasecmp(boolval, "true") ||
!_cups_strcasecmp(boolval, "yes") ||
!_cups_strcasecmp(boolval, "on")) ? "True" : "False");
}
else if (strncmp(line, "*Default", 8))
cupsFilePrintf(out, "%s\n", line);
else
{
/*
* Get default option name...
*/
strlcpy(keyword, line + 8, sizeof(keyword));
for (keyptr = keyword; *keyptr; keyptr ++)
if (*keyptr == ':' || isspace(*keyptr & 255))
break;
*keyptr++ = '\0';
while (isspace(*keyptr & 255))
keyptr ++;
if (!strcmp(keyword, "PageRegion") ||
!strcmp(keyword, "PageSize") ||
!strcmp(keyword, "PaperDimension") ||
!strcmp(keyword, "ImageableArea"))
{
if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) == NULL)
choice = ppdFindMarkedChoice(ppd, "PageRegion");
}
else
choice = ppdFindMarkedChoice(ppd, keyword);
if (choice && strcmp(choice->choice, keyptr))
{
if (strcmp(choice->choice, "Custom"))
{
cupsFilePrintf(out, "*Default%s: %s\n", keyword, choice->choice);
ppdchanged = 1;
}
else if ((customval = cupsGetOption(keyword, num_options,
options)) != NULL)
{
cupsFilePrintf(out, "*Default%s: %s\n", keyword, customval);
ppdchanged = 1;
}
else
cupsFilePrintf(out, "%s\n", line);
}
else
cupsFilePrintf(out, "%s\n", line);
}
}
if (!wrote_ipp_supplies &&
(boolval = cupsGetOption("cupsIPPSupplies", num_options,
options)) != NULL)
{
ppdchanged = 1;
cupsFilePrintf(out, "*cupsIPPSupplies: %s\n",
(!_cups_strcasecmp(boolval, "true") ||
!_cups_strcasecmp(boolval, "yes") ||
!_cups_strcasecmp(boolval, "on")) ? "True" : "False");
}
if (!wrote_snmp_supplies &&
(boolval = cupsGetOption("cupsSNMPSupplies", num_options,
options)) != NULL)
{
ppdchanged = 1;
cupsFilePrintf(out, "*cupsSNMPSupplies: %s\n",
(!_cups_strcasecmp(boolval, "true") ||
!_cups_strcasecmp(boolval, "yes") ||
!_cups_strcasecmp(boolval, "on")) ? "True" : "False");
}
cupsFileClose(in);
cupsFileClose(out);
ppdClose(ppd);
/*
* Do the request...
*/
ippDelete(cupsDoFileRequest(http, request, "/admin/", ppdchanged ? tempfile : file));
/*
* Clean up temp files... (TODO: catch signals in case we CTRL-C during
* lpadmin)
*/
if (ppdfile != file)
unlink(ppdfile);
unlink(tempfile);
}
else
{
/*
* No PPD file - just set the options...
*/
ippDelete(cupsDoRequest(http, request, "/admin/"));
}
if (copied_options)
cupsFreeOptions(num_options, options);
/*
* Check the response...
*/
if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
{
_cupsLangPrintf(stderr, _("%s: %s"), "lpadmin", cupsLastErrorString());
return (1);
}
else
return (0);
}
/*
* 'usage()' - Show program usage and exit.
*/
static void
usage(void)
{
_cupsLangPuts(stdout, _("Usage: lpadmin [options] -d destination\n"
" lpadmin [options] -p destination\n"
" lpadmin [options] -p destination -c class\n"
" lpadmin [options] -p destination -r class\n"
" lpadmin [options] -x destination"));
_cupsLangPuts(stdout, _("Options:"));
_cupsLangPuts(stdout, _("-c class Add the named destination to a class"));
_cupsLangPuts(stdout, _("-d destination Set the named destination as the server default"));
_cupsLangPuts(stdout, _("-D description Specify the textual description of the printer"));
_cupsLangPuts(stdout, _("-E Encrypt the connection to the server"));
_cupsLangPuts(stdout, _("-E Enable and accept jobs on the printer (after -p)"));
_cupsLangPuts(stdout, _("-h server[:port] Connect to the named server and port"));
_cupsLangPuts(stdout, _("-i ppd-file Specify a PPD file for the printer"));
_cupsLangPuts(stdout, _("-L location Specify the textual location of the printer"));
_cupsLangPuts(stdout, _("-m model Specify a standard model/PPD file for the printer"));
_cupsLangPuts(stdout, _("-m everywhere Specify the printer is compatible with IPP Everywhere"));
_cupsLangPuts(stdout, _("-o name-default=value Specify the default value for the named option"));
_cupsLangPuts(stdout, _("-o Name=Value Specify the default value for the named PPD option "));
_cupsLangPuts(stdout, _("-o cupsIPPSupplies=false\n"
" Disable supply level reporting via IPP"));
_cupsLangPuts(stdout, _("-o cupsSNMPSupplies=false\n"
" Disable supply level reporting via SNMP"));
_cupsLangPuts(stdout, _("-o job-k-limit=N Specify the kilobyte limit for per-user quotas"));
_cupsLangPuts(stdout, _("-o job-page-limit=N Specify the page limit for per-user quotas"));
_cupsLangPuts(stdout, _("-o job-quota-period=N Specify the per-user quota period in seconds"));
_cupsLangPuts(stdout, _("-o printer-error-policy=name\n"
" Specify the printer error policy"));
_cupsLangPuts(stdout, _("-o printer-is-shared=true\n"
" Share the printer"));
_cupsLangPuts(stdout, _("-o printer-op-policy=name\n"
" Specify the printer operation policy"));
_cupsLangPuts(stdout, _("-p destination Specify/add the named destination"));
_cupsLangPuts(stdout, _("-r class Remove the named destination from a class"));
_cupsLangPuts(stdout, _("-R name-default Remove the default value for the named option"));
_cupsLangPuts(stdout, _("-u allow:all Allow all users to print"));
_cupsLangPuts(stdout, _("-u allow:list Allow the list of users or groups (@name) to print"));
_cupsLangPuts(stdout, _("-u deny:list Prevent the list of users or groups (@name) to print"));
_cupsLangPuts(stdout, _("-U username Specify the username to use for authentication"));
_cupsLangPuts(stdout, _("-v device-uri Specify the device URI for the printer"));
_cupsLangPuts(stdout, _("-x destination Remove the named destination"));
exit(1);
}
/*
* 'validate_name()' - Make sure the printer name only contains valid chars.
*/
static int /* O - 0 if name is no good, 1 if name is good */
validate_name(const char *name) /* I - Name to check */
{
const char *ptr; /* Pointer into name */
/*
* Scan the whole name...
*/
for (ptr = name; *ptr; ptr ++)
if (*ptr == '@')
break;
else if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '\\' || *ptr == '?' || *ptr == '\'' || *ptr == '\"' || *ptr == '#')
return (0);
/*
* All the characters are good; validate the length, too...
*/
return ((ptr - name) < 128);
}