diff -up cups-filters-1.0.35/utils/cups-browsed.c.browsed-efficiency cups-filters-1.0.35/utils/cups-browsed.c
--- cups-filters-1.0.35/utils/cups-browsed.c.browsed-efficiency 2015-06-25 16:48:33.667228224 +0100
+++ cups-filters-1.0.35/utils/cups-browsed.c 2015-06-25 16:48:48.215194502 +0100
@@ -23,15 +23,16 @@
#include <ctype.h>
#include <errno.h>
-#include <ifaddrs.h>
#if defined(__OpenBSD__)
#include <sys/socket.h>
#endif /* __OpenBSD__ */
+#include <sys/types.h>
#include <net/if.h>
#include <netinet/in.h>
+#include <ifaddrs.h>
#include <resolv.h>
#include <stdio.h>
-#include <sys/types.h>
+#include <sys/stat.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>
@@ -62,17 +63,20 @@
/* Status of remote printer */
typedef enum printer_status_e {
- STATUS_UNCONFIRMED = 0,
- STATUS_CONFIRMED,
- STATUS_TO_BE_CREATED,
- STATUS_BROWSE_PACKET_RECEIVED,
- STATUS_DISAPPEARED
+ STATUS_UNCONFIRMED = 0, /* Generated in a previous session */
+ STATUS_CONFIRMED, /* Avahi confirms UNCONFIRMED printer */
+ STATUS_TO_BE_CREATED, /* Scheduled for creation */
+ STATUS_BROWSE_PACKET_RECEIVED,/* Scheduled for creation with timeout */
+ STATUS_DISAPPEARED /* Scheduled for removal */
} printer_status_t;
/* Data structure for remote printers */
typedef struct remote_printer_s {
char *name;
char *uri;
+ char *ppd;
+ char *model;
+ char *ifscript;
printer_status_t status;
time_t timeout;
int duplicate;
@@ -100,13 +104,62 @@ typedef struct allow_s {
http_addr_t mask;
} allow_t;
+/* Data struct for a printer discovered using BrowsePoll */
+typedef struct browsepoll_printer_s {
+ char *uri_supported;
+ char *info;
+} browsepoll_printer_t;
+
+/* Data structure for a BrowsePoll server */
+typedef struct browsepoll_s {
+ char *server;
+ int port;
+ int major;
+ int minor;
+ gboolean can_subscribe;
+ int subscription_id;
+ int sequence_number;
+
+ /* Remember which printers we discovered. This way we can just ask
+ * if anything has changed, and if not we know these printers are
+ * still there. */
+ GList *printers; /* of browsepoll_printer_t */
+} browsepoll_t;
+
+/* Local printer (key is name) */
+typedef struct local_printer_s {
+ char *device_uri;
+ gboolean cups_browsed_controlled;
+} local_printer_t;
+
+/* Browse data to send for local printer */
+typedef struct browse_data_s {
+ int type;
+ int state;
+ char *uri;
+ char *location;
+ char *info;
+ char *make_model;
+ char *browse_options;
+} browse_data_t;
+
cups_array_t *remote_printers;
static cups_array_t *netifs;
static cups_array_t *browseallow;
+static gboolean browseallow_all = FALSE;
+
+static GHashTable *local_printers;
+static browsepoll_t *local_printers_context = NULL;
+static http_t *local_conn = NULL;
+static gboolean inhibit_local_printers_update = FALSE;
+
+static GList *browse_data = NULL;
static GMainLoop *gmainloop = NULL;
#ifdef HAVE_AVAHI
static AvahiGLibPoll *glib_poll = NULL;
+static AvahiClient *client = NULL;
+static AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL;
#endif /* HAVE_AVAHI */
static guint queues_timer_id = (guint) -1;
static int browsesocket = -1;
@@ -118,12 +171,22 @@ static unsigned int BrowseRemoteProtocol
static unsigned int BrowseInterval = 60;
static unsigned int BrowseTimeout = 300;
static uint16_t BrowsePort = 631;
-static char **BrowsePoll = NULL;
+static browsepoll_t **BrowsePoll = NULL;
static size_t NumBrowsePoll = 0;
+static char *DomainSocket = NULL;
+static unsigned int CreateIPPPrinterQueues = 0;
+static int autoshutdown = 0;
+static int autoshutdown_avahi = 0;
+static int autoshutdown_timeout = 30;
+static guint autoshutdown_exec_id = 0;
static int debug = 0;
static void recheck_timer (void);
+static void browse_poll_create_subscription (browsepoll_t *context,
+ http_t *conn);
+static gboolean browse_poll_get_notifications (browsepoll_t *context,
+ http_t *conn);
#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
#define HAVE_CUPS_1_6 1
@@ -211,6 +274,15 @@ ippNextAttribute(ipp_t *ipp)
return (ipp->current = ipp->current->next);
}
+int
+ippSetVersion(ipp_t *ipp, int major, int minor)
+{
+ if (!ipp || major < 0 || minor < 0)
+ return (0);
+ ipp->request.any.version[0] = major;
+ ipp->request.any.version[1] = minor;
+ return (1);
+}
#endif
void debug_printf(const char *format, ...) {
@@ -223,16 +295,425 @@ void debug_printf(const char *format, ..
}
}
+static const char *
+password_callback (const char *prompt,
+ http_t *http,
+ const char *method,
+ const char *resource,
+ void *user_data)
+{
+ return NULL;
+}
+
+static http_t *
+http_connect_local (void)
+{
+ if (!local_conn)
+ local_conn = httpConnectEncrypt(cupsServer(), ippPort(),
+ cupsEncryption());
+
+ return local_conn;
+}
+
+static void
+http_close_local (void)
+{
+ if (local_conn) {
+ httpClose (local_conn);
+ local_conn = NULL;
+ }
+}
+
+static local_printer_t *
+new_local_printer (const char *device_uri,
+ gboolean cups_browsed_controlled)
+{
+ local_printer_t *printer = g_malloc (sizeof (local_printer_t));
+ printer->device_uri = strdup (device_uri);
+ printer->cups_browsed_controlled = cups_browsed_controlled;
+ return printer;
+}
+
+static void
+free_local_printer (gpointer data)
+{
+ local_printer_t *printer = data;
+ free (printer->device_uri);
+ free (printer);
+}
+
+static gboolean
+local_printer_has_uri (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ local_printer_t *printer = value;
+ char *device_uri = user_data;
+ return g_str_equal (printer->device_uri, device_uri);
+}
+
+static void
+local_printers_create_subscription (http_t *conn)
+{
+ if (!local_printers_context) {
+ local_printers_context = g_malloc0 (sizeof (browsepoll_t));
+ local_printers_context->server = "localhost";
+ local_printers_context->port = BrowsePort;
+ local_printers_context->can_subscribe = TRUE;
+ }
+
+ browse_poll_create_subscription (local_printers_context, conn);
+}
+
+static void
+get_local_printers (void)
+{
+ cups_dest_t *dests = NULL;
+ int num_dests = cupsGetDests (&dests);
+ debug_printf ("cups-browsed [BrowsePoll localhost:631]: cupsGetDests\n");
+ g_hash_table_remove_all (local_printers);
+ for (int i = 0; i < num_dests; i++) {
+ const char *val;
+ cups_dest_t *dest = &dests[i];
+ local_printer_t *printer;
+ gboolean cups_browsed_controlled;
+ const char *device_uri = cupsGetOption ("device-uri",
+ dest->num_options,
+ dest->options);
+ val = cupsGetOption (CUPS_BROWSED_MARK,
+ dest->num_options,
+ dest->options);
+ cups_browsed_controlled = val && (!strcasecmp (val, "yes") ||
+ !strcasecmp (val, "on") ||
+ !strcasecmp (val, "true"));
+ printer = new_local_printer (device_uri,
+ cups_browsed_controlled);
+ g_hash_table_insert (local_printers,
+ g_strdup (dest->name),
+ printer);
+ }
+
+ cupsFreeDests (num_dests, dests);
+}
+
+static browse_data_t *
+new_browse_data (int type, int state, const gchar *uri,
+ const gchar *location, const gchar *info,
+ const gchar *make_model, const gchar *browse_options)
+{
+ browse_data_t *data = g_malloc (sizeof (browse_data_t));
+ data->type = type;
+ data->state = state;
+ data->uri = g_strdup (uri);
+ data->location = g_strdup (location);
+ data->info = g_strdup (info);
+ data->make_model = g_strdup (make_model);
+ data->browse_options = g_strdup (browse_options);
+ return data;
+}
+
+static void
+browse_data_free (gpointer data)
+{
+ browse_data_t *bdata = data;
+ g_free (bdata->uri);
+ g_free (bdata->location);
+ g_free (bdata->info);
+ g_free (bdata->make_model);
+ g_free (bdata->browse_options);
+ g_free (bdata);
+}
+
+static void
+prepare_browse_data (void)
+{
+ static const char * const rattrs[] = { "printer-type",
+ "printer-state",
+ "printer-uri-supported",
+ "printer-info",
+ "printer-location",
+ "printer-make-and-model",
+ "auth-info-required",
+ "printer-uuid",
+ "job-template" };
+ ipp_t *request, *response = NULL;
+ ipp_attribute_t *attr;
+ http_t *conn = NULL;
+
+ conn = http_connect_local ();
+
+ if (conn == NULL) {
+ debug_printf("cups-browsed: browse send failed to connect to localhost\n");
+ goto fail;
+ }
+
+ request = ippNewRequest(CUPS_GET_PRINTERS);
+ ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
+ NULL, rattrs);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+
+ debug_printf("cups-browsed: preparing browse data\n");
+ response = cupsDoRequest (conn, request, "/");
+ if (cupsLastError() > IPP_OK_CONFLICT) {
+ debug_printf("cups-browsed: browse send failed for localhost: %s\n",
+ cupsLastErrorString ());
+ goto fail;
+ }
+
+ g_list_free_full (browse_data, browse_data_free);
+ browse_data = NULL;
+ for (attr = ippFirstAttribute(response); attr;
+ attr = ippNextAttribute(response)) {
+ int type = -1, state = -1;
+ const char *uri = NULL;
+ gchar *location = NULL;
+ gchar *info = NULL;
+ gchar *make_model = NULL;
+ GString *browse_options = g_string_new ("");
+
+ /* Skip any non-printer attributes */
+ while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
+ attr = ippNextAttribute(response);
+
+ if (!attr)
+ break;
+
+ while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
+ const char *attrname = ippGetName(attr);
+ int value_tag = ippGetValueTag(attr);
+
+ if (!strcasecmp(attrname, "printer-type") &&
+ value_tag == IPP_TAG_ENUM) {
+ type = ippGetInteger(attr, 0);
+ if (type & CUPS_PRINTER_NOT_SHARED) {
+ /* Skip CUPS queues not marked as shared */
+ state = -1;
+ type = -1;
+ break;
+ }
+ } else if (!strcasecmp(attrname, "printer-state") &&
+ value_tag == IPP_TAG_ENUM)
+ state = ippGetInteger(attr, 0);
+ else if (!strcasecmp(attrname, "printer-uri-supported") &&
+ value_tag == IPP_TAG_URI)
+ uri = ippGetString(attr, 0, NULL);
+ else if (!strcasecmp(attrname, "printer-location") &&
+ value_tag == IPP_TAG_TEXT) {
+ /* Remove quotes */
+ gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
+ location = g_strjoinv ("", tokens);
+ g_strfreev (tokens);
+ } else if (!strcasecmp(attrname, "printer-info") &&
+ value_tag == IPP_TAG_TEXT) {
+ /* Remove quotes */
+ gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
+ info = g_strjoinv ("", tokens);
+ g_strfreev (tokens);
+ } else if (!strcasecmp(attrname, "printer-make-and-model") &&
+ value_tag == IPP_TAG_TEXT) {
+ /* Remove quotes */
+ gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
+ make_model = g_strjoinv ("", tokens);
+ g_strfreev (tokens);
+ } else if (!strcasecmp(attrname, "auth-info-required") &&
+ value_tag == IPP_TAG_KEYWORD) {
+ if (strcasecmp (ippGetString(attr, 0, NULL), "none"))
+ g_string_append_printf (browse_options, "auth-info-required=%s ",
+ ippGetString(attr, 0, NULL));
+ } else if (!strcasecmp(attrname, "printer-uuid") &&
+ value_tag == IPP_TAG_URI)
+ g_string_append_printf (browse_options, "uuid=%s ",
+ ippGetString(attr, 0, NULL));
+ else if (!strcasecmp(attrname, "job-sheets-default") &&
+ value_tag == IPP_TAG_NAME &&
+ ippGetCount(attr) == 2)
+ g_string_append_printf (browse_options, "job-sheets=%s,%s ",
+ ippGetString(attr, 0, NULL),
+ ippGetString(attr, 1, NULL));
+ else if (strstr(attrname, "-default")) {
+ gchar *name = g_strdup (attrname);
+ gchar *value = NULL;
+ *strstr (name, "-default") = '\0';
+
+ switch (value_tag) {
+ gchar **tokens;
+
+ case IPP_TAG_KEYWORD:
+ case IPP_TAG_STRING:
+ case IPP_TAG_NAME:
+ /* Escape value */
+ tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
+ " \"\'\\", -1);
+ value = g_strjoinv ("\\", tokens);
+ g_strfreev (tokens);
+ break;
+
+ default:
+ /* other values aren't needed? */
+ debug_printf("cups-browsed: skipping %s (%d)\n", name, value_tag);
+ break;
+ }
+
+ if (value) {
+ g_string_append_printf (browse_options, "%s=%s ", name, value);
+ g_free (value);
+ }
+
+ g_free (name);
+ }
+
+ attr = ippNextAttribute(response);
+ }
+
+ if (type != -1 && state != -1 && uri && location && info && make_model) {
+ gchar *browse_options_str = g_string_free (browse_options, FALSE);
+ browse_data_t *data;
+ browse_options = NULL;
+ g_strchomp (browse_options_str);
+ data = new_browse_data (type, state, uri, location,
+ info, make_model, browse_options_str);
+ browse_data = g_list_insert (browse_data, data, 0);
+ g_free (browse_options_str);
+ }
+
+ if (make_model)
+ g_free (make_model);
+
+ if (info)
+ g_free (info);
+
+ if (location)
+ g_free (location);
+
+ if (browse_options)
+ g_string_free (browse_options, TRUE);
+
+ if (!attr)
+ break;
+ }
+
+ fail:
+ if (response)
+ ippDelete(response);
+}
+
+static void
+update_local_printers (void)
+{
+ gboolean get_printers = FALSE;
+ http_t *conn;
+
+ if (inhibit_local_printers_update)
+ return;
+
+ conn = http_connect_local ();
+ if (conn &&
+ (!local_printers_context || local_printers_context->can_subscribe)) {
+ if (!local_printers_context ||
+ local_printers_context->subscription_id == -1) {
+ /* No subscription yet. First, create the subscription. */
+ local_printers_create_subscription (conn);
+ get_printers = TRUE;
+ } else
+ /* We already have a subscription, so use it. */
+
+ /* Note: for the moment, browse_poll_get_notifications() just
+ * tells us whether we should re-fetch the printer list, so it
+ * is safe to use here. */
+ get_printers = browse_poll_get_notifications (local_printers_context,
+ conn);
+ } else
+ get_printers = TRUE;
+
+ if (get_printers) {
+ get_local_printers ();
+
+ if (BrowseLocalProtocols & BROWSE_CUPS)
+ prepare_browse_data ();
+ }
+}
+
+gboolean
+autoshutdown_execute (gpointer data)
+{
+ /* Are we still in auto shutdown mode and are we still without queues */
+ if (autoshutdown && cupsArrayCount(remote_printers) == 0) {
+ debug_printf("cups-browsed: Automatic shutdown as there are no print queues maintained by us for %d sec.\n",
+ autoshutdown_timeout);
+ g_main_loop_quit(gmainloop);
+ }
+
+ /* Stop this timeout handler, we needed it only once */
+ return FALSE;
+}
+
static remote_printer_t *
create_local_queue (const char *name,
const char *uri,
const char *host,
const char *info,
const char *type,
- const char *domain)
+ const char *domain,
+ const char *pdl,
+ const char *make_model,
+ int is_cups_queue)
{
remote_printer_t *p;
remote_printer_t *q;
+ int fd = 0; /* Script file descriptor */
+ char tempfile[1024]; /* Temporary file */
+ char buffer[8192]; /* Buffer for creating script */
+ int bytes;
+ const char *cups_serverbin; /* CUPS_SERVERBIN environment variable */
+#if 0
+ int i, uri_status, port, status;
+ http_t *http;
+ char scheme[10], userpass[1024], host_name[1024], resource[1024];
+ ipp_t *request, *response;
+ ipp_attribute_t *attr;
+ static const char * const requested_attrs[] =
+ { /* Requested attributes for getting IPP network printer capabilities */
+ /* Explicit attribute listings for the case that "all" does not cover
+ everything */
+ "job-template",
+ "printer-description",
+ /*"document-format-supported",
+ "color-supported",
+ "pages-per-minute",
+ "pages-per-minute-color",
+ "media-supported",
+ "media-ready",
+ "media-default",
+ "media-type-supported",
+ "media-source-supported",*/
+ "media-col-database",
+ /*"sides-supported",
+ "sides-default",
+ "output-bin-supported",
+ "output-bin-default",
+ "finishings-supported",
+ "finishings-default",
+ "print-color-mode-supported",
+ "print-color-mode-default",
+ "output-mode-supported",
+ "output-mode-default",
+ "print-quality-supported",
+ "print-quality-default",
+ "printer-resolution-supported",
+ "printer-resolution-default",
+ "copies-supported",
+ "copies-default",*/
+ /* Catch things which were forgotten above or newly introduced */
+ "all"
+ };
+ static int versions_to_try[] =
+ {
+ 20,
+ 11
+ };
+#endif /* 0 */
/* Mark this as a queue to be created locally pointing to the printer */
if ((p = (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
@@ -264,39 +745,219 @@ create_local_queue (const char *name,
goto fail;
p->domain = strdup (domain);
- if (!p->domain) {
- fail:
- debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
- free (p->type);
- free (p->service_name);
- free (p->host);
- free (p->uri);
- free (p->name);
- free (p);
- return NULL;
- }
+ if (!p->domain)
+ goto fail;
/* Schedule for immediate creation of the CUPS queue */
p->status = STATUS_TO_BE_CREATED;
p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
- /* Check whether we have an equally named queue already from another
- server */
- for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
- q;
- q = (remote_printer_t *)cupsArrayNext(remote_printers))
- if (!strcmp(q->name, p->name))
- break;
- p->duplicate = q ? 1 : 0;
+ if (is_cups_queue) {
+ /* Our local queue must be raw, so that the PPD file and driver
+ on the remote CUPS server get used */
+ p->ppd = NULL;
+ p->model = NULL;
+ p->ifscript = NULL;
+ /* Check whether we have an equally named queue already from another
+ server */
+ for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
+ q;
+ q = (remote_printer_t *)cupsArrayNext(remote_printers))
+ if (!strcasecmp(q->name, p->name))
+ break;
+ p->duplicate = (q && q->status != STATUS_DISAPPEARED &&
+ q->status != STATUS_UNCONFIRMED) ? 1 : 0;
+ if (p->duplicate)
+ debug_printf("cups-browsed: Printer %s already available through host %s.\n",
+ p->name, q->host);
+ else if (q) {
+ q->duplicate = 1;
+ debug_printf("cups-browsed: Unconfirmed/disappeared printer %s already available through host %s, marking that printer duplicate of the newly found one.\n",
+ p->name, q->host);
+ }
+ } else {
+ /* Non-CUPS printer broadcasts are most probably from printers
+ directly connected to the network and using the IPP protocol.
+ We check whether we can set them up without a device-specific
+ driver, only using page description languages which the
+ operating system provides: PCL 5c/5e/6/XL, PostScript, PDF, PWG
+ Raster. Especially IPP Everywhere printers and PDF-capable
+ AirPrint printers will work this way. Making only driverless
+ queues we can get an easy, configuration-less way to print
+ from mobile devices, even if there is no CUPS server with
+ shared printers around. */
+
+ if (CreateIPPPrinterQueues == 0) {
+ debug_printf("cups-browsed: Printer %s (%s) is an IPP network printer and cups-browsed id not configured to set up such printers automatically, ignoring this printer.\n",
+ p->name, p->uri);
+ goto fail;
+ }
+
+ if (!pdl || pdl[0] == '\0' || (!strcasestr(pdl, "application/postscript") && !strcasestr(pdl, "application/pdf") && !strcasestr(pdl, "image/pwg-raster") && !strcasestr(pdl, "application/vnd.hp-PCL") && !strcasestr(pdl, "application/vnd.hp-PCLXL"))) {
+ debug_printf("cups-browsed: Cannot create remote printer %s (%s) as its PDLs are not known, ignoring this printer.\n",
+ p->name, p->uri);
+ goto fail;
+ }
+
+#if 0
+ uri_status = httpSeparateURI(HTTP_URI_CODING_ALL, uri,
+ scheme, sizeof(scheme),
+ userpass, sizeof(userpass),
+ host_name, sizeof(host_name),
+ &(port),
+ resource, sizeof(resource));
+ if (uri_status != HTTP_URI_OK)
+ goto fail;
+ if ((http = httpConnect(host_name, port)) ==
+ NULL) {
+ debug_printf("cups-browsed: Cannot connect to remote printer %s (%s:%d), ignoring this printer.\n",
+ p->uri, host_name, port);
+ goto fail;
+ }
+ for (i = 0;
+ i < sizeof(versions_to_try) / sizeof(versions_to_try[0]);
+ i ++) {
+ /* Create IPP request */
+ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+ /* Set IPP version */
+ ippSetVersion(request, versions_to_try[i] / 10, versions_to_try[i] % 10);
+ /* Printer URI */
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, p->uri);
+ /* Requested IPP attributes */
+ ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes",
+ sizeof(requested_attrs) / sizeof(requested_attrs[0]),
+ NULL, requested_attrs);
+ /* Do it */
+ response = cupsDoRequest(http, request, resource);
+ if (response == NULL) {
+ debug_printf("cups-browsed: No answer to Get-Printer-Attributes IPP request from remote printer %s, ignoring this printer (IPP Error: %s %s).\n",
+ p->uri, ippErrorString(cupsLastError()),
+ cupsLastErrorString());
+ httpClose(http);
+ goto fail;
+ }
+ status = cupsLastError();
+ debug_printf("cups-browsed: Remote printer %s, IPP %3.1f: %s (%s)\n",
+ p->uri, versions_to_try[i] / 10.0,
+ ippErrorString(cupsLastError()),
+ cupsLastErrorString());
+ /* If succeeded, go on, on error try a lower IPP version */
+ if (status < 0x0400)
+ break;
+ }
+ if (i >= sizeof(versions_to_try) / sizeof(versions_to_try[0])) {
+ /* All IPP versions failed */
+ debug_printf("cups-browsed: Remote printer %s: All IPP versions failed\n",
+ p->uri);
+ goto fail;
+ }
+ /* Read out the printer's capabilities */
+ attr = ippFirstAttribute(response);
+ while (attr) {
+ debug_printf("Attr: %s\n",
+ ippGetName(attr));
+ for (i = 0; i < ippGetCount(attr); i ++)
+ debug_printf("Keyword: %s\n",
+ ippGetString(attr, i, NULL));
+ attr = ippNextAttribute(response);
+ }
+ attr = ippFindAttribute(response,
+ "document-format-supported",
+ IPP_TAG_ZERO);
+ if (attr)
+ for (i = 0; i < ippGetCount(attr); i ++)
+ debug_printf("Format: %s\n",
+ ippGetString(attr, i, NULL));
+ else
+ debug_printf("No formats\n");
+
+ /* Clean up */
+ ippDelete(response);
+ httpClose(http);
+#endif /* 0 */
+
+ p->duplicate = 0;
+
+ /*p->model = "drv:///sample.drv/laserjet.ppd";
+ debug_printf("cups-browsed: PPD from system for %s: %s\n", p->name, p->model);*/
+
+ /*p->ppd = "/usr/share/ppd/cupsfilters/pxlcolor.ppd";
+ debug_printf("cups-browsed: PPD from file for %s: %s\n", p->name, p->ppd);*/
+
+ /*p->ifscript = "/usr/lib/cups/filter/pdftoippprinter-wrapper";
+ debug_printf("cups-browsed: System V Interface script for %s: %s\n", p->name, p->ifscript);*/
+
+ if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+ cups_serverbin = CUPS_SERVERBIN;
+
+ if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) {
+ debug_printf("Unable to create interface script file\n");
+ goto fail;
+ }
+
+ debug_printf("Creating temp script file \"%s\"\n", tempfile);
+
+ snprintf(buffer, sizeof(buffer),
+ "#!/bin/sh\n"
+ "# System V interface script for printer %s generated by cups-browsed\n"
+ "\n"
+ "if [ $# -lt 5 -o $# -gt 6 ]; then\n"
+ " echo \"ERROR: $0 job-id user title copies options [file]\" >&2\n"
+ " exit 1\n"
+ "fi\n"
+ "\n"
+ "# Read from given file\n"
+ "if [ -n \"$6\" ]; then\n"
+ " exec \"$0\" \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" < \"$6\"\n"
+ "fi\n"
+ "\n"
+ "extra_options=\"output-format=%s make-and-model=%s\"\n"
+ "\n"
+ "%s/filter/pdftoippprinter \"$1\" \"$2\" \"$3\" \"$4\" \"$5 $extra_options\"\n",
+ p->name, pdl, make_model, cups_serverbin);
+
+ bytes = write(fd, buffer, strlen(buffer));
+ if (bytes != strlen(buffer)) {
+ debug_printf("Unable to write interface script into the file\n");
+ goto fail;
+ }
+
+ close(fd);
+
+ p->ppd = NULL;
+ p->model = NULL;
+ p->ifscript = strdup(tempfile);
+ }
/* Add the new remote printer entry */
cupsArrayAdd(remote_printers, p);
- if (p->duplicate)
- debug_printf("cups-browsed: Printer already available through host %s.\n",
- q->host);
+ /* If auto shutdown is active we have perhaps scheduled a timer to shut down
+ due to not having queues any more to maintain, kill the timer now */
+ if (autoshutdown && autoshutdown_exec_id &&
+ cupsArrayCount(remote_printers) > 0) {
+ debug_printf ("cups-browsed: New printers there to make available, killing auto shutdown timer.\n");
+ g_source_destroy(g_main_context_find_source_by_id(NULL,
+ autoshutdown_exec_id));
+ autoshutdown_exec_id = 0;
+ }
return p;
+
+ fail:
+ debug_printf("cups-browsed: ERROR: Unable to create print queue, ignoring printer.\n");
+ free (p->type);
+ free (p->service_name);
+ free (p->host);
+ free (p->uri);
+ free (p->name);
+ if (p->ppd) free (p->ppd);
+ if (p->model) free (p->model);
+ if (p->ifscript) free (p->ifscript);
+ free (p);
+ return NULL;
}
/*
@@ -414,8 +1075,7 @@ gboolean handle_cups_queues(gpointer unu
/* Remove the CUPS queue */
if (!p->duplicate) { /* Duplicates do not have a CUPS queue */
- if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
- cupsEncryption())) == NULL) {
+ if ((http = http_connect_local ()) == NULL) {
debug_printf("cups-browsed: Unable to connect to CUPS!\n");
p->timeout = current_time + TIMEOUT_RETRY;
break;
@@ -429,7 +1089,6 @@ gboolean handle_cups_queues(gpointer unu
if (num_jobs != 0) { /* error or jobs */
debug_printf("cups-browsed: Queue has still jobs or CUPS error!\n");
cupsFreeJobs(num_jobs, jobs);
- httpClose(http);
/* Schedule the removal of the queue for later */
p->timeout = current_time + TIMEOUT_RETRY;
break;
@@ -454,7 +1113,7 @@ gboolean handle_cups_queues(gpointer unu
if (attr) {
for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER;
attr = ippNextAttribute(response)) {
- if (!strcmp(ippGetName(attr), "printer-name") &&
+ if (!strcasecmp(ippGetName(attr), "printer-name") &&
ippGetValueTag(attr) == IPP_TAG_NAME) {
default_printer_name = ippGetString(attr, 0, NULL);
break;
@@ -469,9 +1128,9 @@ gboolean handle_cups_queues(gpointer unu
!strcasecmp(default_printer_name, p->name)) {
/* Printer is currently the system's default printer,
do not remove it */
- httpClose(http);
/* Schedule the removal of the queue for later */
p->timeout = current_time + TIMEOUT_RETRY;
+ ippDelete(response);
break;
}
if (response)
@@ -492,10 +1151,8 @@ gboolean handle_cups_queues(gpointer unu
if (cupsLastError() > IPP_OK_CONFLICT) {
debug_printf("cups-browsed: Unable to remove CUPS queue!\n");
p->timeout = current_time + TIMEOUT_RETRY;
- httpClose(http);
break;
}
- httpClose(http);
}
/* CUPS queue removed, remove the list entry */
@@ -506,7 +1163,22 @@ gboolean handle_cups_queues(gpointer unu
if (p->service_name) free (p->service_name);
if (p->type) free (p->type);
if (p->domain) free (p->domain);
+ if (p->ppd) free (p->ppd);
+ if (p->model) free (p->model);
+ if (p->ifscript) free (p->ifscript);
free(p);
+ p = NULL;
+
+ /* If auto shutdown is active and all printers we have set up got removed
+ again, schedule the shutdown in autoshutdown_timeout seconds */
+ if (autoshutdown && !autoshutdown_exec_id &&
+ cupsArrayCount(remote_printers) == 0) {
+ debug_printf ("cups-browsed: No printers there any more to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+ autoshutdown_exec_id =
+ g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+ NULL);
+ }
+
break;
/* Bonjour has reported a new remote printer, create a CUPS queue for it,
@@ -530,8 +1202,7 @@ gboolean handle_cups_queues(gpointer unu
p->name);
/* Create a new CUPS queue or modify the existing queue */
- if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
- cupsEncryption())) == NULL) {
+ if ((http = http_connect_local ()) == NULL) {
debug_printf("cups-browsed: Unable to connect to CUPS!\n");
p->timeout = current_time + TIMEOUT_RETRY;
break;
@@ -556,7 +1227,7 @@ gboolean handle_cups_queues(gpointer unu
num_options = cupsAddOption("device-uri", p->uri,
num_options, &options);
/* Option cups-browsed=true, marking that we have created this queue */
- num_options = cupsAddOption("cups-browsed-default", "true",
+ num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true",
num_options, &options);
/* Do not share a queue which serves only to point to a remote printer */
num_options = cupsAddOption("printer-is-shared", "false",
@@ -568,16 +1239,34 @@ gboolean handle_cups_queues(gpointer unu
num_options = cupsAddOption("printer-location", p->host,
num_options, &options);
cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
+ /* PPD from system's CUPS installation */
+ if (p->model) {
+ debug_printf("cups-browsed: Non-raw queue %s with system PPD: %s\n", p->name, p->model);
+ p->ppd = cupsGetServerPPD(http, p->model);
+ }
/* Do it */
- ippDelete(cupsDoRequest(http, request, "/admin/"));
+ if (p->ppd) {
+ debug_printf("cups-browsed: Non-raw queue %s with PPD file: %s\n", p->name, p->ppd);
+ ippDelete(cupsDoFileRequest(http, request, "/admin/", p->ppd));
+ if (p->model) {
+ unlink(p->ppd);
+ free(p->ppd);
+ p->ppd = NULL;
+ }
+ } else if (p->ifscript) {
+ debug_printf("cups-browsed: Non-raw queue %s with interface script: %s\n", p->name, p->ifscript);
+ ippDelete(cupsDoFileRequest(http, request, "/admin/", p->ifscript));
+ unlink(p->ifscript);
+ free(p->ifscript);
+ p->ifscript = NULL;
+ } else
+ ippDelete(cupsDoRequest(http, request, "/admin/"));
cupsFreeOptions(num_options, options);
if (cupsLastError() > IPP_OK_CONFLICT) {
debug_printf("cups-browsed: Unable to create CUPS queue!\n");
p->timeout = current_time + TIMEOUT_RETRY;
- httpClose(http);
break;
}
- httpClose(http);
if (p->status == STATUS_BROWSE_PACKET_RECEIVED) {
p->status = STATUS_DISAPPEARED;
@@ -637,40 +1326,123 @@ recheck_timer (void)
}
}
-void generate_local_queue(const char *host,
- uint16_t port,
- char *resource,
- const char *name,
- const char *type,
- const char *domain) {
- char *remote_queue = NULL, *remote_host = NULL;
+static remote_printer_t *
+generate_local_queue(const char *host,
+ uint16_t port,
+ char *resource,
+ const char *name,
+ const char *type,
+ const char *domain,
+ void *txt) {
+
+ char uri[HTTP_MAX_URI];
+ char *remote_queue = NULL, *remote_host = NULL, *pdl = NULL;
+#ifdef HAVE_AVAHI
+ char *fields[] = { "product", "usb_MDL", "ty", NULL }, **f;
+ AvahiStringList *entry = NULL;
+ char *key = NULL, *value = NULL;
+#endif /* HAVE_AVAHI */
remote_printer_t *p;
+ local_printer_t *local_printer;
char *backup_queue_name = NULL, *local_queue_name = NULL;
- cups_dest_t *dests = NULL, *dest = NULL;
- int i, num_dests;
+ int is_cups_queue;
size_t hl = 0;
- const char *val = NULL;
+ gboolean create = TRUE;
+
- /* This is a remote CUPS queue, find queue name and host name */
- if (strncasecmp(resource, "printers/", 9)) {
- debug_printf("cups-browsed: resource does not begin 'printers/'\n");
- return;
- }
+ is_cups_queue = 0;
+ memset(uri, 0, sizeof(uri));
- remote_queue = remove_bad_chars(resource + 9, 0);
+ /* Determine the device URI of the remote printer */
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
+ (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
+ host, port, "/%s", resource);
/* Find the remote host name.
* Used in constructing backup_queue_name, so need to sanitize.
* strdup() is called inside remove_bad_chars() and result is free()-able.
*/
remote_host = remove_bad_chars(host, 1);
hl = strlen(remote_host);
- if (hl > 6 && !strcmp(remote_host + strlen(remote_host) - 6, ".local"))
- remote_host[strlen(remote_host) - 6] = '\0';
- if (hl > 7 && !strcmp(remote_host + strlen(remote_host) - 7, ".local."))
- remote_host[strlen(remote_host) - 7] = '\0';
- debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
- remote_queue, remote_host);
-
+ if (hl > 6 && !strcasecmp(remote_host + hl - 6, ".local"))
+ remote_host[hl - 6] = '\0';
+ if (hl > 7 && !strcasecmp(remote_host + hl - 7, ".local."))
+ remote_host[hl - 7] = '\0';
+
+ /* Check by the resource whether the discovered printer is a CUPS queue */
+ if (!strncasecmp(resource, "printers/", 9)) {
+ /* This is a remote CUPS queue, use the remote queue name for the
+ local queue */
+ is_cups_queue = 1;
+ /* Not directly used in script generation input later, but taken from
+ packet, so better safe than sorry. (consider second loop with
+ backup_queue_name) */
+ remote_queue = remove_bad_chars(resource + 9, 0);
+ debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
+ remote_queue, remote_host);
+#ifdef HAVE_AVAHI
+ /* If the remote queue has a PPD file, the "product" field of the
+ TXT record is populated. If it has no PPD file the remote queue
+ is a raw queue and so we do not know enough about the printer
+ behind it for auto-creating a local queue pointing to it. */
+ int raw_queue = 0;
+ if (txt) {
+ entry = avahi_string_list_find((AvahiStringList *)txt, "product");
+ if (entry) {
+ avahi_string_list_get_pair(entry, &key, &value, NULL);
+ if (!key || !value || strcasecmp(key, "product") || value[0] != '(' ||
+ value[strlen(value) - 1] != ')') {
+ raw_queue = 1;
+ }
+ } else
+ raw_queue = 1;
+ } else if (domain && domain[0] != '\0')
+ raw_queue = 1;
+ if (raw_queue) {
+ /* The remote CUPS queue is raw, ignore it */
+ debug_printf("cups-browsed: Remote Bonjour-advertised CUPS queue %s on host %s is raw, ignored.\n",
+ remote_queue, remote_host);
+ free (remote_host);
+ return NULL;
+ }
+#endif /* HAVE_AVAHI */
+ } else if (!strncasecmp(resource, "classes/", 8)) {
+ /* This is a remote CUPS queue, use the remote queue name for the
+ local queue */
+ is_cups_queue = 1;
+ /* Not directly used in script generation input later, but taken from
+ packet, so better safe than sorry. (consider second loop with
+ backup_queue_name) */
+ remote_queue = remove_bad_chars(resource + 8, 0);
+ debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
+ remote_queue, remote_host);
+ } else {
+ /* This is an IPP-based network printer */
+ is_cups_queue = 0;
+ /* Determine the queue name by the model */
+ remote_queue = strdup("printer");
+#ifdef HAVE_AVAHI
+ if (txt) {
+ for (f = fields; *f; f ++) {
+ entry = avahi_string_list_find((AvahiStringList *)txt, *f);
+ if (entry) {
+ avahi_string_list_get_pair(entry, &key, &value, NULL);
+ if (key && value && !strcasecmp(key, *f) && strlen(value) >= 3) {
+ remote_queue = remove_bad_chars(value, 0);
+ break;
+ }
+ }
+ }
+ /* Find out which PDLs the printer understands */
+ entry = avahi_string_list_find((AvahiStringList *)txt, "pdl");
+ if (entry) {
+ avahi_string_list_get_pair(entry, &key, &value, NULL);
+ if (key && value && !strcasecmp(key, "pdl") && strlen(value) >= 3) {
+ pdl = remove_bad_chars(value, 1);
+ }
+ }
+ }
+#endif /* HAVE_AVAHI */
+ }
/* Check if there exists already a CUPS queue with the
requested name Try name@host in such a case and if
this is also taken, ignore the printer */
@@ -683,136 +1455,150 @@ void generate_local_queue(const char *ho
sprintf(backup_queue_name, "%s@%s", remote_queue, remote_host);
/* Get available CUPS queues */
- num_dests = cupsGetDests(&dests);
+ update_local_printers ();
local_queue_name = remote_queue;
- if (num_dests > 0) {
+
+ /* Is there a local queue with the same URI as the remote queue? */
+ if (g_hash_table_find (local_printers,
+ local_printer_has_uri,
+ uri))
+ create = FALSE;
+
+ if (create) {
/* Is there a local queue with the name of the remote queue? */
- for (i = num_dests, dest = dests; i > 0; i --, dest ++)
+ local_printer = g_hash_table_lookup (local_printers,
+ local_queue_name);
/* Only consider CUPS queues not created by us */
- if ((((val =
- cupsGetOption(CUPS_BROWSED_MARK, dest->num_options,
- dest->options)) == NULL) ||
- (strcasecmp(val, "yes") != 0 &&
- strcasecmp(val, "on") != 0 &&
- strcasecmp(val, "true") != 0)) &&
- !strcmp(local_queue_name, dest->name))
- break;
- if (i > 0) {
+ if (local_printer && !local_printer->cups_browsed_controlled) {
/* Found local queue with same name as remote queue */
/* Is there a local queue with the name <queue>@<host>? */
local_queue_name = backup_queue_name;
debug_printf("cups-browsed: %s already taken, using fallback name: %s\n",
remote_queue, local_queue_name);
- for (i = num_dests, dest = dests; i > 0; i --, dest ++)
- /* Only consider CUPS queues not created by us */
- if ((((val =
- cupsGetOption(CUPS_BROWSED_MARK, dest->num_options,
- dest->options)) == NULL) ||
- (strcasecmp(val, "yes") != 0 &&
- strcasecmp(val, "on") != 0 &&
- strcasecmp(val, "true") != 0)) &&
- !strcmp(local_queue_name, dest->name))
- break;
- if (i > 0) {
+ local_printer = g_hash_table_lookup (local_printers,
+ local_queue_name);
+ if (local_printer && !local_printer->cups_browsed_controlled) {
/* Found also a local queue with name <queue>@<host>, so
ignore this remote printer */
debug_printf("cups-browsed: %s also taken, printer ignored.\n",
local_queue_name);
free (backup_queue_name);
free (remote_host);
+ free (pdl);
free (remote_queue);
- cupsFreeDests(num_dests, dests);
- return;
+ return NULL;
}
}
- cupsFreeDests(num_dests, dests);
}
/* Check if we have already created a queue for the discovered
printer */
for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
- if (!strcmp(p->name, local_queue_name) &&
+ if (!strcasecmp(p->name, local_queue_name) &&
(p->host[0] == '\0' ||
- !strcmp(p->host, remote_host)))
+ p->status == STATUS_UNCONFIRMED ||
+ p->status == STATUS_DISAPPEARED ||
+ !strcasecmp(p->host, remote_host)))
break;
+ if (!create) {
+ free (remote_host);
+ free (backup_queue_name);
+ free (pdl);
+ free (remote_queue);
+ if (p) {
+ return p;
+ } else {
+ /* Found a local queue with the same URI as our discovered printer
+ would get, so ignore this remote printer */
+ debug_printf("cups-browsed: Printer with URI %s already exists, printer ignored.\n",
+ uri);
+ return NULL;
+ }
+ }
+
if (p) {
/* We have already created a local queue, check whether the
- discovered service allows us to upgrade the queue to IPPS */
- if (strcasestr(type, "_ipps") &&
- !strncmp(p->uri, "ipp:", 4)) {
-
- /* Schedule local queue for upgrade to ipps: */
- if ((p->uri = realloc(p->uri, strlen(p->uri) + 2)) == NULL){
- debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
- exit(1);
- }
- memmove((void *)(p->uri + 4), (const void *)(p->uri + 3),
- strlen(p->uri) - 2);
- p->uri[3] = 's';
+ discovered service allows us to upgrade the queue to IPPS
+ or whether the URI part after ipp(s):// has changed */
+ if ((strcasestr(type, "_ipps") &&
+ !strncasecmp(p->uri, "ipp:", 4)) ||
+ strcasecmp(strchr(p->uri, ':'), strchr(uri, ':'))) {
+
+ /* Schedule local queue for upgrade to ipps: or for URI change */
+ if (strcasestr(type, "_ipps") &&
+ !strncasecmp(p->uri, "ipp:", 4))
+ debug_printf("cups-browsed: Upgrading printer %s (Host: %s) to IPPS. New URI: %s\n",
+ p->name, remote_host, uri);
+ if (strcasecmp(strchr(p->uri, ':'), strchr(uri, ':')))
+ debug_printf("cups-browsed: Changing URI of printer %s (Host: %s) to %s.\n",
+ p->name, remote_host, uri);
+ free(p->uri);
+ free(p->host);
+ free(p->service_name);
+ free(p->type);
+ free(p->domain);
+ p->uri = strdup(uri);
p->status = STATUS_TO_BE_CREATED;
p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
p->host = strdup(remote_host);
p->service_name = strdup(name);
p->type = strdup(type);
p->domain = strdup(domain);
- debug_printf("cups-browsed: Upgrading printer %s (Host: %s) to IPPS. New URI: %s\n",
- p->name, p->host, p->uri);
} else {
/* Nothing to do, mark queue entry as confirmed if the entry
is unconfirmed */
- debug_printf("cups-browsed: Entry for %s (Host: %s, URI: %s) already exists.\n",
- p->name, p->host, p->uri);
- if (p->status == STATUS_UNCONFIRMED) {
+ debug_printf("cups-browsed: Entry for %s (URI: %s) already exists.\n",
+ p->name, p->uri);
+ if (p->status == STATUS_UNCONFIRMED ||
+ p->status == STATUS_DISAPPEARED) {
p->status = STATUS_CONFIRMED;
p->timeout = (time_t) -1;
- debug_printf("cups-browsed: Marking entry for %s (Host: %s, URI: %s) as confirmed.\n",
- p->name, p->host, p->uri);
+ debug_printf("cups-browsed: Marking entry for %s (URI: %s) as confirmed.\n",
+ p->name, p->uri);
}
}
- if (p->host[0] == '\0')
+ if (p->host[0] == '\0') {
+ free (p->host);
p->host = strdup(remote_host);
- if (p->service_name[0] == '\0' && name)
+ }
+ if (p->service_name[0] == '\0' && name) {
+ free (p->service_name);
p->service_name = strdup(name);
- if (p->type[0] == '\0' && type)
+ }
+ if (p->type[0] == '\0' && type) {
+ free (p->type);
p->type = strdup(type);
- if (p->domain[0] == '\0' && domain)
+ }
+ if (p->domain[0] == '\0' && domain) {
+ free (p->domain);
p->domain = strdup(domain);
-
+ }
} else {
/* We need to create a local queue pointing to the
discovered printer */
-
- /* Device URI: ipp(s)://<remote host>:631/printers/<remote queue> */
- char *uri = malloc(strlen(host) +
- strlen(remote_queue) + 34);
- if (uri == NULL) {
- debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
- exit(1);
- }
- sprintf(uri, "ipp%s://%s:%u/printers/%s",
- (strcasestr(type, "_ipps") ? "s" : ""), host,
- port, remote_queue);
-
p = create_local_queue (local_queue_name, uri, remote_host,
- name ? name : "", type, domain);
- free (uri);
+ name ? name : "", type, domain, pdl, remote_queue,
+ is_cups_queue);
}
free (backup_queue_name);
free (remote_host);
+ free (pdl);
free (remote_queue);
if (p)
debug_printf("cups-browsed: Bonjour IDs: Service name: \"%s\", "
"Service type: \"%s\", Domain: \"%s\"\n",
p->service_name, p->type, p->domain);
+
+ return p;
}
#ifdef HAVE_AVAHI
@@ -852,30 +1638,47 @@ static void resolve_callback(
debug_printf("cups-browsed: Avahi Resolver: Service '%s' of type '%s' in domain '%s'.\n",
name, type, domain);
- /* Check if we have a remote CUPS queue, other remote printers are not
- handled by us */
rp_entry = avahi_string_list_find(txt, "rp");
- adminurl_entry = avahi_string_list_find(txt, "adminurl");
- if (rp_entry && adminurl_entry) {
+ if (rp_entry)
avahi_string_list_get_pair(rp_entry, &rp_key, &rp_value, NULL);
+ else {
+ rp_key = strdup("rp");
+ rp_value = strdup("");
+ }
+ adminurl_entry = avahi_string_list_find(txt, "adminurl");
+ if (adminurl_entry)
avahi_string_list_get_pair(adminurl_entry, &adminurl_key,
&adminurl_value, NULL);
+ else {
+ adminurl_key = strdup("adminurl");
+ if ((adminurl_value = malloc(strlen(host_name) + 8)) != NULL)
+ sprintf(adminurl_value, "http://%s", host_name);
+ else
+ adminurl_value = strdup("");
+ }
- /* Check by "rp" and "adminurl" TXT record fields whether
- the discovered printer is a CUPS queue */
- if (rp_key && rp_value && adminurl_key && adminurl_value &&
- !strcmp(rp_key, "rp") && !strncmp(rp_value, "printers/", 9) &&
- !strcmp(adminurl_key, "adminurl") &&
- !strcmp(adminurl_value + strlen(adminurl_value) -
- strlen(rp_value), rp_value)) {
- generate_local_queue(host_name, port, rp_value, name, type, domain);
- }
+ if (rp_key && rp_value && adminurl_key && adminurl_value &&
+ !strcasecmp(rp_key, "rp") && !strcasecmp(adminurl_key, "adminurl")) {
+ /* Check remote printer type and create appropriate local queue to
+ point to it */
+ generate_local_queue(host_name, port, rp_value, name, type, domain, txt);
+ }
+
+ /* Clean up */
- /* Clean up */
+ if (rp_entry) {
avahi_free(rp_key);
avahi_free(rp_value);
+ } else {
+ free(rp_key);
+ free(rp_value);
+ }
+ if (adminurl_entry) {
avahi_free(adminurl_key);
avahi_free(adminurl_value);
+ } else {
+ free(adminurl_key);
+ free(adminurl_value);
}
break;
}
@@ -905,7 +1708,7 @@ static void browse_callback(
switch (event) {
- /* Avah browser error */
+ /* Avahi browser error */
case AVAHI_BROWSER_FAILURE:
debug_printf("cups-browsed: Avahi Browser: ERROR: %s\n",
@@ -947,9 +1750,9 @@ static void browse_callback(
/* Check whether we have listed this printer */
for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
- if (!strcmp(p->service_name, name) &&
- !strcmp(p->type, type) &&
- !strcmp(p->domain, domain))
+ if (!strcasecmp(p->service_name, name) &&
+ !strcasecmp(p->type, type) &&
+ !strcasecmp(p->domain, domain))
break;
if (p) {
/* Check whether this queue has a duplicate from another server */
@@ -958,8 +1761,8 @@ static void browse_callback(
for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
q;
q = (remote_printer_t *)cupsArrayNext(remote_printers))
- if (!strcmp(q->name, p->name) &&
- strcmp(q->host, p->host) &&
+ if (!strcasecmp(q->name, p->name) &&
+ strcasecmp(q->host, p->host) &&
q->duplicate)
break;
}
@@ -970,16 +1773,22 @@ static void browse_callback(
free (p->service_name);
free (p->type);
free (p->domain);
+ if (p->ppd) free (p->ppd);
+ if (p->model) free (p->model);
+ if (p->ifscript) free (p->ifscript);
/* Replace the data with the data of the duplicate printer */
p->uri = strdup(q->uri);
p->host = strdup(q->host);
p->service_name = strdup(q->service_name);
p->type = strdup(q->type);
p->domain = strdup(q->domain);
+ if (q->ppd) p->ppd = strdup(q->ppd);
+ if (q->model) p->model = strdup(q->model);
+ if (q->ifscript) p->ifscript = strdup(q->ifscript);
/* Schedule this printer for updating the CUPS queue */
p->status = STATUS_TO_BE_CREATED;
p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
- /* Schedule the remote printer for removal */
+ /* Schedule the duplicate printer entry for removal */
q->status = STATUS_DISAPPEARED;
q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
@@ -1015,20 +1824,171 @@ static void browse_callback(
}
+void avahi_browser_shutdown() {
+ remote_printer_t *p;
+
+ /* Remove all queues which we have set up based on Bonjour discovery*/
+ for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
+ p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
+ if (p->type && p->type[0]) {
+ p->status = STATUS_DISAPPEARED;
+ p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
+ }
+ }
+ handle_cups_queues(NULL);
+
+ /* Free the data structures for Bonjour browsing */
+ if (sb1) {
+ avahi_service_browser_free(sb1);
+ sb1 = NULL;
+ }
+ if (sb2) {
+ avahi_service_browser_free(sb2);
+ sb2 = NULL;
+ }
+
+ /* Switch on auto shutdown mode */
+ if (autoshutdown_avahi) {
+ autoshutdown = 1;
+ debug_printf("cups-browsed: Avahi server disappeared, switching to auto shutdown mode ...\n");
+ /* If there are no printers schedule the shutdown in autoshutdown_timeout
+ seconds */
+ if (!autoshutdown_exec_id &&
+ cupsArrayCount(remote_printers) == 0) {
+ debug_printf ("cups-browsed: We entered auto shutdown mode and no printers are there to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+ autoshutdown_exec_id =
+ g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+ NULL);
+ }
+ }
+}
+
+void avahi_shutdown() {
+ avahi_browser_shutdown();
+ if (client) {
+ avahi_client_free(client);
+ client = NULL;
+ }
+ if (glib_poll) {
+ avahi_glib_poll_free(glib_poll);
+ glib_poll = NULL;
+ }
+}
+
static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+ int error;
+
assert(c);
/* Called whenever the client or server state changes */
+ switch (state) {
- if (state == AVAHI_CLIENT_FAILURE) {
- debug_printf("cups-browsed: ERROR: Avahi server connection failure: %s\n",
- avahi_strerror(avahi_client_errno(c)));
- g_main_loop_quit(gmainloop);
+ /* avahi-daemon available */
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ case AVAHI_CLIENT_S_COLLISION:
+
+ debug_printf("cups-browsed: Avahi server connection got available, setting up service browsers.\n");
+
+ /* Create the service browsers */
+ if (!sb1)
+ if (!(sb1 =
+ avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ "_ipp._tcp", NULL, 0, browse_callback,
+ c))) {
+ debug_printf("cups-browsed: ERROR: Failed to create service browser for IPP: %s\n",
+ avahi_strerror(avahi_client_errno(c)));
+ }
+ if (!sb2)
+ if (!(sb2 =
+ avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ "_ipps._tcp", NULL, 0, browse_callback,
+ c))) {
+ debug_printf("cups-browsed: ERROR: Failed to create service browser for IPPS: %s\n",
+ avahi_strerror(avahi_client_errno(c)));
+ }
+
+ /* switch off auto shutdown mode */
+ if (autoshutdown_avahi) {
+ autoshutdown = 0;
+ debug_printf("cups-browsed: Avahi server available, switching to permanent mode ...\n");
+ /* If there is still an active auto shutdown timer, kill it */
+ if (autoshutdown_exec_id > 0) {
+ debug_printf ("cups-browsed: We have left auto shutdown mode, killing auto shutdown timer.\n");
+ g_source_destroy(g_main_context_find_source_by_id(NULL,
+ autoshutdown_exec_id));
+ autoshutdown_exec_id = 0;
+ }
+ }
+
+ break;
+
+ /* Avahi client error */
+ case AVAHI_CLIENT_FAILURE:
+
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+ debug_printf("cups-browsed: Avahi server disappeared, shutting down service browsers, removing Bonjour-discovered print queues.\n");
+ avahi_browser_shutdown();
+ /* Renewing client */
+ avahi_client_free(client);
+ client = avahi_client_new(avahi_glib_poll_get(glib_poll),
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback, NULL, &error);
+ if (!client) {
+ debug_printf("cups-browsed: ERROR: Failed to create client: %s\n",
+ avahi_strerror(error));
+ BrowseRemoteProtocols &= ~BROWSE_DNSSD;
+ avahi_shutdown();
+ }
+ } else {
+ debug_printf("cups-browsed: ERROR: Avahi server connection failure: %s\n",
+ avahi_strerror(avahi_client_errno(c)));
+ g_main_loop_quit(gmainloop);
+ }
+ break;
+
+ default:
+ break;
}
+}
+
+void avahi_init() {
+ int error;
+
+ if (BrowseRemoteProtocols & BROWSE_DNSSD) {
+ /* Allocate main loop object */
+ if (!glib_poll)
+ if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
+ debug_printf("cups-browsed: ERROR: Failed to create glib poll object.\n");
+ goto avahi_init_fail;
+ }
+
+ /* Allocate a new client */
+ if (!client)
+ client = avahi_client_new(avahi_glib_poll_get(glib_poll),
+ AVAHI_CLIENT_NO_FAIL,
+ client_callback, NULL, &error);
+ /* Check wether creating the client object succeeded */
+ if (!client) {
+ debug_printf("cups-browsed: ERROR: Failed to create client: %s\n",
+ avahi_strerror(error));
+ goto avahi_init_fail;
+ }
+
+ return;
+
+ avahi_init_fail:
+ BrowseRemoteProtocols &= ~BROWSE_DNSSD;
+ avahi_shutdown();
+ }
}
#endif /* HAVE_AVAHI */
+/*
+ * A CUPS printer has been discovered via CUPS Browsing
+ * or with BrowsePoll
+ */
void
found_cups_printer (const char *remote_host, const char *uri,
const char *info)
@@ -1041,6 +2001,7 @@ found_cups_printer (const char *remote_h
netif_t *iface;
char local_resource[HTTP_MAX_URI];
char *c;
+ remote_printer_t *printer;
memset(scheme, 0, sizeof(scheme));
memset(username, 0, sizeof(username));
@@ -1059,7 +2020,7 @@ found_cups_printer (const char *remote_h
for (iface = cupsArrayFirst (netifs);
iface;
iface = cupsArrayNext (netifs))
- if (!strcmp (host, iface->address))
+ if (!strcasecmp (host, iface->address))
break;
if (iface) {
debug_printf("cups-browsed: ignoring own broadcast on %s\n",
@@ -1067,7 +2028,8 @@ found_cups_printer (const char *remote_h
return;
}
- if (strncmp (resource, "/printers/", 10)) {
+ if (strncasecmp (resource, "/printers/", 10) &&
+ strncasecmp (resource, "/classes/", 9)) {
debug_printf("cups-browsed: don't understand URI: %s\n", uri);
return;
}
@@ -1081,15 +2043,26 @@ found_cups_printer (const char *remote_h
debug_printf("cups-browsed: browsed queue name is %s\n",
local_resource + 9);
- generate_local_queue(host, port, local_resource, info ? info : "", "", "");
+ printer = generate_local_queue(host, port, local_resource,
+ info ? info : "",
+ "", "", NULL);
+
+ if (printer) {
+ if (printer->status == STATUS_TO_BE_CREATED)
+ printer->status = STATUS_BROWSE_PACKET_RECEIVED;
+ else {
+ printer->status = STATUS_DISAPPEARED;
+ printer->timeout = time(NULL) + BrowseTimeout;
+ }
+ }
}
static gboolean
allowed (struct sockaddr *srcaddr)
{
allow_t *allow;
- if (cupsArrayCount(browseallow) == 0) {
- /* No "BrowseAllow" line, allow all servers */
+ if (browseallow_all || cupsArrayCount(browseallow) == 0) {
+ /* "BrowseAllow All", or no "BrowseAllow" line, so allow all servers */
return TRUE;
}
for (allow = cupsArrayFirst (browseallow);
@@ -1237,7 +2210,9 @@ process_browse_data (GIOChannel *source,
if (c >= end)
return TRUE;
- found_cups_printer (remote_host, uri, info);
+ if (!(type & CUPS_PRINTER_DELETE))
+ found_cups_printer (remote_host, uri, info);
+
recheck_timer ();
/* Don't remove this I/O source */
@@ -1326,12 +2301,10 @@ update_netifs (void)
freeifaddrs (ifaddr);
}
-void
-broadcast_browse_packets (int type, int state,
- const char *local_uri, const char *location,
- const char *info, const char *make_model,
- const char *browse_options)
+static void
+broadcast_browse_packets (gpointer data, gpointer user_data)
{
+ browse_data_t *bdata = data;
netif_t *browse;
char packet[2048];
char uri[HTTP_MAX_URI];
@@ -1345,262 +2318,102 @@ broadcast_browse_packets (int type, int
browse != NULL;
browse = (netif_t *)cupsArrayNext (netifs)) {
/* Replace 'localhost' with our IP address on this interface */
- httpSeparateURI(HTTP_URI_CODING_ALL, local_uri,
+ httpSeparateURI(HTTP_URI_CODING_ALL, bdata->uri,
scheme, sizeof(scheme),
username, sizeof(username),
host, sizeof(host),
&port,
resource, sizeof(resource));
- httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
- scheme, username, browse->address, port, resource);
-
- if (snprintf (packet, sizeof (packet),
- "%x " /* type */
- "%x " /* state */
- "%s " /* uri */
- "\"%s\" " /* location */
- "\"%s\" " /* info */
- "\"%s\" " /* make-and-model */
- "lease-duration=%d" /* BrowseTimeout */
- "%s%s" /* other browse options */
- "\n",
- type, state, uri, location,
- info, make_model,
- BrowseTimeout,
- browse_options ? " " : "",
- browse_options ? browse_options : "") >= sizeof (packet)) {
- debug_printf ("cups-browsed: oversize packet not sent\n");
- continue;
- }
-
- debug_printf("cups-browsed: packet to send:\n%s", packet);
-
- int err = sendto (browsesocket, packet,
- strlen (packet), 0,
- &browse->broadcast.addr,
- httpAddrLength (&browse->broadcast));
- if (err)
- debug_printf("cupsd-browsed: sendto returned %d: %s\n",
- err, strerror (errno));
- }
-}
-
-gboolean
-send_browse_data (gpointer data)
-{
- static const char * const rattrs[] = { "printer-type",
- "printer-state",
- "printer-uri-supported",
- "printer-info",
- "printer-location",
- "printer-make-and-model",
- "auth-info-required",
- "printer-uuid",
- "job-template" };
- ipp_t *request, *response = NULL;
- ipp_attribute_t *attr;
- http_t *conn = NULL;
-
- update_netifs ();
- res_init ();
- conn = httpConnectEncrypt ("localhost", BrowsePort,
- HTTP_ENCRYPT_IF_REQUESTED);
-
- if (conn == NULL) {
- debug_printf("cups-browsed: browse send failed to connect to localhost\n");
- goto fail;
- }
-
- request = ippNewRequest(CUPS_GET_PRINTERS);
- ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
- "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
- NULL, rattrs);
- ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
- "requesting-user-name", NULL, cupsUser ());
-
- response = cupsDoRequest (conn, request, "/");
- if (cupsLastError() > IPP_OK_CONFLICT) {
- debug_printf("cups-browsed: browse send failed for localhost: %s\n",
- cupsLastErrorString ());
- goto fail;
- }
-
- for (attr = ippFirstAttribute(response); attr;
- attr = ippNextAttribute(response)) {
- int type = -1, state = -1;
- const char *uri = NULL;
- gchar *location = NULL;
- gchar *info = NULL;
- gchar *make_model = NULL;
- GString *browse_options = g_string_new ("");
-
- /* Skip any non-printer attributes */
- while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
- attr = ippNextAttribute(response);
-
- if (!attr)
- break;
-
- while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
- const char *attrname = ippGetName(attr);
- int value_tag = ippGetValueTag(attr);
-
- if (!strcmp(attrname, "printer-type") &&
- value_tag == IPP_TAG_ENUM) {
- type = ippGetInteger(attr, 0);
- if (type & CUPS_PRINTER_NOT_SHARED) {
- /* Skip CUPS queues not marked as shared */
- state = -1;
- type = -1;
- break;
- }
- } else if (!strcmp(attrname, "printer-state") &&
- value_tag == IPP_TAG_ENUM)
- state = ippGetInteger(attr, 0);
- else if (!strcmp(attrname, "printer-uri-supported") &&
- value_tag == IPP_TAG_URI)
- uri = ippGetString(attr, 0, NULL);
- else if (!strcmp(attrname, "printer-location") &&
- value_tag == IPP_TAG_TEXT) {
- /* Remove quotes */
- gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
- location = g_strjoinv ("", tokens);
- g_strfreev (tokens);
- } else if (!strcmp(attrname, "printer-info") &&
- value_tag == IPP_TAG_TEXT) {
- /* Remove quotes */
- gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
- info = g_strjoinv ("", tokens);
- g_strfreev (tokens);
- } else if (!strcmp(attrname, "printer-make-and-model") &&
- value_tag == IPP_TAG_TEXT) {
- /* Remove quotes */
- gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
- make_model = g_strjoinv ("", tokens);
- g_strfreev (tokens);
- } else if (!strcmp(attrname, "auth-info-required") &&
- value_tag == IPP_TAG_KEYWORD) {
- if (strcmp (ippGetString(attr, 0, NULL), "none"))
- g_string_append_printf (browse_options, "auth-info-required=%s ",
- ippGetString(attr, 0, NULL));
- } else if (!strcmp(attrname, "printer-uuid") &&
- value_tag == IPP_TAG_URI)
- g_string_append_printf (browse_options, "uuid=%s ",
- ippGetString(attr, 0, NULL));
- else if (!strcmp(attrname, "job-sheets-default") &&
- value_tag == IPP_TAG_NAME &&
- ippGetCount(attr) == 2)
- g_string_append_printf (browse_options, "job-sheets=%s,%s ",
- ippGetString(attr, 0, NULL),
- ippGetString(attr, 1, NULL));
- else if (strstr(attrname, "-default")) {
- gchar *name = g_strdup (attrname);
- gchar *value = NULL;
- *strstr (name, "-default") = '\0';
-
- switch (value_tag) {
- gchar **tokens;
-
- case IPP_TAG_KEYWORD:
- case IPP_TAG_STRING:
- case IPP_TAG_NAME:
- /* Escape value */
- tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
- " \"\'\\", -1);
- value = g_strjoinv ("\\", tokens);
- g_strfreev (tokens);
- break;
-
- default:
- /* other values aren't needed? */
- debug_printf("cups-browsed: skipping %s (%d)\n", name, value_tag);
- break;
- }
-
- if (value) {
- g_string_append_printf (browse_options, "%s=%s ", name, value);
- g_free (value);
- }
-
- g_free (name);
- }
-
- attr = ippNextAttribute(response);
- }
-
- if (type != -1 && state != -1 && uri && location && info && make_model) {
- gchar *browse_options_str = g_string_free (browse_options, FALSE);
- browse_options = NULL;
- g_strchomp (browse_options_str);
-
- broadcast_browse_packets (type, state, uri, location,
- info, make_model,
- browse_options_str);
-
- g_free (browse_options_str);
- }
-
- if (make_model)
- g_free (make_model);
-
- if (info)
- g_free (info);
+ httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
+ scheme, username, browse->address, port, resource);
- if (location)
- g_free (location);
+ if (snprintf (packet, sizeof (packet),
+ "%x " /* type */
+ "%x " /* state */
+ "%s " /* uri */
+ "\"%s\" " /* location */
+ "\"%s\" " /* info */
+ "\"%s\" " /* make-and-model */
+ "lease-duration=%d" /* BrowseTimeout */
+ "%s%s" /* other browse options */
+ "\n",
+ bdata->type,
+ bdata->state,
+ uri,
+ bdata->location,
+ bdata->info,
+ bdata->make_model,
+ BrowseTimeout,
+ bdata->browse_options ? " " : "",
+ bdata->browse_options ? bdata->browse_options : "")
+ >= sizeof (packet)) {
+ debug_printf ("cups-browsed: oversize packet not sent\n");
+ continue;
+ }
- if (browse_options)
- g_string_free (browse_options, TRUE);
+ debug_printf("cups-browsed: packet to send:\n%s", packet);
- if (!attr)
- break;
+ int err = sendto (browsesocket, packet,
+ strlen (packet), 0,
+ &browse->broadcast.addr,
+ httpAddrLength (&browse->broadcast));
+ if (err == -1)
+ debug_printf("cupsd-browsed: sendto returned %d: %s\n",
+ err, strerror (errno));
}
+}
- fail:
- if (response)
- ippDelete(response);
-
- if (conn)
- httpClose (conn);
-
+gboolean
+send_browse_data (gpointer data)
+{
+ update_netifs ();
+ res_init ();
+ update_local_printers ();
+ g_list_foreach (browse_data, broadcast_browse_packets, NULL);
g_timeout_add_seconds (BrowseInterval, send_browse_data, NULL);
/* Stop this timeout handler, we called a new one */
return FALSE;
}
-gboolean
-browse_poll (gpointer data)
+static browsepoll_printer_t *
+new_browsepoll_printer (const char *uri_supported,
+ const char *info)
+{
+ browsepoll_printer_t *printer = g_malloc (sizeof (browsepoll_printer_t));
+ printer->uri_supported = g_strdup (uri_supported);
+ printer->info = g_strdup (info);
+ return printer;
+}
+
+static void
+browsepoll_printer_free (gpointer data)
+{
+ browsepoll_printer_t *printer = data;
+ free (printer->uri_supported);
+ free (printer->info);
+ free (printer);
+}
+
+static void
+browse_poll_get_printers (browsepoll_t *context, http_t *conn)
{
- static const char * const rattrs[] = { "printer-uri-supported" };
- char *server = strdup (data);
+ static const char * const rattrs[] = { "printer-uri-supported",
+ "printer-info"};
ipp_t *request, *response = NULL;
ipp_attribute_t *attr;
- http_t *conn;
- int port = BrowsePort;
- char *colon;
-
- debug_printf ("cups-browsed: browse polling %s\n", server);
-
- colon = strchr (server, ':');
- if (colon) {
- char *endptr;
- unsigned long n;
- *colon++ = '\0';
- n = strtoul (colon, &endptr, 10);
- if (endptr != colon && n < INT_MAX)
- port = (int) n;
- }
-
- res_init ();
- conn = httpConnectEncrypt (server, port, HTTP_ENCRYPT_IF_REQUESTED);
+ GList *printers = NULL;
- if (conn == NULL) {
- debug_printf("cups-browsed: browse poll failed to connect to %s\n", server);
- goto fail;
- }
+ debug_printf ("cups-browsed [BrowsePoll %s:%d]: CUPS-Get-Printers\n",
+ context->server, context->port);
request = ippNewRequest(CUPS_GET_PRINTERS);
+ if (context->major > 0) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+ context->server, context->port, context->major,
+ context->minor);
+ ippSetVersion (request, context->major, context->minor);
+ }
ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
"requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
@@ -1621,13 +2434,14 @@ browse_poll (gpointer data)
response = cupsDoRequest(conn, request, "/");
if (cupsLastError() > IPP_OK_CONFLICT) {
- debug_printf("cups-browsed: browse poll failed for server %s: %s\n",
- server, cupsLastErrorString ());
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
+ context->server, context->port, cupsLastErrorString ());
goto fail;
}
for (attr = ippFirstAttribute(response); attr;
attr = ippNextAttribute(response)) {
+ browsepoll_printer_t *printer;
const char *uri, *info;
while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
@@ -1639,35 +2453,287 @@ browse_poll (gpointer data)
uri = NULL;
info = NULL;
while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
- if (!strcmp (ippGetName(attr), "printer-uri-supported") &&
+
+ if (!strcasecmp (ippGetName(attr), "printer-uri-supported") &&
ippGetValueTag(attr) == IPP_TAG_URI)
uri = ippGetString(attr, 0, NULL);
- else if (!strcmp (ippGetName(attr), "printer-info") &&
+ else if (!strcasecmp (ippGetName(attr), "printer-info") &&
ippGetValueTag(attr) == IPP_TAG_TEXT)
info = ippGetString(attr, 0, NULL);
attr = ippNextAttribute(response);
}
- if (uri)
- found_cups_printer (server, uri, info);
+ if (uri) {
+ found_cups_printer (context->server, uri, info);
+ printer = new_browsepoll_printer (uri, info);
+ printers = g_list_insert (printers, printer, 0);
+ }
if (!attr)
break;
}
+ g_list_free_full (context->printers, browsepoll_printer_free);
+ context->printers = printers;
recheck_timer ();
fail:
if (response)
ippDelete(response);
+}
+
+static void
+browse_poll_create_subscription (browsepoll_t *context, http_t *conn)
+{
+ static const char * const events[] = { "printer-added",
+ "printer-changed",
+ "printer-config-changed",
+ "printer-modified",
+ "printer-deleted",
+ "printer-state-changed" };
+ ipp_t *request, *response = NULL;
+ ipp_attribute_t *attr;
+
+ debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Create-Subscription\n",
+ context->server, context->port);
+
+ request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
+ if (context->major > 0) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+ context->server, context->port, context->major,
+ context->minor);
+ ippSetVersion (request, context->major, context->minor);
+ }
+
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, "/");
+ ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
+ "notify-pull-method", NULL, "ippget");
+ ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET,
+ "notify-charset", NULL, "utf-8");
+ ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+ ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
+ "notify-events", sizeof (events) / sizeof (events[0]),
+ NULL, events);
+ ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
+ "notify-time-interval", BrowseInterval);
+
+ response = cupsDoRequest (conn, request, "/");
+ if (!response || ippGetStatusCode (response) > IPP_OK_CONFLICT) {
+ debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
+ context->server, context->port, cupsLastErrorString ());
+ context->subscription_id = -1;
+ context->can_subscribe = FALSE;
+ goto fail;
+ }
+
+ for (attr = ippFirstAttribute(response); attr;
+ attr = ippNextAttribute(response)) {
+ if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) {
+ if (ippGetValueTag (attr) == IPP_TAG_INTEGER &&
+ !strcasecmp (ippGetName (attr), "notify-subscription-id")) {
+ context->subscription_id = ippGetInteger (attr, 0);
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: subscription ID=%d\n",
+ context->server, context->port, context->subscription_id);
+ break;
+ }
+ }
+ }
+
+ if (!attr) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: no ID returned\n",
+ context->server, context->port);
+ context->subscription_id = -1;
+ context->can_subscribe = FALSE;
+ }
+
+fail:
+ if (response)
+ ippDelete(response);
+}
+
+static void
+browse_poll_cancel_subscription (browsepoll_t *context)
+{
+ ipp_t *request, *response = NULL;
+ http_t *conn = httpConnectEncrypt (context->server, context->port,
+ HTTP_ENCRYPT_IF_REQUESTED);
+
+ if (conn == NULL) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: connection failure "
+ "attempting to cancel\n", context->server, context->port);
+ return;
+ }
+
+ debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Cancel-Subscription\n",
+ context->server, context->port);
+
+ request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
+ if (context->major > 0) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+ context->server, context->port, context->major,
+ context->minor);
+ ippSetVersion (request, context->major, context->minor);
+ }
+
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, "/");
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+ ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+ "notify-subscription-id", context->subscription_id);
+
+ response = cupsDoRequest (conn, request, "/");
+ if (!response || ippGetStatusCode (response) > IPP_OK_CONFLICT)
+ debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
+ context->server, context->port, cupsLastErrorString ());
+
+ if (response)
+ ippDelete(response);
+
+ httpClose (conn);
+}
+
+static gboolean
+browse_poll_get_notifications (browsepoll_t *context, http_t *conn)
+{
+ ipp_t *request, *response = NULL;
+ ipp_status_t status;
+ gboolean get_printers = FALSE;
+
+ debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Get-Notifications\n",
+ context->server, context->port);
+
+ request = ippNewRequest(IPP_GET_NOTIFICATIONS);
+ if (context->major > 0) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
+ context->server, context->port, context->major,
+ context->minor);
+ ippSetVersion (request, context->major, context->minor);
+ }
+
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, "/");
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "requesting-user-name", NULL, cupsUser ());
+ ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+ "notify-subscription-ids", context->subscription_id);
+ ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
+ "notify-sequence-numbers", context->sequence_number + 1);
+
+ response = cupsDoRequest (conn, request, "/");
+ if (!response)
+ status = cupsLastError ();
+ else
+ status = ippGetStatusCode (response);
+
+ if (status == IPP_NOT_FOUND) {
+ /* Subscription lease has expired. */
+ debug_printf ("cups-browsed [BrowsePoll %s:%d]: Lease expired\n",
+ context->server, context->port);
+ browse_poll_create_subscription (context, conn);
+ get_printers = TRUE;
+ } else if (status > IPP_OK_CONFLICT) {
+ debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
+ context->server, context->port, cupsLastErrorString ());
+ context->can_subscribe = FALSE;
+ browse_poll_cancel_subscription (context);
+ context->subscription_id = -1;
+ context->sequence_number = 0;
+ get_printers = TRUE;
+ }
+
+ if (!get_printers) {
+ ipp_attribute_t *attr;
+ gboolean seen_event = FALSE;
+ int last_seq = context->sequence_number;
+ assert (response != NULL);
+ for (attr = ippFirstAttribute(response); attr;
+ attr = ippNextAttribute(response))
+ if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION) {
+ /* There is a printer-* event here. */
+ seen_event = TRUE;
+
+ if (!strcmp (ippGetName (attr), "notify-sequence-number") &&
+ ippGetValueTag (attr) == IPP_TAG_INTEGER)
+ last_seq = ippGetInteger (attr, 0);
+ }
+
+ if (seen_event) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: printer-* event\n",
+ context->server, context->port);
+ context->sequence_number = last_seq;
+ get_printers = TRUE;
+ } else
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: no events\n",
+ context->server, context->port);
+ }
+
+ if (response)
+ ippDelete (response);
+
+ return get_printers;
+}
+
+static void
+browsepoll_printer_keepalive (gpointer data, gpointer user_data)
+{
+ browsepoll_printer_t *printer = data;
+ const char *server = user_data;
+ found_cups_printer (server, printer->uri_supported, printer->info);
+}
+
+gboolean
+browse_poll (gpointer data)
+{
+ browsepoll_t *context = data;
+ http_t *conn = NULL;
+ gboolean get_printers = FALSE;
+
+ debug_printf ("cups-browsed: browse polling %s:%d\n",
+ context->server, context->port);
+
+ res_init ();
+
+ conn = httpConnectEncrypt (context->server, context->port,
+ HTTP_ENCRYPT_IF_REQUESTED);
+ if (conn == NULL) {
+ debug_printf("cups-browsed [BrowsePoll %s:%d]: failed to connect\n",
+ context->server, context->port);
+ goto fail;
+ }
+
+ if (context->can_subscribe) {
+ if (context->subscription_id == -1) {
+ /* The first time this callback is run we need to create the IPP
+ * subscription to watch to printer-* events. */
+ browse_poll_create_subscription (context, conn);
+ get_printers = TRUE;
+ } else
+ /* On subsequent runs, check for notifications using our
+ * subscription. */
+ get_printers = browse_poll_get_notifications (context, conn);
+ }
+ else
+ get_printers = TRUE;
+
+ update_local_printers ();
+ inhibit_local_printers_update = TRUE;
+ if (get_printers)
+ browse_poll_get_printers (context, conn);
+ else
+ g_list_foreach (context->printers, browsepoll_printer_keepalive,
+ context->server);
+
+ inhibit_local_printers_update = FALSE;
+
+fail:
if (conn)
httpClose (conn);
- if (server)
- free (server);
-
/* Call a new timeout handler so that we run again */
g_timeout_add_seconds (BrowseInterval, browse_poll, data);
@@ -1686,7 +2752,7 @@ compare_pointers (void *a, void *b, void
}
int compare_remote_printers (remote_printer_t *a, remote_printer_t *b) {
- return strcmp(a->name, b->name);
+ return strcasecmp(a->name, b->name);
}
static void
@@ -1698,12 +2764,53 @@ sigterm_handler(int sig) {
debug_printf("cups-browsed: Caught signal %d, shutting down ...\n", sig);
}
+static void
+sigusr1_handler(int sig) {
+ (void)sig; /* remove compiler warnings... */
+
+ /* Turn off auto shutdown mode... */
+ autoshutdown = 0;
+ debug_printf("cups-browsed: Caught signal %d, switching to permanent mode ...\n", sig);
+ /* If there is still an active auto shutdown timer, kill it */
+ if (autoshutdown_exec_id > 0) {
+ debug_printf ("cups-browsed: We have left auto shutdown mode, killing auto shutdown timer.\n");
+ g_source_destroy(g_main_context_find_source_by_id(NULL,
+ autoshutdown_exec_id));
+ autoshutdown_exec_id = 0;
+ }
+}
+
+static void
+sigusr2_handler(int sig) {
+ (void)sig; /* remove compiler warnings... */
+
+ /* Turn on auto shutdown mode... */
+ autoshutdown = 1;
+ debug_printf("cups-browsed: Caught signal %d, switching to auto shutdown mode ...\n", sig);
+ /* If there are no printers schedule the shutdown in autoshutdown_timeout
+ seconds */
+ if (!autoshutdown_exec_id &&
+ cupsArrayCount(remote_printers) == 0) {
+ debug_printf ("cups-browsed: We entered auto shutdown mode and no printers are there to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+ autoshutdown_exec_id =
+ g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+ NULL);
+ }
+}
+
static int
read_browseallow_value (const char *value)
{
char *p;
struct in_addr addr;
- allow_t *allow = calloc (1, sizeof (allow_t));
+ allow_t *allow;
+
+ if (value && !strcasecmp (value, "all")) {
+ browseallow_all = TRUE;
+ return 0;
+ }
+
+ allow = calloc (1, sizeof (allow_t));
if (value == NULL)
goto fail;
p = strchr (value, '/');
@@ -1785,7 +2892,7 @@ read_configuration (const char *filename
else if (!strcasecmp(p, "cups"))
protocols |= BROWSE_CUPS;
else if (strcasecmp(p, "none"))
- debug_printf("cups-browsed: unknown protocol '%s'\n", p);
+ debug_printf("cups-browsed: Unknown protocol '%s'\n", p);
p = strtok_r (NULL, delim, &saveptr);
}
@@ -1797,44 +2904,153 @@ read_configuration (const char *filename
else
BrowseLocalProtocols = BrowseRemoteProtocols = protocols;
} else if (!strcasecmp(line, "BrowsePoll") && value) {
- char **old = BrowsePoll;
- BrowsePoll = realloc (BrowsePoll, (NumBrowsePoll + 1) * sizeof (char *));
+ browsepoll_t **old = BrowsePoll;
+ BrowsePoll = realloc (BrowsePoll,
+ (NumBrowsePoll + 1) *
+ sizeof (browsepoll_t *));
if (!BrowsePoll) {
debug_printf("cups-browsed: unable to realloc: ignoring BrowsePoll line\n");
BrowsePoll = old;
} else {
+ char *colon, *slash;
+ browsepoll_t *b = g_malloc0 (sizeof (browsepoll_t));
debug_printf("cups-browsed: Adding BrowsePoll server: %s\n", value);
- BrowsePoll[NumBrowsePoll++] = strdup (value);
+ b->server = strdup (value);
+ b->port = BrowsePort;
+ b->can_subscribe = TRUE; /* first assume subscriptions work */
+ b->subscription_id = -1;
+ slash = strchr (b->server, '/');
+ if (slash) {
+ *slash++ = '\0';
+ if (!strcasecmp (slash, "version=1.0")) {
+ b->major = 1;
+ b->minor = 0;
+ } else if (!strcasecmp (slash, "version=1.1")) {
+ b->major = 1;
+ b->minor = 1;
+ } else if (!strcasecmp (slash, "version=2.0")) {
+ b->major = 2;
+ b->minor = 0;
+ } else if (!strcasecmp (slash, "version=2.1")) {
+ b->major = 2;
+ b->minor = 1;
+ } else if (!strcasecmp (slash, "version=2.2")) {
+ b->major = 2;
+ b->minor = 2;
+ } else {
+ debug_printf ("ignoring unknown server option: %s\n", slash);
+ }
+ } else
+ b->major = 0;
+
+ colon = strchr (b->server, ':');
+ if (colon) {
+ char *endptr;
+ unsigned long n;
+ *colon++ = '\0';
+ n = strtoul (colon, &endptr, 10);
+ if (endptr != colon && n < INT_MAX)
+ b->port = (int) n;
+ }
+
+ BrowsePoll[NumBrowsePoll++] = b;
}
- } else if (!strcasecmp(line, "BrowseAllow"))
+ } else if (!strcasecmp(line, "BrowseAllow")) {
if (read_browseallow_value (value))
debug_printf ("cups-browsed: BrowseAllow value \"%s\" not understood\n",
value);
+ } else if (!strcasecmp(line, "DomainSocket") && value) {
+ if (value[0] != '\0')
+ DomainSocket = strdup(value);
+ } else if (!strcasecmp(line, "CreateIPPPrinterQueues") && value) {
+ if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
+ !strcasecmp(value, "on") || !strcasecmp(value, "1"))
+ CreateIPPPrinterQueues = 1;
+ else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
+ !strcasecmp(value, "off") || !strcasecmp(value, "0"))
+ CreateIPPPrinterQueues = 0;
+ } else if (!strcasecmp(line, "AutoShutdown") && value) {
+ char *p, *saveptr;
+ p = strtok_r (value, delim, &saveptr);
+ while (p) {
+ if (!strcasecmp(p, "On") || !strcasecmp(p, "Yes") ||
+ !strcasecmp(p, "True") || !strcasecmp(p, "1")) {
+ autoshutdown = 1;
+ debug_printf("cups-browsed: Turning on auto shutdown mode.\n");
+ } else if (!strcasecmp(p, "Off") || !strcasecmp(p, "No") ||
+ !strcasecmp(p, "False") || !strcasecmp(p, "0")) {
+ autoshutdown = 0;
+ debug_printf("cups-browsed: Turning off auto shutdown mode (permanent mode).\n");
+ } else if (!strcasecmp(p, "avahi")) {
+ autoshutdown_avahi = 1;
+ debug_printf("cups-browsed: Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
+ } else if (strcasecmp(p, "none"))
+ debug_printf("cups-browsed: Unknown mode '%s'\n", p);
+ p = strtok_r (NULL, delim, &saveptr);
+ }
+ } else if (!strcasecmp(line, "AutoShutdownTimeout") && value) {
+ int t = atoi(value);
+ if (t >= 0) {
+ autoshutdown_timeout = t;
+ debug_printf("cups-browsed: Set auto shutdown timeout to %d sec.\n",
+ t);
+ } else
+ debug_printf("cups-browsed: Invalid auto shutdown timeout value: %d\n",
+ t);
+ }
}
cupsFileClose(fp);
}
+static void
+find_previous_queue (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ const char *name = key;
+ const local_printer_t *printer = value;
+ remote_printer_t *p;
+ if (printer->cups_browsed_controlled) {
+ /* Queue found, add to our list */
+ p = create_local_queue (name,
+ printer->device_uri,
+ "", "", "", "", NULL, NULL, 1);
+ if (p) {
+ /* Mark as unconfirmed, if no Avahi report of this queue appears
+ in a certain time frame, we will remove the queue */
+ p->status = STATUS_UNCONFIRMED;
+
+ if (BrowseRemoteProtocols & BROWSE_CUPS)
+ p->timeout = time(NULL) + BrowseTimeout;
+ else
+ p->timeout = time(NULL) + TIMEOUT_CONFIRM;
+
+ p->duplicate = 0;
+ debug_printf("cups-browsed: Found CUPS queue %s (URI: %s) from previous session.\n",
+ p->name, p->uri);
+ } else {
+ debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
+ exit(1);
+ }
+ }
+}
+
int main(int argc, char*argv[]) {
-#ifdef HAVE_AVAHI
- AvahiClient *client = NULL;
- AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL;
- int error;
-#endif /* HAVE_AVAHI */
int ret = 1;
http_t *http;
- cups_dest_t *dests,
- *dest;
- int i,
- num_dests;
+ int i;
const char *val;
remote_printer_t *p;
/* Turn on debug mode if requested */
- if (argc >= 2 &&
- (!strcmp(argv[1], "--debug") || !strcmp(argv[1], "-d") ||
- !strncmp(argv[1], "-v", 2)))
- debug = 1;
+ if (argc >= 2)
+ for (i = 1; i < argc; i++)
+ if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") ||
+ !strncasecmp(argv[i], "-v", 2)) {
+ debug = 1;
+ debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+ }
/* Initialise the browseallow array */
browseallow = cupsArrayNew(compare_pointers, NULL);
@@ -1842,6 +3058,79 @@ int main(int argc, char*argv[]) {
/* Read in cups-browsed.conf */
read_configuration (NULL);
+ /* Parse command line options after reading the config file to override
+ config file settings */
+ if (argc >= 2) {
+ for (i = 1; i < argc; i++)
+ if (!strncasecmp(argv[i], "--autoshutdown-timeout", 22)) {
+ debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+ if (argv[i][22] == '=' && argv[i][23])
+ val = argv[i] + 23;
+ else if (!argv[i][22] && i < argc -1) {
+ i++;
+ debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+ val = argv[i];
+ } else {
+ fprintf(stderr, "cups-browsed: Expected auto shutdown timeout setting after \"--autoshutdown-timeout\" option.\n");
+ exit(1);
+ }
+ int t = atoi(val);
+ if (t >= 0) {
+ autoshutdown_timeout = t;
+ debug_printf("cups-browsed: Set auto shutdown timeout to %d sec.\n",
+ t);
+ } else {
+ debug_printf("cups-browsed: Invalid auto shutdown timeout value: %d\n",
+ t);
+ exit(1);
+ }
+ } else if (!strncasecmp(argv[i], "--autoshutdown", 14)) {
+ debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+ if (argv[i][14] == '=' && argv[i][15])
+ val = argv[i] + 15;
+ else if (!argv[i][14] && i < argc -1) {
+ i++;
+ debug_printf("cups-browsed: Reading command line: %s\n", argv[i]);
+ val = argv[i];
+ } else {
+ fprintf(stderr, "cups-browsed: Expected auto shutdown setting after \"--autoshutdown\" option.\n");
+ exit(1);
+ }
+ if (!strcasecmp(val, "On") || !strcasecmp(val, "Yes") ||
+ !strcasecmp(val, "True") || !strcasecmp(val, "1")) {
+ autoshutdown = 1;
+ debug_printf("cups-browsed: Turning on auto shutdown mode.\n");
+ } else if (!strcasecmp(val, "Off") || !strcasecmp(val, "No") ||
+ !strcasecmp(val, "False") || !strcasecmp(val, "0")) {
+ autoshutdown = 0;
+ debug_printf("cups-browsed: Turning off auto shutdown mode (permanent mode).\n");
+ } else if (!strcasecmp(val, "avahi")) {
+ autoshutdown_avahi = 1;
+ debug_printf("cups-browsed: Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
+ } else if (strcasecmp(val, "none")) {
+ debug_printf("cups-browsed: Unknown mode '%s'\n", val);
+ exit(1);
+ }
+ }
+ }
+
+ /* Set the CUPS_SERVER environment variable to assure that cups-browsed
+ always works with the local CUPS daemon and never with a remote one
+ specified by a client.conf file */
+#ifdef CUPS_DEFAULT_DOMAINSOCKET
+ if (DomainSocket == NULL)
+ DomainSocket = CUPS_DEFAULT_DOMAINSOCKET;
+#endif
+ if (DomainSocket != NULL) {
+ struct stat sockinfo; /* Domain socket information */
+ if (!stat(DomainSocket, &sockinfo) &&
+ (sockinfo.st_mode & S_IRWXO) == S_IRWXO)
+ setenv("CUPS_SERVER", DomainSocket, 1);
+ else
+ setenv("CUPS_SERVER", "localhost", 1);
+ } else
+ setenv("CUPS_SERVER", "localhost", 1);
+
if (BrowseLocalProtocols & BROWSE_DNSSD) {
fprintf(stderr, "Local support for DNSSD not implemented\n");
BrowseLocalProtocols &= ~BROWSE_DNSSD;
@@ -1855,60 +3144,34 @@ int main(int argc, char*argv[]) {
#endif /* HAVE_AVAHI */
/* Wait for CUPS daemon to start */
- while ((http = httpConnectEncrypt(cupsServer(), ippPort(),
- cupsEncryption())) == NULL)
+ while ((http = http_connect_local ()) == NULL)
sleep(1);
/* Initialise the array of network interfaces */
netifs = cupsArrayNew(compare_pointers, NULL);
update_netifs ();
+ local_printers = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ free_local_printer);
+
/* Read out the currently defined CUPS queues and find the ones which we
have added in an earlier session */
- num_dests = cupsGetDests(&dests);
+ update_local_printers ();
remote_printers = cupsArrayNew((cups_array_func_t)compare_remote_printers,
NULL);
- if (num_dests > 0) {
- for (i = num_dests, dest = dests; i > 0; i --, dest ++) {
- if ((val = cupsGetOption(CUPS_BROWSED_MARK, dest->num_options,
- dest->options)) != NULL) {
- if (strcasecmp(val, "no") != 0 && strcasecmp(val, "off") != 0 &&
- strcasecmp(val, "false") != 0) {
- /* Queue found, add to our list */
- p = create_local_queue (dest->name,
- strdup(cupsGetOption("device-uri",
- dest->num_options,
- dest->options)),
- "", "", "", "");
- if (p) {
- /* Mark as unconfirmed, if no Avahi report of this queue appears
- in a certain time frame, we will remove the queue */
- p->status = STATUS_UNCONFIRMED;
-
- if (BrowseRemoteProtocols & BROWSE_CUPS)
- p->timeout = time(NULL) + BrowseTimeout;
- else
- p->timeout = time(NULL) + TIMEOUT_CONFIRM;
-
- p->duplicate = 0;
- debug_printf("cups-browsed: Found CUPS queue %s (URI: %s) from previous session.\n",
- p->name, p->uri);
- } else {
- debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
- exit(1);
- }
- }
- }
- }
- cupsFreeDests(num_dests, dests);
- }
- httpClose(http);
+ g_hash_table_foreach (local_printers, find_previous_queue, NULL);
/* Redirect SIGINT and SIGTERM so that we do a proper shutdown, removing
- the CUPS queues which we have created */
+ the CUPS queues which we have created
+ Use SIGUSR1 and SIGUSR2 to turn off and turn on auto shutdown mode
+ resp. */
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
sigset(SIGTERM, sigterm_handler);
sigset(SIGINT, sigterm_handler);
+ sigset(SIGUSR1, sigusr1_handler);
+ sigset(SIGUSR2, sigusr2_handler);
debug_printf("cups-browsed: Using signal handler SIGSET\n");
#elif defined(HAVE_SIGACTION)
struct sigaction action; /* Actions for POSIX signals */
@@ -1921,65 +3184,27 @@ int main(int argc, char*argv[]) {
sigaddset(&action.sa_mask, SIGINT);
action.sa_handler = sigterm_handler;
sigaction(SIGINT, &action, NULL);
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGUSR1);
+ action.sa_handler = sigusr1_handler;
+ sigaction(SIGUSR1, &action, NULL);
+ sigemptyset(&action.sa_mask);
+ sigaddset(&action.sa_mask, SIGUSR2);
+ action.sa_handler = sigusr2_handler;
+ sigaction(SIGUSR2, &action, NULL);
debug_printf("cups-browsed: Using signal handler SIGACTION\n");
#else
signal(SIGTERM, sigterm_handler);
signal(SIGINT, sigterm_handler);
+ signal(SIGUSR1, sigusr1_handler);
+ signal(SIGUSR2, sigusr2_handler);
debug_printf("cups-browsed: Using signal handler SIGNAL\n");
#endif /* HAVE_SIGSET */
#ifdef HAVE_AVAHI
- if (BrowseRemoteProtocols & BROWSE_DNSSD) {
- /* Allocate main loop object */
- if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
- debug_printf("cups-browsed: ERROR: Failed to create glib poll object.\n");
- goto avahi_fail;
- }
-
- /* Allocate a new client */
- client = avahi_client_new(avahi_glib_poll_get(glib_poll), 0,
- client_callback, NULL, &error);
-
- /* Check wether creating the client object succeeded */
- if (!client) {
- debug_printf("cups-browsed: ERROR: Failed to create client: %s\n",
- avahi_strerror(error));
- goto avahi_fail;
- }
-
- /* Create the service browsers */
- if (!(sb1 =
- avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
- "_ipp._tcp", NULL, 0, browse_callback,
- client))) {
- debug_printf("cups-browsed: ERROR: Failed to create service browser for IPP: %s\n",
- avahi_strerror(avahi_client_errno(client)));
- goto avahi_fail;
- }
- if (!(sb2 =
- avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
- "_ipps._tcp", NULL, 0, browse_callback,
- client))) {
- debug_printf("cups-browsed: ERROR: Failed to create service browser for IPPS: %s\n",
- avahi_strerror(avahi_client_errno(client)));
- avahi_fail:
- BrowseRemoteProtocols &= ~BROWSE_DNSSD;
- if (sb2) {
- avahi_service_browser_free(sb2);
- sb2 = NULL;
- }
-
- if (client) {
- avahi_client_free(client);
- client = NULL;
- }
-
- if (glib_poll) {
- avahi_glib_poll_free(glib_poll);
- glib_poll = NULL;
- }
- }
- }
+ if (autoshutdown_avahi)
+ autoshutdown = 1;
+ avahi_init();
#endif /* HAVE_AVAHI */
if (BrowseLocalProtocols & BROWSE_CUPS ||
@@ -2025,6 +3250,10 @@ int main(int argc, char*argv[]) {
goto fail;
}
+ /* Override the default password callback so we don't end up
+ * prompting for it. */
+ cupsSetPasswordCB2 (password_callback, NULL);
+
/* Run the main loop */
gmainloop = g_main_loop_new (NULL, FALSE);
recheck_timer ();
@@ -2047,11 +3276,20 @@ int main(int argc, char*argv[]) {
index < NumBrowsePoll;
index++) {
debug_printf ("cups-browsed: will browse poll %s every %ds\n",
- BrowsePoll[index], BrowseInterval);
+ BrowsePoll[index]->server, BrowseInterval);
g_idle_add (browse_poll, BrowsePoll[index]);
}
}
+ /* If auto shutdown is active and we do not find any printers initially,
+ schedule the shutdown in autoshutdown_timeout seconds */
+ if (autoshutdown && !autoshutdown_exec_id &&
+ cupsArrayCount(remote_printers) == 0) {
+ debug_printf ("cups-browsed: No printers found to make available, shutting down in %d sec...\n", autoshutdown_timeout);
+ autoshutdown_exec_id =
+ g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, NULL);
+ }
+
g_main_loop_run (gmainloop);
debug_printf("cups-browsed: main loop exited\n");
@@ -2071,22 +3309,44 @@ fail:
}
handle_cups_queues(NULL);
-#ifdef HAVE_AVAHI
- /* Free the data structures for Bonjour browsing */
- if (sb1)
- avahi_service_browser_free(sb1);
- if (sb2)
- avahi_service_browser_free(sb2);
+ if (BrowsePoll) {
+ size_t index;
+ for (index = 0;
+ index < NumBrowsePoll;
+ index++) {
+ if (BrowsePoll[index]->can_subscribe &&
+ BrowsePoll[index]->subscription_id != -1)
+ browse_poll_cancel_subscription (BrowsePoll[index]);
- if (client)
- avahi_client_free(client);
+ free (BrowsePoll[index]->server);
+ g_list_free_full (BrowsePoll[index]->printers,
+ browsepoll_printer_free);
+ free (BrowsePoll[index]);
+ }
- if (glib_poll)
- avahi_glib_poll_free(glib_poll);
+ free (BrowsePoll);
+ }
+
+ if (local_printers_context) {
+ browse_poll_cancel_subscription (local_printers_context);
+ g_list_free_full (local_printers_context->printers,
+ browsepoll_printer_free);
+ free (local_printers_context);
+ }
+
+ http_close_local ();
+
+#ifdef HAVE_AVAHI
+ avahi_shutdown();
#endif /* HAVE_AVAHI */
if (browsesocket != -1)
close (browsesocket);
+ g_hash_table_destroy (local_printers);
+
+ if (BrowseLocalProtocols & BROWSE_CUPS)
+ g_list_free_full (browse_data, browse_data_free);
+
return ret;
}