/* * "lpadmin" command for CUPS. * * Copyright © 2021 by OpenPrinting. * Copyright © 2007-2021 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 #include /* * 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 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_TLS cupsSetEncryption(HTTP_ENCRYPTION_REQUIRED); if (http) httpEncryption(http, HTTP_ENCRYPTION_REQUIRED); #else _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]); #endif /* HAVE_TLS */ 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")) || 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_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, "everywhere") && 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); }