Based on f0e1fc43dc5ec79a783cf2df82d9df827f3ccece Mon Sep 17 00:00:00 2001
From: Till Kamppeter <till.kamppeter@gmail.com>
Date: Fri, 15 Mar 2019 18:35:47 +0100
Subject: [PATCH] cups-browsed: Let distribution of jobs sent to queues with
 "implicitclass" backend be done by a "job-state" CUPS notification.

---
 NEWS                 |    9 ++
 utils/cups-browsed.c |  162 +++++++++++++++++++++++++++++----------------------
 2 files changed, 102 insertions(+), 69 deletions(-)

--- NEWS
+++ NEWS	2019-10-15 09:08:21.159736556 +0000
@@ -1,6 +1,15 @@
 NEWS - OpenPrinting CUPS Filters v1.20.3 - 2018-04-12
 -----------------------------------------------------
 
+BACKPORT OF V1.22.2
+	- cups-browsed: Let distribution of jobs sent to queues with
+	  "implicitclass" backend (usually clusters) be done by a
+	  "job-state" CUPS notification and not by
+	  "printer-state-changed" any more. The "job-state"
+	  notification already contains the job ID. Before we had to
+	  poll the job ID from CUPS via IPP which was sometimes
+	  unreliable (Issue #97).
+
 CHANGES IN V1.20.3
 
 	- braille: Do not remove read permission on cups-brf. Thanks
--- utils/cups-browsed.c
+++ utils/cups-browsed.c	2019-10-15 09:05:53.830475542 +0000
@@ -2805,39 +2805,7 @@ on_printer_state_changed (CupsNotifier *
                           gboolean printer_is_accepting_jobs,
                           gpointer user_data)
 {
-  int i;
-  char *ptr, buf[1024];
-  remote_printer_t *p, *q;
-  http_t *http = NULL;
-  ipp_t *request, *response;
-  ipp_attribute_t *attr;
-  const char *pname = NULL;
-  char *remote_cups_queue;
-  ipp_pstate_t pstate = IPP_PRINTER_IDLE;
-  int paccept = 0;
-  int num_jobs, min_jobs = 99999999;
-  cups_job_t *jobs = NULL;
-  const char *dest_host = NULL;
-  int dest_port = 0;
-  char dest_name[1024];
-  int dest_index = 0;
-  int valid_dest_found = 0;
-  char uri[HTTP_MAX_URI];
-  int job_id = 0;
-  int num_options;
-  cups_option_t *options;
-  static const char *pattrs[] =
-                {
-                  "printer-name",
-                  "printer-state",
-                  "printer-is-accepting-jobs"
-                };
-  static const char *jattrs[] =
-		{
-		  "job-id",
-		  "job-state"
-		};
-  http_t *conn = NULL;
+  char *ptr, buf[2048];
 
   debug_printf("on_printer_state_changed() in THREAD %ld\n", pthread_self());
 
@@ -2916,7 +2884,94 @@ on_printer_state_changed (CupsNotifier *
     strncpy(buf, text, ptr - text);
     buf[ptr - text] = '\0';
     debug_printf("[CUPS Notification] %s not default printer any more.\n", buf);
-  } else if ((ptr = strstr(text, " state changed to processing")) != NULL) {
+  }
+}
+
+static void
+on_job_state (CupsNotifier *object,
+	      const gchar *text,
+	      const gchar *printer_uri,
+	      const gchar *printer,
+	      guint printer_state,
+	      const gchar *printer_state_reasons,
+	      gboolean printer_is_accepting_jobs,
+	      guint job_id,
+	      guint job_state,
+	      const gchar *job_state_reasons,
+	      const gchar *job_name,
+	      guint job_impressions_completed,
+	      gpointer user_data)
+{
+  int i;
+  char buf[2048];
+  remote_printer_t *p, *q;
+  http_t *http = NULL;
+  ipp_t *request, *response;
+  ipp_attribute_t *attr;
+  const char *pname = NULL;
+  char *remote_cups_queue;
+  ipp_pstate_t pstate = IPP_PRINTER_IDLE;
+  int paccept = 0;
+  int num_jobs, min_jobs = 99999999;
+  cups_job_t *jobs = NULL;
+  const char *dest_host = NULL;
+  int dest_port = 0;
+  char dest_name[1024];
+  int dest_index = 0;
+  int valid_dest_found = 0;
+  char uri[HTTP_MAX_URI];
+  /*int job_id = 0;*/
+  int num_options;
+  cups_option_t *options;
+  static const char *pattrs[] =
+                {
+                  "printer-name",
+                  "printer-state",
+                  "printer-is-accepting-jobs"
+                };
+  http_t *conn = NULL;
+
+  debug_printf("on_job_state() in THREAD %ld\n", pthread_self());
+
+  debug_printf("[CUPS Notification] Job state changed on printer %s: %s\n",
+	       printer, text);
+  debug_printf("[CUPS Notification] Printer state reasons: %s\n",
+	       printer_state_reasons);
+  debug_printf("[CUPS Notification] Job ID: %d\n",
+	       job_id);
+  debug_printf("[CUPS Notification] Job State: %s\n",
+	       job_state_reasons);
+  debug_printf("[CUPS Notification] Job is processing: %s\n",
+	       job_state == IPP_JOB_PROCESSING ? "Yes" : "No");
+
+  if (terminating) {
+    debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
+    return;
+  }
+
+  if (autoshutdown && autoshutdown_on == NO_JOBS) {
+    if (check_jobs() == 0) {
+      /* If auto shutdown is active for triggering on no jobs being left, we
+	 schedule the shutdown in autoshutdown_timeout seconds */
+      if (!autoshutdown_exec_id) {
+	debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n", autoshutdown_timeout);
+	autoshutdown_exec_id =
+	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
+				 NULL);
+      }
+    } else {
+      /* If auto shutdown is active for triggering on no jobs being left, we
+	 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
+         again. */
+      if (autoshutdown_exec_id) {
+	debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
+	g_source_remove(autoshutdown_exec_id);
+	autoshutdown_exec_id = 0;
+      }
+    }
+  }
+
+  if (job_id != 0 && job_state == IPP_JOB_PROCESSING) {
     /* Printer started processing a job, check if it uses the implicitclass
        backend and if so, we select the remote queue to which to send the job
        in a way so that we get load balancing between all remote queues
@@ -3102,46 +3157,13 @@ on_printer_state_changed (CupsNotifier *
 	if (i == q->last_printer)
 	  break;
       }
-      /* Find the ID of the current job */
-      request = ippNewRequest(IPP_GET_JOBS);
-      httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-		       "localhost", ippPort(), "/printers/%s", printer);
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
-		   "printer-uri", NULL, uri);
-      ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
-		    "requested-attributes",
-		    sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs);
-      job_id = 0;
-      if ((response = cupsDoRequest(conn, request, "/")) != NULL) {
-	/* Get the current active job on this queue... */
-	ipp_jstate_t jobstate = IPP_JOB_PENDING;
-	for (attr = ippFirstAttribute(response); attr != NULL;
-	     attr = ippNextAttribute(response)) {
-	  if (!ippGetName(attr)) {
-	    if (jobstate == IPP_JOB_PROCESSING)
-	      break;
-	    else
-	      continue;
-	  }
-	  if (!strcmp(ippGetName(attr), "job-id") &&
-	      ippGetValueTag(attr) == IPP_TAG_INTEGER)
-	    job_id = ippGetInteger(attr, 0);
-	  else if (!strcmp(ippGetName(attr), "job-state") &&
-		   ippGetValueTag(attr) == IPP_TAG_ENUM)
-	    jobstate = (ipp_jstate_t)ippGetInteger(attr, 0);
-	}
-	if (jobstate != IPP_JOB_PROCESSING)
-	  job_id = 0;
-	ippDelete(response);
-      }
-      if (job_id == 0)
-	debug_printf("ERROR: could not determine ID of curremt job on %s\n",
-		     printer);
 
       /* Write the selected destination host into an option of our implicit
 	 class queue (cups-browsed-dest-printer="<dest>") so that the
 	 implicitclass backend will pick it up */
       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
+      httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+		       "localhost", ippPort(), "/printers/%s", printer);
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
 		   "printer-uri", NULL, uri);
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
@@ -8436,6 +8458,8 @@ int main(int argc, char*argv[]) {
   if (cups_notifier != NULL) {
     g_signal_connect (cups_notifier, "printer-state-changed",
 		      G_CALLBACK (on_printer_state_changed), NULL);
+    g_signal_connect (cups_notifier, "job-state",
+		      G_CALLBACK (on_job_state), NULL);
     g_signal_connect (cups_notifier, "printer-deleted",
 		      G_CALLBACK (on_printer_deleted), NULL);
     g_signal_connect (cups_notifier, "printer-modified",
