Here is my first go at hal'izing lpadmin. It supports adding and removing a printer using the printer's UDI. This patch requires Joe's Hal backend patch and my small patch to the hal backend which was sent in an earlier mail. Basicly you can call hal_lpadmin -p <hal UDI> to add a printer and hal_lpadmin -x <hal UDI> to remove it. hal_lpadmin gets the rest of the info from hal. It generates a printer name based on the product info (with underscores placed where there are whitespaces) and the device sequence number which is parsed out of the UDI. It would be nice if the sequence number was a hal property. The name should be unique enough without obfuscating it. Attached are also two callout scripts, printer-update.sh which goes in /etc/hal/capability.d/ and printer-remove.sh which goes in /etc/hal/device.d. This is if you want the printer added and removed when plugged in or out. It would be nice if I could just use one script and capability callouts supported add and remove states. Also since callouts block if cupsd is not running hal_lpadmin will take a while to timeout and consequently block hal. It would be nice if there was a way to do asynchronous callouts but also be able to save the state of hal at the point of the callout. Or even somehow allow a callout to block until it got all of the state info it was interested in and then signal to hal that it no longer needs to block. This patch is not finished. The next step is to select a best match ppd driver file and then broadcast a dbus message in case the driver selection is wrong. A desktop user daemon could then intercept this message and notify the user for further configuration info. This info is then shipped via dbus to either cupsd or another system daemon and updates the printer configuration. To do this in a secure manner I am going to implement a current console user security policy in dbus which would allow the configuration of services to accept connections only from the current console user. That is the idea anyway. Any thoughts or concerns people have on this approach would be good. -- John (J5) Palmieri Associate Software Engineer Desktop Group Red Hat, Inc. Blog: http://martianrock.com
--- cups-1.1.20/systemv/Makefile.hal 2004-06-10 15:24:59.000000000 -0400 +++ cups-1.1.20/systemv/Makefile 2004-06-10 16:28:05.000000000 -0400 @@ -25,7 +25,7 @@ include ../Makedefs TARGETS = accept cancel cupsaddsmb cupstestppd lp lpadmin lpinfo \ - lpmove lpoptions lppasswd lpstat + lpmove lpoptions lppasswd lpstat hal_lpadmin OBJS = accept.o cancel.o cupsaddsmb.o cupstestppd.o lp.o \ lpadmin.o lpinfo.o lpmove.o lpoptions.o lppasswd.o \ lpstat.o @@ -65,6 +65,7 @@ $(LN) accept $(SBINDIR)/reject $(INSTALL_BIN) cupsaddsmb $(SBINDIR) $(INSTALL_BIN) lpadmin $(SBINDIR) + $(INSTALL_BIN) hal_lpadmin $(SBINDIR) $(INSTALL_BIN) lpinfo $(SBINDIR) $(INSTALL_BIN) lpmove $(SBINDIR) $(INSTALL_DIR) $(BINDIR) @@ -138,6 +139,12 @@ echo Linking $ $(CC) $(LDFLAGS) -o lpadmin lpadmin.o $(LIBZ) $(LIBS) +# +# hal_lpadmin +# +hal_lpadmin: hal_lpadmin.o ../cups/$(LIBCUPS) + echo Linking $ + $(CC) $(HALFLAGS) $(LDFLAGS) -o hal_lpadmin hal_lpadmin.o $(LIBZ) $(HALLIBS) $(LIBS) # # lpinfo --- cups-1.1.20/systemv/hal_lpadmin.c.hal 2004-06-10 15:25:15.000000000 -0400 +++ cups-1.1.20/systemv/hal_lpadmin.c 2004-06-16 14:34:14.000000000 -0400 @@ -0,0 +1,1287 @@ + +/* + hal_lpadmin - a halafied local printer configuration app based on lpadmin from cups +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <cups/cups.h> +#include <cups/string.h> +#include <cups/language.h> +#include <cups/debug.h> +#include <config.h> +#ifdef HAVE_LIBZ +# include <zlib.h> +#endif /* HAVE_LIBZ */ +#include <hal/libhal.h> + +#define URI_PREFIX "hal://" + +static int enable_printer(http_t *, char *); +static int delete_printer(http_t *, char *); +static char *get_line(char *, int, FILE *fp); +static int set_printer_device(http_t *, char *, char *); +static int set_printer_file(http_t *, char *, char *); +static int set_printer_info(http_t *, char *, char *); +static int set_printer_location(http_t *, char *, char *); +static int set_printer_model(http_t *, char *, char *); +static int set_printer_options(http_t *, char *, int, cups_option_t *); +static int validate_name(char *); + +static void print_usage_and_exit(); +static void add_hal_device_if_printer(LibHalContext *, const char *); +static void remove_hal_device_if_printer(LibHalContext *, const char *); + +static char *construct_printer_name(const char *, const char*); +static int send_dbus_signal_printer_needs_config(void); + +/* + * 'main()' - Parse options and configure the scheduler. + */ + +int +main(int argc, /* I - Number of command-line arguments */ + char *argv[]) /* I - Command-line arguments */ +{ + + char *hal_udi; /* UDI of hal device*/ + int i; /* Looping var */ + int remove_device; /* if true then we are removing the device, else adding */ + LibHalContext *hal_ctx; /* Hal context */ + + + hal_udi = NULL; + remove_device = 0; + + for (i = 1; i < argc; i ++) + if (argv[i][0] == '-') + switch (argv[i][1]) + { + case 'p' : /* device UID */ + if (argv[i][2]) + { + hal_udi = argv[i] + 2; + } + else + { + i ++; + + if (i >= argc) + { + print_usage_and_exit(); + } + + hal_udi = argv[i]; + } + break; + + case 'x' : /* remove device */ + remove_device = 1; + + if (argv[i][2]) + { + hal_udi = argv[i] + 2; + } + else + { + i ++; + + if (i >= argc) + { + print_usage_and_exit(); + } + + hal_udi = argv[i]; + } + break; + } + + if (hal_udi == NULL) + print_usage_and_exit(); + + /* get hal context */ + if ((hal_ctx = hal_initialize(NULL, 0)) == NULL) + { + fprintf(stderr, "hal_initialize failed\n"); + exit(1); + } + + if (!remove_device) + add_hal_device_if_printer(hal_ctx, hal_udi); + else + remove_hal_device_if_printer(hal_ctx, hal_udi); + + return (0); +} + +/* + * 'print_usage_and_exit()' - prints out the command line usage and exits + */ + +static void +print_usage_and_exit() +{ + fprintf(stderr, "USAGE: hal_lpadmin -d <hal device udi> [-r]"); + exit(1); +} + +/* + * 'add_hal_device_if_printer()' - checks if the device is a printer and adds it to cups + */ +static void +add_hal_device_if_printer(LibHalContext *ctx, + const char *udi) +{ + char *capabilities; + char *product; + char *vendor; + char *description; + char *serial; + char *device; + char *uri = NULL; + char *printer = NULL; + http_t *http = NULL; + + capabilities = hal_device_get_property_string(ctx, udi, "info.capabilities"); + + if (capabilities == NULL || strstr(capabilities, "printer") == NULL) + { + hal_free_string(capabilities); + return; + } + + product = hal_device_get_property_string(ctx, udi, "printer.product"); + vendor = hal_device_get_property_string(ctx, udi, "printer.vendor"); + description = hal_device_get_property_string(ctx, udi, "printer.description"); + serial = hal_device_get_property_string(ctx, udi, "printer.serial"); + device = hal_device_get_property_string(ctx, udi, "printer.device"); + + uri = malloc(sizeof(char) * (strlen(udi) + strlen(URI_PREFIX) + 1)); + + if (!uri) + { + fprintf(stderr, "ERROR: Out of memory\n"); + goto out; + } + + sprintf(uri, "%s%s", URI_PREFIX, udi); + + printer = construct_printer_name(udi, product); + + if (!printer) + goto out; + + http = httpConnectEncrypt(cupsServer(), ippPort(), + cupsEncryption()); + + if (http == NULL) + { + fprintf(stderr, "Unable to connect to server\n"); + goto out; + } + + enable_printer(http, printer); + set_printer_device(http, printer, uri); + set_printer_info(http, printer, description); + + /*send_dbus_signal_printer_needs_config(udi);*/ + + out: + hal_free_string(capabilities); + hal_free_string(product); + hal_free_string(vendor); + hal_free_string(description); + hal_free_string(serial); + hal_free_string(device); + hal_free_string(uri); + hal_free_string(printer); +} + +/* + * 'remove_hal_device_if_printer()' - checks if the device is a printer and removes it from cups + */ +static void +remove_hal_device_if_printer(LibHalContext *ctx, + const char *udi) +{ + char *capabilities; + char *product; + char *serial; + char *printer = NULL; + + http_t *http = NULL; + + capabilities = hal_device_get_property_string(ctx, udi, "info.capabilities"); + + if (capabilities == NULL || strstr(capabilities, "printer") == NULL) + { + hal_free_string(capabilities); + return; + } + + product = hal_device_get_property_string(ctx, udi, "printer.product"); + serial = hal_device_get_property_string(ctx, udi, "printer.serial"); + + printer = construct_printer_name(udi, product); + + if (!printer) + goto out; + + http = httpConnectEncrypt(cupsServer(), ippPort(), + cupsEncryption()); + + if (http == NULL) + { + fprintf(stderr, "Unable to connect to server\n"); + goto out; + } + + delete_printer(http, printer); + + out: + hal_free_string(capabilities); + hal_free_string(product); + hal_free_string(serial); + hal_free_string(printer); + + +} + + +/* + * '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 */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ + + + DEBUG_printf(("delete_printer(%p, \"%s\")\n", http, printer)); + + /* + * Build a CUPS_DELETE_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_DELETE_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/admin/")) == NULL) + { + fprintf(stderr, "lpadmin: delete-printer failed: %s\n", + ippErrorString(cupsLastError())); + + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: delete-printer failed: %s\n", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + 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 */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ + + + DEBUG_printf(("enable_printer(%p, \"%s\")\n", http, printer)); + + /* + * Build a CUPS_ADD_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * printer-state + * printer-is-accepting-jobs + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_ADD_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", + IPP_PRINTER_IDLE); + + ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/admin/")) == NULL) + { + fprintf(stderr, "lpadmin: add-printer (enable) failed: %s\n", + ippErrorString(cupsLastError())); + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: add-printer (enable) failed: %s\n", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + return (0); + } +} + + +/* + * 'get_line()' - Get a line that is terminated by a LF, CR, or CR LF. + */ + +static char * /* O - Pointer to buf or NULL on EOF */ +get_line(char *buf, /* I - Line buffer */ + int length, /* I - Length of buffer */ + FILE *fp) /* I - File to read from */ +{ + char *bufptr; /* Pointer into buffer */ + int ch; /* Character from file */ + + + length --; + bufptr = buf; + + while ((ch = getc(fp)) != EOF) + { + if (ch == '\n') + break; + else if (ch == '\r') + { + /* + * Look for LF... + */ + + ch = getc(fp); + if (ch != '\n' && ch != EOF) + ungetc(ch, fp); + + break; + } + + *bufptr++ = ch; + length --; + if (length == 0) + break; + } + + *bufptr = '\0'; + + if (ch == EOF) + return (NULL); + else + return (buf); +} + + +/* + * 'set_printer_device()' - Set the device-uri attribute. + */ + +static int /* O - 0 on success, 1 on fail */ +set_printer_device(http_t *http, /* I - Server connection */ + char *printer, /* I - Printer */ + char *device) /* I - New device URI */ +{ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ + + + DEBUG_printf(("set_printer_device(%p, \"%s\", \"%s\")\n", http, printer, + device)); + + /* + * Build a CUPS_ADD_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_ADD_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Add the device URI... + */ + + if (device[0] == '/') + { + /* + * Convert filename to URI... + */ + + snprintf(uri, sizeof(uri), "file:%s", device); + ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL, + uri); + } + else + ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_URI, "device-uri", NULL, + device); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/admin/")) == NULL) + { + fprintf(stderr, "lpadmin: add-printer (set device) failed: %s\n", + ippErrorString(cupsLastError())); + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: add-printer (set device) failed: %s\n", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + return (0); + } +} + + +/* + * 'set_printer_file()' - Set the interface script or PPD file. + */ + +static int /* O - 0 on success, 1 on fail */ +set_printer_file(http_t *http, /* I - Server connection */ + char *printer, /* I - Printer */ + char *file) /* I - PPD file or interface script */ +{ + ipp_status_t status; /* IPP status code */ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ +#ifdef HAVE_LIBZ + char tempfile[1024]; /* Temporary filename */ + int fd; /* Temporary file */ + gzFile *gz; /* GZIP'd file */ + char buffer[8192]; /* Copy buffer */ + int bytes; /* Bytes in buffer */ + + + DEBUG_printf(("set_printer_file(%p, \"%s\", \"%s\")\n", http, printer, + file)); + + /* + * See if the file is gzip'd; if so, unzip it to a temporary file and + * send the uncompressed file. + */ + + if (strcmp(file + strlen(file) - 3, ".gz") == 0) + { + /* + * Yes, the file is compressed; uncompress to a temp file... + */ + + if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) + { + perror("lpadmin: Unable to create temporary file"); + return (1); + } + + if ((gz = gzopen(file, "rb")) == NULL) + { + perror("lpadmin: Unable to open file"); + close(fd); + unlink(tempfile); + return (1); + } + + while ((bytes = gzread(gz, buffer, sizeof(buffer))) > 0) + write(fd, buffer, bytes); + + close(fd); + gzclose(gz); + + file = tempfile; + } +#endif /* HAVE_LIBZ */ + + /* + * Build a CUPS_ADD_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_ADD_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoFileRequest(http, request, "/admin/", file)) == NULL) + status = cupsLastError(); + else + { + status = response->request.status.status_code; + ippDelete(response); + } + +#ifdef HAVE_LIBZ + /* + * Remove the temporary file as needed... + */ + + if (file == tempfile) + unlink(tempfile); +#endif /* HAVE_LIBZ */ + + if (status > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: add-printer (set model) failed: %s\n", + ippErrorString(status)); + + return (1); + } + else + return (0); +} + + +/* + * 'set_printer_info()' - Set the printer description string. + */ + +static int /* O - 0 on success, 1 on fail */ +set_printer_info(http_t *http, /* I - Server connection */ + char *printer, /* I - Printer */ + char *info) /* I - New description string */ +{ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ + + + DEBUG_printf(("set_printer_info(%p, \"%s\", \"%s\")\n", http, printer, + info)); + + /* + * Build a CUPS_ADD_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_ADD_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Add the info string... + */ + + ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-info", NULL, + info); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/admin/")) == NULL) + { + fprintf(stderr, "lpadmin: add-printer (set description) failed: %s\n", + ippErrorString(cupsLastError())); + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: add-printer (set description) failed: %s\n", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + return (0); + } +} + + +/* + * 'set_printer_location()' - Set the printer location string. + */ + +static int /* O - 0 on success, 1 on fail */ +set_printer_location(http_t *http, /* I - Server connection */ + char *printer, /* I - Printer */ + char *location) /* I - New location string */ +{ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ + + + DEBUG_printf(("set_printer_location(%p, \"%s\", \"%s\")\n", http, printer, + location)); + + /* + * Build a CUPS_ADD_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_ADD_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Add the location string... + */ + + ippAddString(request, IPP_TAG_PRINTER, IPP_TAG_TEXT, "printer-location", NULL, + location); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/admin/")) == NULL) + { + fprintf(stderr, "lpadmin: add-printer (set location) failed: %s\n", + ippErrorString(cupsLastError())); + + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: add-printer (set location) failed: %s\n", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + return (0); + } +} + + +/* + * 'set_printer_model()' - Set the driver model file. + */ + +static int /* O - 0 on success, 1 on fail */ +set_printer_model(http_t *http, /* I - Server connection */ + char *printer, /* I - Printer */ + char *model) /* I - Driver model file */ +{ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + cups_lang_t *language; /* Default language */ + char uri[HTTP_MAX_URI]; /* URI for printer/class */ + + + /* + * Build a CUPS_ADD_PRINTER request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * ppd-name + */ + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + request = ippNew(); + + request->request.op.operation_id = CUPS_ADD_PRINTER; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "ppd-name", NULL, model); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/admin/")) == NULL) + { + fprintf(stderr, "lpadmin: add-printer (set model) failed: %s\n", + ippErrorString(cupsLastError())); + + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: add-printer (set model) failed: %s\n", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + return (0); + } +} + + +/* + * '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 */ +{ + ipp_t *request, /* IPP Request */ + *response; /* IPP Response */ + ipp_attribute_t *attr; /* IPP attribute */ + cups_lang_t *language; /* Default language */ + ipp_op_t op; /* Operation to perform */ + const char *val, /* Option value */ + *ppdfile; /* PPD filename */ + 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 */ + FILE *in, /* PPD file */ + *out; /* Temporary file */ + int outfd; /* Temporary file descriptor */ + const char *protocol; /* Protocol */ + + + DEBUG_printf(("set_printer_options(%p, \"%s\", %d, %p)\n", http, printer, + num_options, options)); + + language = cupsLangDefault(); + + snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", printer); + + /* + * Build a GET_PRINTER_ATTRIBUTES request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requested-attributes + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; + request->request.op.request_id = 1; + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + 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"); + + /* + * Do the request... + */ + + op = CUPS_ADD_PRINTER; + + if ((response = cupsDoRequest(http, request, "/")) != NULL) + { + /* + * See what kind of printer or class it is... + */ + + if ((attr = ippFindAttribute(response, "printer-type", IPP_TAG_ENUM)) != NULL) + { + if (attr->values[0].integer & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT)) + { + op = CUPS_ADD_CLASS; + snprintf(uri, sizeof(uri), "ipp://localhost/classes/%s", printer); + } + } + + ippDelete(response); + } + + /* + * Build a CUPS_ADD_PRINTER or CUPS_ADD_CLASS request, which requires + * the following attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * other options + */ + + request = ippNew(); + + request->request.op.operation_id = op; + request->request.op.request_id = 1; + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cupsLangEncoding(language)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + /* + * Add the options... + */ + + cupsEncodeOptions(request, num_options, options); + + if (op == CUPS_ADD_PRINTER) + ppdfile = cupsGetPPD(printer); + else + ppdfile = NULL; + + if (ppdfile != NULL) + { + /* + * Set default options in the PPD file... + */ + + if ((outfd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) + { + fprintf(stderr, "lpadmin: Unable to create temporary file - %s\n", + strerror(errno)); + ippDelete(request); + unlink(ppdfile); + return (1); + } + + if ((in = fopen(ppdfile, "rb")) == NULL) + { + fprintf(stderr, "lpadmin: Unable to open PPD file \"%s\" - %s\n", + ppdfile, strerror(errno)); + ippDelete(request); + unlink(ppdfile); + close(outfd); + unlink(tempfile); + return (1); + } + + out = fdopen(outfd, "wb"); + protocol = cupsGetOption("protocol", num_options, options); + + while (get_line(line, sizeof(line), in) != NULL) + { + if (!strncmp(line, "*cupsProtocol:", 14) && protocol) + { + /* + * Set a new output protocol (BCP or TBCP) below... + */ + + continue; + } + else if (strncmp(line, "*Default", 8)) + fprintf(out, "%s\n", line); + else + { + /* + * Get default option name... + */ + + strlcpy(keyword, line + 8, sizeof(keyword)); + + for (keyptr = keyword; *keyptr; keyptr ++) + if (*keyptr == ':' || isspace(*keyptr)) + break; + + *keyptr = '\0'; + + if (strcmp(keyword, "PageRegion") == 0) + val = cupsGetOption("PageSize", num_options, options); + else + val = cupsGetOption(keyword, num_options, options); + + if (val != NULL) + fprintf(out, "*Default%s: %s\n", keyword, val); + else + fprintf(out, "%s\n", line); + } + } + + if (protocol) + fprintf(out, "*cupsProtocol: \"%s\"\n", protocol); + + fclose(in); + fclose(out); + close(outfd); + + /* + * Do the request... + */ + + response = cupsDoFileRequest(http, request, "/admin/", tempfile); + + /* + * Clean up temp files... (TODO: catch signals in case we CTRL-C during + * lpadmin) + */ + + unlink(ppdfile); + unlink(tempfile); + } + else + { + /* + * No PPD file - just set the options... + */ + + response = cupsDoRequest(http, request, "/admin/"); + } + + /* + * Check the response... + */ + + if (response == NULL) + { + fprintf(stderr, "lpadmin: %s failed: %s\n", + op == CUPS_ADD_PRINTER ? "add-printer" : "add-class", + ippErrorString(cupsLastError())); + + return (1); + } + else if (response->request.status.status_code > IPP_OK_CONFLICT) + { + fprintf(stderr, "lpadmin: %s failed: %s\n", + op == CUPS_ADD_PRINTER ? "add-printer" : "add-class", + ippErrorString(response->request.status.status_code)); + + ippDelete(response); + + return (1); + } + else + { + ippDelete(response); + + return (0); + } +} + + +/* + * 'validate_name()' - Make sure the printer name only contains valid chars, + * replace spaces with dashes and make all letters lowercase. + */ + +static int /* O - 0 if name is no good, 1 if name is good */ +validate_name(char *name) /* I - Name to check */ +{ + char *ptr; /* Pointer into name */ + + + /* + * Scan the whole name... + */ + + for (ptr = name; *ptr; ptr ++) + if (*ptr == '@') + break; + else if (*ptr == ' ') + *ptr = '-'; + else if ((*ptr >= 0 && *ptr < ' ') || *ptr == 127 || *ptr == '/') + return (0); + else + *ptr = tolower(*ptr); + + /* + * All the characters are good; validate the length, too... + */ + + return ((ptr - name) < 128); +} + +/* + * 'construct_printer_name()' - creates a name by taking the product name + * and appending it with a unique substring + * of the udi. + * + */ +static char * +construct_printer_name(const char *udi, const char *product) +{ + char *postfix_start; + char *postfix_end; + char *postfix; + char *printer; + + postfix_start = rindex(udi, '-'); + if (postfix_start == NULL) + { + /*postfix = "-1"*/ + postfix = malloc(sizeof(char) * 3); + if (!postfix) + { + fprintf(stderr, "ERROR: Out of memory\n"); + return NULL; + } + + strcpy(postfix, "-1"); + } + else + { + postfix_end = index(postfix_start, '_'); + if (postfix_end == NULL) + { + postfix = malloc(sizeof(char) * strlen(postfix_start) + 1); + if (!postfix) + { + fprintf(stderr, "ERROR: Out of memory\n"); + return NULL; + } + + strcpy(postfix, postfix_start); + } else { + int length = ((int)postfix_end) - ((int)postfix_start); + + postfix = malloc(sizeof(char) * length + 1); + if (!postfix) + { + fprintf(stderr, "ERROR: Out of memory\n"); + return NULL; + } + + strncpy(postfix, postfix_start, length); + postfix[length] = '\0'; + } + + + } + + printer = malloc(sizeof(char) * (strlen(postfix) + strlen(product) + 1)); + + if (!printer) + { + fprintf(stderr, "ERROR: Out of memory\n"); + return NULL; + } + + sprintf(printer, "%s%s", product, postfix); + free(postfix); + + if (!validate_name(printer)) + { + fprintf(stderr, "Printer name %s, contains non printable characters\n", printer); + free(printer); + return NULL; + } + + return printer; +} + +static int +send_dbus_signal_printer_needs_config() +{ + DBusConnection *connection; + DBusError error; + DBusMessage *message; + DBusMessageIter iter; + + dbus_error_init (&error); + connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error); + if (connection == NULL) { + dbus_error_free (&error); + return -1; + } + + message = dbus_message_new_signal ("/org/cups/PrinterAdmin", + "org.cups.PrinterAdmin", + "PrinterNeedsConfig"); + + dbus_message_append_iter_init (message, &iter); + dbus_message_iter_append_string (&iter, ""); /*manufacturer*/ + dbus_message_iter_append_string (&iter, ""); /*model*/ + dbus_message_iter_append_string (&iter, "reserved"); /*suggested driver*/ + + + dbus_connection_send (connection, message, NULL); + dbus_connection_flush (connection); + dbus_message_unref (message); + dbus_connection_unref (connection); +}
Attachment:
printer_remove.sh
Description: application/shellscript
Attachment:
printer_update.sh
Description: application/shellscript