]> Pileus Git - ~andy/gtk/blobdiff - modules/printbackends/cups/gtkcupsutils.c
printing: List Avahi printers
[~andy/gtk] / modules / printbackends / cups / gtkcupsutils.c
index 7ef202c3b6176d7446c5e98697367453c7bcd6a7..f0bb951b5d4c62a00d178e53464bf8b8916de97a 100644 (file)
@@ -14,9 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "config.h"
@@ -83,6 +81,28 @@ static GtkCupsRequestStateFunc get_states[] = {
   _get_read_data
 };
 
+#ifndef HAVE_CUPS_API_1_6
+#define ippSetOperation(ipp_request, ipp_op_id) ipp_request->request.op.operation_id = ipp_op_id
+#define ippSetRequestId(ipp_request, ipp_rq_id) ipp_request->request.op.request_id = ipp_rq_id
+#define ippSetState(ipp_request, ipp_state) ipp_request->state = ipp_state
+#define ippGetString(attr, index, foo) attr->values[index].string.text
+#define ippGetCount(attr) attr->num_values
+
+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
+
 static void
 gtk_cups_result_set_error (GtkCupsResult    *result,
                            GtkCupsErrorType  error_type,
@@ -165,8 +185,8 @@ gtk_cups_request_new_with_username (http_t             *connection,
   request->data_io = data_io;
 
   request->ipp_request = ippNew ();
-  request->ipp_request->request.op.operation_id = operation_id;
-  request->ipp_request->request.op.request_id = 1;
+  ippSetOperation (request->ipp_request, operation_id);
+  ippSetRequestId (request->ipp_request, 1);
 
   language = cupsLangDefault ();
 
@@ -187,6 +207,10 @@ gtk_cups_request_new_with_username (http_t             *connection,
                                      "requesting-user-name",
                                      NULL, cupsUser ());
 
+  request->auth_info_required = NULL;
+  request->auth_info = NULL;
+  request->need_auth_info = FALSE;
+
   cupsLangFree (language);
 
   return request;
@@ -241,6 +265,7 @@ gtk_cups_request_free (GtkCupsRequest *request)
     }
 
   g_free (request->username);
+  g_strfreev (request->auth_info_required);
 
   gtk_cups_result_free (request->result);
 
@@ -248,34 +273,46 @@ gtk_cups_request_free (GtkCupsRequest *request)
 }
 
 gboolean 
-gtk_cups_request_read_write (GtkCupsRequest *request)
+gtk_cups_request_read_write (GtkCupsRequest *request, gboolean connect_only)
 {
-  if (request->type == GTK_CUPS_POST)
-    post_states[request->state] (request);
-  else if (request->type == GTK_CUPS_GET)
-    get_states[request->state] (request);
+  if (connect_only && request->state != GTK_CUPS_REQUEST_START)
+    return FALSE;
 
-  if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && 
-      request->state != GTK_CUPS_REQUEST_DONE)
+  do
     {
-      /* TODO: should add a status or error code for too many failed attempts */
-      gtk_cups_result_set_error (request->result, 
-                                 GTK_CUPS_ERROR_GENERAL,
-                                 0,
-                                 0, 
-                                 "Too many failed attempts");
+      if (request->type == GTK_CUPS_POST)
+        post_states[request->state] (request);
+      else if (request->type == GTK_CUPS_GET)
+        get_states[request->state] (request);
 
-      request->state = GTK_CUPS_REQUEST_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-    }
-    
-  if (request->state == GTK_CUPS_REQUEST_DONE)
-    {
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-      return TRUE;
+      if (gtk_cups_result_is_error (request->result))
+        request->state = GTK_CUPS_REQUEST_DONE;
+
+      if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
+          request->state != GTK_CUPS_REQUEST_DONE)
+        {
+          /* TODO: should add a status or error code for too many failed attempts */
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_GENERAL,
+                                     0,
+                                     0,
+                                     "Too many failed attempts");
+
+          request->state = GTK_CUPS_REQUEST_DONE;
+        }
+
+      if (request->state == GTK_CUPS_REQUEST_DONE)
+        {
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          return TRUE;
+        }
     }
-  else
-    return FALSE;
+  /* We need to recheck using httpCheck if the poll_state is read, because
+   * Cups has an internal read buffer. And if this buffer is filled, we may
+   * never get a poll event again. */
+  while (request->poll_state == GTK_CUPS_HTTP_READ && request->http && httpCheck(request->http));
+
+  return FALSE;
 }
 
 GtkCupsPollState 
