From ba7055cbdda03ba64c17047bb1ecb91c8c466b36 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 19 2015 15:42:35 +0000 Subject: import cups-filters-1.0.35-21.el7 --- diff --git a/SOURCES/cups-filters-CVE-2015-3258-3279.patch b/SOURCES/cups-filters-CVE-2015-3258-3279.patch new file mode 100644 index 0000000..6fcd83a --- /dev/null +++ b/SOURCES/cups-filters-CVE-2015-3258-3279.patch @@ -0,0 +1,98 @@ +diff -up cups-filters-1.0.35/filter/textcommon.c.CVE-2015-3258-3279 cups-filters-1.0.35/filter/textcommon.c +--- cups-filters-1.0.35/filter/textcommon.c.CVE-2015-3258-3279 2013-05-07 19:24:58.000000000 +0100 ++++ cups-filters-1.0.35/filter/textcommon.c 2015-07-09 08:16:32.506423028 +0100 +@@ -26,6 +26,7 @@ + */ + + #include "textcommon.h" ++#include + + + /* +@@ -644,6 +645,45 @@ TextMain(const char *name, /* I - Name o + if (PrettyPrint) + PageTop -= 216.0f / LinesPerInch; + ++ /* ++ * Allocate memory for the page... ++ */ ++ ++ SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch; ++ SizeLines = (PageTop - PageBottom) / 72.0 * LinesPerInch; ++ ++ /* ++ * Enforce minimum size... ++ */ ++ if (SizeColumns < 1) ++ SizeColumns = 1; ++ if (SizeLines < 1) ++ SizeLines = 1; ++ ++ if (SizeLines >= INT_MAX / SizeColumns / sizeof(lchar_t)) ++ { ++ fprintf(stderr, "ERROR: bad page size\n"); ++ exit(1); ++ } ++ ++ Page = calloc(sizeof(lchar_t *), SizeLines); ++ if (!Page) ++ { ++ fprintf(stderr, "ERROR: cannot allocate memory for page\n"); ++ exit(1); ++ } ++ ++ Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines); ++ if (!Page[0]) ++ { ++ free(Page); ++ fprintf(stderr, "ERROR: cannot allocate memory for page\n"); ++ exit(1); ++ } ++ ++ for (i = 1; i < SizeLines; i ++) ++ Page[i] = Page[0] + i * SizeColumns; ++ + Copies = atoi(argv[4]); + + WriteProlog(argv[3], argv[2], getenv("CLASSIFICATION"), +@@ -1122,6 +1162,8 @@ TextMain(const char *name, /* I - Name o + if (ppd != NULL) + ppdClose(ppd); + ++ free(Page[0]); ++ free(Page); + return (0); + } + +diff -up cups-filters-1.0.35/filter/texttopdf.c.CVE-2015-3258-3279 cups-filters-1.0.35/filter/texttopdf.c +--- cups-filters-1.0.35/filter/texttopdf.c.CVE-2015-3258-3279 2015-07-09 08:16:12.266663237 +0100 ++++ cups-filters-1.0.35/filter/texttopdf.c 2015-07-09 08:16:32.506423028 +0100 +@@ -172,9 +172,6 @@ WriteEpilogue(void) + { "FN","FB","FI" }; + int i,j; + +- free(Page[0]); +- free(Page); +- + // embed fonts + for (i = PrettyPrint ? 2 : 1; i >= 0; i --) { + for (j = 0; j < NumFonts; j ++) +@@ -333,18 +330,6 @@ WriteProlog(const char *title, /* I - T + PageTop -= 36; + } + +- /* +- * Allocate memory for the page... +- */ +- +- SizeColumns = (PageRight - PageLeft) / 72.0 * CharsPerInch; +- SizeLines = (PageTop - PageBottom) / 72.0 * LinesPerInch; +- +- Page = calloc(sizeof(lchar_t *), SizeLines); +- Page[0] = calloc(sizeof(lchar_t), SizeColumns * SizeLines); +- for (i = 1; i < SizeLines; i ++) +- Page[i] = Page[0] + i * SizeColumns; +- + if (PageColumns > 1) + { + ColumnGutter = CharsPerInch / 2; diff --git a/SOURCES/cups-filters-browsed-efficiency.patch b/SOURCES/cups-filters-browsed-efficiency.patch new file mode 100644 index 0000000..9b532c8 --- /dev/null +++ b/SOURCES/cups-filters-browsed-efficiency.patch @@ -0,0 +1,2906 @@ +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 + #include +-#include + #if defined(__OpenBSD__) + #include + #endif /* __OpenBSD__ */ ++#include + #include + #include ++#include + #include + #include +-#include ++#include + #include + #include + #include +@@ -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 @? */ + 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 @, 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)://:631/printers/ */ +- 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; + } diff --git a/SOURCES/cups-filters-browsed-underscore.patch b/SOURCES/cups-filters-browsed-underscore.patch new file mode 100644 index 0000000..4c96565 --- /dev/null +++ b/SOURCES/cups-filters-browsed-underscore.patch @@ -0,0 +1,34 @@ +diff -up cups-filters-1.0.35/NEWS.browsed-underscore cups-filters-1.0.35/NEWS +diff -up cups-filters-1.0.35/utils/cups-browsed.c.browsed-underscore cups-filters-1.0.35/utils/cups-browsed.c +--- cups-filters-1.0.35/utils/cups-browsed.c.browsed-underscore 2015-06-22 15:28:06.219524890 +0100 ++++ cups-filters-1.0.35/utils/cups-browsed.c 2015-06-22 15:36:17.811452072 +0100 +@@ -303,11 +303,11 @@ create_local_queue (const char *name, + * Remove all illegal characters and replace each group of such characters + * by a single dash, return a free()-able string. + * +- * mode = 0: Only allow letters, numbers, and dashes, for turning make/model +- * info into a valid print queue name or into a string which can +- * be supplied as option value in a filter command line without +- * need of quoting +- * mode = 1: Allow also '/', '.', ',', '_', for cleaning up MIME type ++ * mode = 0: Only allow letters, numbers, dashes, and underscores for ++ * turning make/model info into a valid print queue name or ++ * into a string which can be supplied as option value in a ++ * filter command line without need of quoting ++ * mode = 1: Allow also '/', '.', ',' for cleaning up MIME type + * strings (here available Page Description Languages, PDLs) to + * supply them on a filter command line without quoting + * +@@ -339,9 +339,10 @@ remove_bad_chars(const char *str_orig, / + if (((str[i] >= 'A') && (str[i] <= 'Z')) || + ((str[i] >= 'a') && (str[i] <= 'z')) || + ((str[i] >= '0') && (str[i] <= '9')) || +- (mode == 1 && (str[i] == '/' || str[i] == '_' || ++ str[i] == '_' || ++ (mode == 1 && (str[i] == '/' || + str[i] == '.' || str[i] == ','))) { +- /* Letter or number, keep it */ ++ /* Allowed character, keep it */ + havedash = 0; + } else { + /* Replace all other characters by a single '-' */ diff --git a/SOURCES/cups-filters-poppler023.patch b/SOURCES/cups-filters-poppler023.patch new file mode 100644 index 0000000..416bad8 --- /dev/null +++ b/SOURCES/cups-filters-poppler023.patch @@ -0,0 +1,225 @@ +diff -up cups-filters-1.0.35/filter/pdf.cxx.poppler023 cups-filters-1.0.35/filter/pdf.cxx +--- cups-filters-1.0.35/filter/pdf.cxx.poppler023 2015-06-16 15:47:21.870132195 +0100 ++++ cups-filters-1.0.35/filter/pdf.cxx 2015-06-16 15:47:59.623128579 +0100 +@@ -17,6 +17,10 @@ + #include "pdf.h" + + #include ++#include ++#ifdef HAVE_CPP_POPPLER_VERSION_H ++#include "cpp/poppler-version.h" ++#endif + + + extern "C" pdf_t * pdf_load_template(const char *filename) +@@ -343,7 +347,11 @@ public: + { + } + ++#if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 23 ++ Goffset getPos() ++#else + int getPos() ++#endif + { + return this->pos; + } +diff -up cups-filters-1.0.35/filter/pdftoijs.cxx.poppler023 cups-filters-1.0.35/filter/pdftoijs.cxx +--- cups-filters-1.0.35/filter/pdftoijs.cxx.poppler023 2015-06-16 15:47:21.871132195 +0100 ++++ cups-filters-1.0.35/filter/pdftoijs.cxx 2015-06-16 15:47:59.623128579 +0100 +@@ -70,8 +70,13 @@ namespace { + } + + #if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 19 ++#if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 23 ++void CDECL myErrorFun(void *data, ErrorCategory category, ++ Goffset pos, char *msg) ++#else + void CDECL myErrorFun(void *data, ErrorCategory category, + int pos, char *msg) ++#endif + { + if (pos >= 0) { + fprintf(stderr, "ERROR (%d): ", pos); +@@ -297,19 +302,15 @@ int main(int argc, char *argv[]) { + if (argc == 6) { + /* stdin */ + int fd; +- Object obj; +- BaseStream *str; +- FILE *fp; ++ char name[BUFSIZ]; + char buf[BUFSIZ]; + int n; + +- fd = cupsTempFd(buf,sizeof(buf)); ++ fd = cupsTempFd(name,sizeof(name)); + if (fd < 0) { + pdfError(-1,"Can't create temporary file"); + exit(1); + } +- /* remove name */ +- unlink(buf); + + /* copy stdin to the tmp file */ + while ((n = read(0,buf,BUFSIZ)) > 0) { +@@ -319,23 +320,10 @@ int main(int argc, char *argv[]) { + exit(1); + } + } +- if (lseek(fd,0,SEEK_SET) < 0) { +- pdfError(-1,"Can't rewind temporary file"); +- close(fd); +- exit(1); +- } +- +- if ((fp = fdopen(fd,"rb")) == 0) { +- pdfError(-1,"Can't fdopen temporary file"); +- close(fd); +- exit(1); +- } +- +- obj.initNull(); +-// parsePDFTOPDFComment(fp); // TODO? +- rewind(fp); +- str = new FileStream(fp,0,gFalse,0,&obj); +- doc = new PDFDoc(str); ++ close(fd); ++ doc = new PDFDoc(new GooString(name)); ++ /* remove name */ ++ unlink(name); + } else { + GooString *fileName = new GooString(argv[6]); + /* argc == 7 filenmae is specified */ +diff -up cups-filters-1.0.35/filter/pdftoopvp/pdftoopvp.cxx.poppler023 cups-filters-1.0.35/filter/pdftoopvp/pdftoopvp.cxx +--- cups-filters-1.0.35/filter/pdftoopvp/pdftoopvp.cxx.poppler023 2015-06-16 15:47:21.871132195 +0100 ++++ cups-filters-1.0.35/filter/pdftoopvp/pdftoopvp.cxx 2015-06-16 15:47:59.624128579 +0100 +@@ -112,8 +112,13 @@ static int outOnePage(PDFDoc *doc, OPVPO + #define MAX_OPVP_OPTIONS 20 + + #if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 19 ++#if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 23 ++void CDECL myErrorFun(void *data, ErrorCategory category, ++ Goffset pos, char *msg) ++#else + void CDECL myErrorFun(void *data, ErrorCategory category, + int pos, char *msg) ++#endif + { + if (pos >= 0) { + fprintf(stderr, "ERROR (%d): ", pos); +@@ -619,9 +624,6 @@ exit(0); + char *s; + GooString name; + int fd; +- Object obj; +- BaseStream *str; +- FILE *fp; + char buf[4096]; + int n; + +@@ -633,8 +635,6 @@ exit(0); + } + name.append("/XXXXXX"); + fd = mkstemp(name.getCString()); +- /* remove name */ +- unlink(name.getCString()); + if (fd < 0) { + opvpError(-1,"Can't create temporary file"); + exitCode = 2; +@@ -675,23 +675,10 @@ exit(0); + goto err0; + } + } +- if (lseek(fd,0,SEEK_SET) < 0) { +- opvpError(-1,"Can't rewind temporary file"); +- close(fd); +- exitCode = 2; +- goto err0; +- } +- +- if ((fp = fdopen(fd,"rb")) == 0) { +- opvpError(-1,"Can't fdopen temporary file"); +- close(fd); +- exitCode = 2; +- goto err0; +- } +- +- obj.initNull(); +- str = new FileStream(fp,0,gFalse,0,&obj); +- doc = new PDFDoc(str); ++ close(fd); ++ doc = new PDFDoc(&name); ++ /* remove name */ ++ unlink(name.getCString()); + } else { + /* no jcl check */ + doc = new PDFDoc(fileName.copy()); +diff -up cups-filters-1.0.35/filter/pdftoraster.cxx.poppler023 cups-filters-1.0.35/filter/pdftoraster.cxx +--- cups-filters-1.0.35/filter/pdftoraster.cxx.poppler023 2015-06-16 15:47:21.871132195 +0100 ++++ cups-filters-1.0.35/filter/pdftoraster.cxx 2015-06-16 15:47:59.624128579 +0100 +@@ -185,8 +185,13 @@ namespace { + } + + #if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 19 ++#if POPPLER_VERSION_MAJOR > 0 || POPPLER_VERSION_MINOR >= 23 ++void CDECL myErrorFun(void *data, ErrorCategory category, ++ Goffset pos, char *msg) ++#else + void CDECL myErrorFun(void *data, ErrorCategory category, + int pos, char *msg) ++#endif + { + if (pos >= 0) { + fprintf(stderr, "ERROR (%d): ", pos); +@@ -1762,19 +1767,15 @@ int main(int argc, char *argv[]) { + if (argc == 6) { + /* stdin */ + int fd; +- Object obj; +- BaseStream *str; +- FILE *fp; ++ char name[BUFSIZ]; + char buf[BUFSIZ]; + int n; + +- fd = cupsTempFd(buf,sizeof(buf)); ++ fd = cupsTempFd(name,sizeof(name)); + if (fd < 0) { + pdfError(-1,const_cast("Can't create temporary file")); + exit(1); + } +- /* remove name */ +- unlink(buf); + + /* copy stdin to the tmp file */ + while ((n = read(0,buf,BUFSIZ)) > 0) { +@@ -1784,23 +1785,10 @@ int main(int argc, char *argv[]) { + exit(1); + } + } +- if (lseek(fd,0,SEEK_SET) < 0) { +- pdfError(-1,const_cast("Can't rewind temporary file")); +- close(fd); +- exit(1); +- } +- +- if ((fp = fdopen(fd,"rb")) == 0) { +- pdfError(-1,const_cast("Can't fdopen temporary file")); +- close(fd); +- exit(1); +- } +- +- obj.initNull(); +- parsePDFTOPDFComment(fp); +- rewind(fp); +- str = new FileStream(fp,0,gFalse,0,&obj); +- doc = new PDFDoc(str); ++ close(fd); ++ doc = new PDFDoc(new GooString(name)); ++ /* remove name */ ++ unlink(name); + } else { + GooString *fileName = new GooString(argv[6]); + /* argc == 7 filenmae is specified */ +diff -up cups-filters-1.0.35/NEWS.poppler023 cups-filters-1.0.35/NEWS diff --git a/SPECS/cups-filters.spec b/SPECS/cups-filters.spec index b782f0c..d6e3643 100644 --- a/SPECS/cups-filters.spec +++ b/SPECS/cups-filters.spec @@ -4,7 +4,7 @@ Summary: OpenPrinting CUPS filters and backends Name: cups-filters Version: 1.0.35 -Release: 15%{?dist}.1 +Release: 21%{?dist} # For a breakdown of the licensing, see COPYING file # GPLv2: filters: commandto*, imagetoraster, pdftops, rasterto*, @@ -33,6 +33,10 @@ Patch8: cups-filters-pdftoopvp.patch Patch9: cups-filters-CVE-2013-6475.patch Patch10: cups-filters-CVE-2014-4337.patch Patch11: cups-filters-CVE-2014-4338.patch +Patch12: cups-filters-poppler023.patch +Patch13: cups-filters-browsed-underscore.patch +Patch14: cups-filters-browsed-efficiency.patch +Patch15: cups-filters-CVE-2015-3258-3279.patch Requires: cups-filters-libs%{?_isa} = %{version}-%{release} @@ -145,6 +149,20 @@ This is the development package for OpenPrinting CUPS filters and backends. # (CVE-2014-4338, bug #1091568). %patch11 -p1 -b .CVE-2014-4338 +# Build against newer poppler (bug #1217552). +%patch12 -p1 -b .poppler023 + +# Fix cups-browsed "_" handling for printer names (bug #1167408). +%patch13 -p1 -b .browsed-underscore + +# Improve cups-browsed efficiency (bug #1191691). +# Fetch printer descriptions with cups-browsed (bug #1223719). +%patch14 -p1 -b .browsed-efficiency + +# Fix heap-based buffer overflow in texttopdf filter (bug #1194263, +# CVE-2015-3258, CVE-2015-3279). +%patch15 -p1 -b .CVE-2015-3258-3279 + %build # work-around Rpath ./autogen.sh @@ -251,7 +269,23 @@ fi %{_libdir}/libfontembed.so %changelog -* Wed Oct 8 2014 Tim Waugh - 1.0.35-15:.1 +* Thu Jul 9 2015 Tim Waugh - 1.0.35-21 +- Fix heap-based buffer overflow in texttopdf filter (bug #1241242, + CVE-2015-3258, CVE-2015-3279). + +* Thu Jun 25 2015 Tim Waugh - 1.0.35-20 +- Improvements to cups-browsed efficiency patch (bug #1191691). + +* Mon Jun 22 2015 Tim Waugh - 1.0.35-18 +- Fix segfault in texttopdf filter (bug #1194263). +- Improve cups-browsed efficiency (bug #1191691). +- Fetch printer descriptions with cups-browsed (bug #1223719). +- Fix cups-browsed "_" handling for printer names (bug #1167408). + +* Tue Jun 16 2015 Tim Waugh - 1.0.35-17 +- Build against newer poppler (bug #1217552). + +* Wed Oct 8 2014 Tim Waugh - 1.0.35-16 - Applied upstream patch to fix BrowseAllow parsing issue (CVE-2014-4338, bug #1091568). - Applied upstream patch for cups-browsed DoS via