[Utopia] [patch] hal_lpadmin take 1 - add and remove cups printers with hal UDI's



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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]