@@ -338,8 +375,8 @@ gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
                                   name,
                                   tag);
 
-  if (attribute != NULL && attribute->values != NULL)
-    return attribute->values[0].string.text;
+  if (attribute != NULL && ippGetCount (attribute) > 0)
+      return ippGetString (attribute, 0, NULL);
   else
     return NULL;
 }
@@ -383,7 +420,8 @@ static const ipp_option_t ipp_options[] = {
   { "saturation",              IPP_TAG_INTEGER },
   { "scaling",                 IPP_TAG_INTEGER },
   { "sides",                   IPP_TAG_KEYWORD },
-  { "wrap",                    IPP_TAG_BOOLEAN }
+  { "wrap",                    IPP_TAG_BOOLEAN },
+  { "number-up-layout",                IPP_TAG_INTEGER }
 };
 
 
@@ -632,11 +670,19 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
     }
 }
                                
+void
+gtk_cups_request_set_ipp_version (GtkCupsRequest     *request,
+                                 gint                major,
+                                 gint                minor)
+{
+  ippSetVersion (request->ipp_request, major, minor);
+}
 
 static void
 _connect (GtkCupsRequest *request)
 {
   request->poll_state = GTK_CUPS_HTTP_IDLE;
+  request->bytes_received = 0;
 
   if (request->http == NULL)
     {
@@ -715,7 +761,7 @@ _post_send (GtkCupsRequest *request)
     request->attempts = 0;
 
     request->state = GTK_CUPS_POST_WRITE_REQUEST;
-    request->ipp_request->state = IPP_IDLE;
+    ippSetState (request->ipp_request, IPP_IDLE);
 }
 
 static void 
@@ -817,11 +863,7 @@ _post_write_data (GtkCupsRequest *request)
         }
 
 
-#if HAVE_CUPS_API_1_2
       if (httpWrite2 (request->http, buffer, bytes) < bytes)
-#else
-      if (httpWrite (request->http, buffer, (int) bytes) < bytes)
-#endif /* HAVE_CUPS_API_1_2 */
         {
           int http_errno;
 
@@ -902,8 +944,8 @@ _get_auth (GtkCupsRequest *request)
  * The callback sets cups_password to NULL to signal that the 
  * password has been used.
  */
-static char *cups_password;
-static char *cups_username;
+static char *cups_password = NULL;
+static char *cups_username = NULL;
 
 static const char *
 passwordCB (const char *prompt)
@@ -939,6 +981,7 @@ _post_check (GtkCupsRequest *request)
 
       if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
         {
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
           request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
           request->state = GTK_CUPS_POST_AUTH;
           request->need_password = TRUE;
@@ -956,7 +999,6 @@ _post_check (GtkCupsRequest *request)
         {
           if (request->password_state == GTK_CUPS_PASSWORD_NONE)
             {
-              cups_password = g_strdup ("");
               cups_username = request->username;
               cupsSetPasswordCB (passwordCB);
 
@@ -968,6 +1010,7 @@ _post_check (GtkCupsRequest *request)
                   /* move to AUTH state to let the backend 
                    * ask for a password
                    */ 
+                  request->poll_state = GTK_CUPS_HTTP_IDLE;
                   request->state = GTK_CUPS_POST_AUTH;
                   request->need_password = TRUE;
 
@@ -1207,7 +1250,7 @@ _get_send (GtkCupsRequest *request)
   request->state = GTK_CUPS_GET_CHECK;
   request->poll_state = GTK_CUPS_HTTP_READ;
   
-  request->ipp_request->state = IPP_IDLE;
+  ippSetState (request->ipp_request, IPP_IDLE);
 }
 
 static void 
@@ -1233,6 +1276,7 @@ _get_check (GtkCupsRequest *request)
 
       if (request->password_state == GTK_CUPS_PASSWORD_APPLIED)
         {
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
           request->password_state = GTK_CUPS_PASSWORD_NOT_VALID;
           request->state = GTK_CUPS_GET_AUTH;
           request->need_password = TRUE;
@@ -1250,7 +1294,6 @@ _get_check (GtkCupsRequest *request)
         {
           if (request->password_state == GTK_CUPS_PASSWORD_NONE)
             {
-              cups_password = g_strdup ("");
               cups_username = request->username;
               cupsSetPasswordCB (passwordCB);
 
@@ -1262,6 +1305,7 @@ _get_check (GtkCupsRequest *request)
                   /* move to AUTH state to let the backend
                    * ask for a password
                    */
+                  request->poll_state = GTK_CUPS_HTTP_IDLE;
                   request->state = GTK_CUPS_GET_AUTH;
                   request->need_password = TRUE;
 
@@ -1308,7 +1352,7 @@ _get_check (GtkCupsRequest *request)
           return;
         }
 
-      request->state = GTK_CUPS_GET_SEND;
+      request->state = GTK_CUPS_GET_CONNECT;
       request->last_status = HTTP_CONTINUE;
 
      return;
@@ -1393,23 +1437,12 @@ _get_read_data (GtkCupsRequest *request)
 
   request->poll_state = GTK_CUPS_HTTP_READ;
 
-#if HAVE_CUPS_API_1_2
   bytes = httpRead2 (request->http, buffer, sizeof (buffer));
-#else
-  bytes = httpRead (request->http, buffer, sizeof (buffer));
-#endif /* HAVE_CUPS_API_1_2 */
+  request->bytes_received += bytes;
 
   GTK_NOTE (PRINTING,
-            g_print ("CUPS Backend: %i bytes read\n", bytes));
-  
-  if (bytes == 0)
-    {
-      request->state = GTK_CUPS_GET_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
+            g_print ("CUPS Backend: %"G_GSIZE_FORMAT" bytes read\n", bytes));
 
-      return;
-    }
-  
   io_status =
     g_io_channel_write_chars (request->data_io, 
                               buffer, 
@@ -1429,6 +1462,15 @@ _get_read_data (GtkCupsRequest *request)
                                  error->message);
       g_error_free (error);
     }
+
+  /* Stop if we do not expect any more data or EOF was received. */
+  if (httpGetLength2 (request->http) <= request->bytes_received || bytes == 0)
+    {
+      request->state = GTK_CUPS_GET_DONE;
+      request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+      return;
+    }
 }
 
 gboolean
@@ -1480,7 +1522,6 @@ GtkCupsConnectionTest *
 gtk_cups_connection_test_new (const char *server)
 {
   GtkCupsConnectionTest *result = NULL;
-#ifdef HAVE_CUPS_API_1_2
   gchar                 *port_str = NULL;
 
   result = g_new (GtkCupsConnectionTest, 1);
@@ -1496,12 +1537,10 @@ gtk_cups_connection_test_new (const char *server)
 
   result->socket = -1;
   result->current_addr = NULL;
+  result->last_wrong_addr = NULL;
   result->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
 
   result->at_init = gtk_cups_connection_test_get_state (result);
-#else
-  result = g_new (GtkCupsConnectionTest, 1);
-#endif
 
   return result;
 }
@@ -1515,7 +1554,6 @@ gtk_cups_connection_test_new (const char *server)
 GtkCupsConnectionState 
 gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
 {
-#ifdef HAVE_CUPS_API_1_2
   GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
   http_addrlist_t       *iter;
   gint                   error_code;
@@ -1534,7 +1572,14 @@ gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
     {
       if (test->socket == -1)
         {
-          iter = test->addrlist;
+          if (test->last_wrong_addr != NULL && test->last_wrong_addr->next != NULL)
+            iter = test->last_wrong_addr->next;
+          else
+            {
+              test->last_wrong_addr = NULL;
+              iter = test->addrlist;
+            }
+
           while (iter)
             {
               test->socket = socket (iter->addr.addr.sa_family,
@@ -1566,7 +1611,7 @@ gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
 
           error_code = errno;
 
-          if (code == 0)
+          if (code == 0 || error_code == EISCONN)
             {
               close (test->socket);
               test->socket = -1;
@@ -1578,15 +1623,17 @@ gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
               if (error_code == EALREADY || error_code == EINPROGRESS)
                 result = GTK_CUPS_CONNECTION_IN_PROGRESS;
               else
-                result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+                {
+                  close (test->socket);
+                  test->socket = -1;
+                  test->last_wrong_addr = test->current_addr;
+                  result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+                }
             }
          }
 
       return result;
     }
-#else
-  return GTK_CUPS_CONNECTION_AVAILABLE;
-#endif
 }
 
 /* This function frees memory used by the GtkCupsConnectionTest structure.
@@ -1597,14 +1644,13 @@ gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
   if (test == NULL)
     return;
 
-#ifdef HAVE_CUPS_API_1_2
   test->current_addr = NULL;
+  test->last_wrong_addr = NULL;
   httpAddrFreeList (test->addrlist);
   if (test->socket != -1)
     {
       close (test->socket);
       test->socket = -1;
     }
-#endif
   g_free (test);
 }