]> Pileus Git - ~andy/gtk/blobdiff - modules/printbackends/cups/gtkcupsutils.c
printing: List Avahi printers
[~andy/gtk] / modules / printbackends / cups / gtkcupsutils.c
index a19639783d377a4e16c11409a1f4fb523463c1bb..f0bb951b5d4c62a00d178e53464bf8b8916de97a 100644 (file)
@@ -1,7 +1,7 @@
 /* GTK - The GIMP Toolkit
  * gtkcupsutils.h: Statemachine implementation of POST and GET 
- * cup calls which can be used to create a non-blocking cups API
- * Copyright (C) 2003, Red Hat, Inc.
+ * cups calls which can be used to create a non-blocking cups API
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * 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 "gtkcupsutils.h"
 #include "config.h"
-#include "gtkdebug.h"
+#include <gtk/gtk.h>
+#include "gtkcupsutils.h"
 
 #include <errno.h>
 #include <unistd.h>
 #include <sys/stat.h>
 #include <stdlib.h>
 #include <time.h>
-
-#if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR > 1) || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR == 1 && CUPS_VERSION_PATCH >= 20)
-#define HAVE_HTTP_AUTHSTRING 1
-#endif
+#include <fcntl.h>
+#include <sys/socket.h>
 
 typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
 
@@ -41,16 +37,23 @@ static void _post_send          (GtkCupsRequest *request);
 static void _post_write_request (GtkCupsRequest *request);
 static void _post_write_data    (GtkCupsRequest *request);
 static void _post_check         (GtkCupsRequest *request);
+static void _post_auth          (GtkCupsRequest *request);
 static void _post_read_response (GtkCupsRequest *request);
 
 static void _get_send           (GtkCupsRequest *request);
 static void _get_check          (GtkCupsRequest *request);
+static void _get_auth           (GtkCupsRequest *request);
 static void _get_read_data      (GtkCupsRequest *request);
 
 struct _GtkCupsResult
 {
   gchar *error_msg;
   ipp_t *ipp_response;
+  GtkCupsErrorType error_type;
+
+  /* some error types like HTTP_ERROR have a status and a code */
+  int error_status;            
+  int error_code;
 
   guint is_error : 1;
   guint is_ipp_response : 1;
@@ -60,28 +63,61 @@ struct _GtkCupsResult
 #define _GTK_CUPS_MAX_ATTEMPTS 10 
 #define _GTK_CUPS_MAX_CHUNK_SIZE 8192
 
-GtkCupsRequestStateFunc post_states[] = {_connect,
-                                         _post_send,
-                                         _post_write_request,
-                                         _post_write_data,
-                                         _post_check,
-                                         _post_read_response};
+static GtkCupsRequestStateFunc post_states[] = {
+  _connect,
+  _post_send,
+  _post_write_request,
+  _post_write_data,
+  _post_check,
+  _post_auth,
+  _post_read_response
+};
+
+static GtkCupsRequestStateFunc get_states[] = {
+  _connect,
+  _get_send,
+  _get_check,
+  _get_auth,
+  _get_read_data
+};
 
-GtkCupsRequestStateFunc get_states[] = {_connect,
-                                        _get_send,
-                                        _get_check,
-                                        _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, 
-                           const char *error_msg,
+gtk_cups_result_set_error (GtkCupsResult    *result,
+                           GtkCupsErrorType  error_type,
+                           int               error_status,
+                           int               error_code, 
+                           const char       *error_msg,
                           ...)
 {
   va_list args;
 
   result->is_ipp_response = FALSE;
-
   result->is_error = TRUE;
+  result->error_type = error_type;
+  result->error_status = error_status;
+  result->error_code = error_code;
 
   va_start (args, error_msg);
   result->error_msg = g_strdup_vprintf (error_msg, args);
@@ -89,12 +125,13 @@ gtk_cups_result_set_error (GtkCupsResult *result,
 }
 
 GtkCupsRequest *
-gtk_cups_request_new (http_t *connection,
-                      GtkCupsRequestType req_type, 
-                      gint operation_id,
-                      GIOChannel *data_io,
-                      const char *server,
-                      const char *resource)
+gtk_cups_request_new_with_username (http_t             *connection,
+                                    GtkCupsRequestType  req_type, 
+                                    gint                operation_id,
+                                    GIOChannel         *data_io,
+                                    const char         *server,
+                                    const char         *resource,
+                                    const char         *username)
 {
   GtkCupsRequest *request;
   cups_lang_t *language;
@@ -111,10 +148,12 @@ gtk_cups_request_new (http_t *connection,
   request->type = req_type;
   request->state = GTK_CUPS_REQUEST_START;
 
+  request->password_state = GTK_CUPS_PASSWORD_NONE;
+
    if (server)
     request->server = g_strdup (server);
   else
-    request->server = g_strdup (cupsServer());
+    request->server = g_strdup (cupsServer ());
 
 
   if (resource)
@@ -130,7 +169,9 @@ gtk_cups_request_new (http_t *connection,
   else
     {
       request->http = NULL;
-      request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
+      request->http = httpConnectEncrypt (request->server, 
+                                          ippPort (), 
+                                          cupsEncryption ());
 
       if (request->http)
         httpBlocking (request->http, 0);
@@ -143,9 +184,9 @@ gtk_cups_request_new (http_t *connection,
   request->attempts = 0;
   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;
+  request->ipp_request = ippNew ();
+  ippSetOperation (request->ipp_request, operation_id);
+  ippSetRequestId (request->ipp_request, 1);
 
   language = cupsLangDefault ();
 
@@ -157,15 +198,41 @@ gtk_cups_request_new (http_t *connection,
                                    "attributes-natural-language", 
                                    NULL, language->language);
 
-  gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-                                   "requesting-user-name",
-                                   NULL, cupsUser());
-  
+  if (username != NULL)
+    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                                     "requesting-user-name",
+                                     NULL, username);
+  else
+    gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+                                     "requesting-user-name",
+                                     NULL, cupsUser ());
+
+  request->auth_info_required = NULL;
+  request->auth_info = NULL;
+  request->need_auth_info = FALSE;
+
   cupsLangFree (language);
 
   return request;
 }
 
+GtkCupsRequest *
+gtk_cups_request_new (http_t             *connection,
+                      GtkCupsRequestType  req_type, 
+                      gint                operation_id,
+                      GIOChannel         *data_io,
+                      const char         *server,
+                      const char         *resource)
+{
+  return gtk_cups_request_new_with_username (connection,
+                                             req_type,
+                                             operation_id,
+                                             data_io,
+                                             server,
+                                             resource,
+                                             NULL);
+}
+
 static void
 gtk_cups_result_free (GtkCupsResult *result)
 {
@@ -181,14 +248,24 @@ void
 gtk_cups_request_free (GtkCupsRequest *request)
 {
   if (request->own_http)
-    if (request->http)
-      httpClose (request->http);
+    {
+      if (request->http)
+        httpClose (request->http);
+    }
   
   if (request->ipp_request)
     ippDelete (request->ipp_request);
 
   g_free (request->server);
   g_free (request->resource);
+  if (request->password != NULL)
+    {
+      memset (request->password, 0, strlen (request->password));
+      g_free (request->password);
+    }
+
+  g_free (request->username);
+  g_strfreev (request->auth_info_required);
 
   gtk_cups_result_free (request->result);
 
@@ -196,30 +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)
-    {
-      gtk_cups_result_set_error (request->result, "Too many failed attempts");
-      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;
-    }
-  else
+  do
     {
-      return FALSE;
+      if (request->type == GTK_CUPS_POST)
+        post_states[request->state] (request);
+      else if (request->type == GTK_CUPS_GET)
+        get_states[request->state] (request);
+
+      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;
+        }
     }
+  /* 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 
@@ -238,11 +331,11 @@ gtk_cups_request_get_result (GtkCupsRequest *request)
 
 void            
 gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
-                                 ipp_tag_t group,
-                                 ipp_tag_t tag,
-                                 const char *name,
-                                 const char *charset,
-                                 const char *value)
+                                 ipp_tag_t       group,
+                                 ipp_tag_t       tag,
+                                 const char     *name,
+                                 const char     *charset,
+                                 const char     *value)
 {
   ippAddString (request->ipp_request,
                 group,
@@ -253,13 +346,13 @@ gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
 }
 
 void            
-gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
-                                 ipp_tag_t group,
-                                 ipp_tag_t tag,
-                                 const char *name,
-                                 int num_values,
-                                 const char *charset,
-                                 const char * const *values)
+gtk_cups_request_ipp_add_strings (GtkCupsRequest    *request,
+                                 ipp_tag_t          group,
+                                 ipp_tag_t          tag,
+                                 const char        *name,
+                                 int                num_values,
+                                 const char        *charset,
+                                 const char *const *values)
 {
   ippAddStrings (request->ipp_request,
                 group,
@@ -270,6 +363,23 @@ gtk_cups_request_ipp_add_strings (GtkCupsRequest *request,
                 values);
 }
 
+const char *
+gtk_cups_request_ipp_get_string (GtkCupsRequest *request,
+                                 ipp_tag_t       tag,
+                                 const char     *name)
+{
+  ipp_attribute_t *attribute = NULL;
+
+  if (request != NULL && request->ipp_request != NULL)
+    attribute = ippFindAttribute (request->ipp_request,
+                                  name,
+                                  tag);
+
+  if (attribute != NULL && ippGetCount (attribute) > 0)
+      return ippGetString (attribute, 0, NULL);
+  else
+    return NULL;
+}
 
 
 typedef struct
@@ -278,41 +388,41 @@ typedef struct
   ipp_tag_t    value_tag;
 } ipp_option_t;
 
-static const ipp_option_t ipp_options[] =
-                       {
-                         { "blackplot",                IPP_TAG_BOOLEAN },
-                         { "brightness",               IPP_TAG_INTEGER },
-                         { "columns",                  IPP_TAG_INTEGER },
-                         { "copies",                   IPP_TAG_INTEGER },
-                         { "finishings",               IPP_TAG_ENUM },
-                         { "fitplot",                  IPP_TAG_BOOLEAN },
-                         { "gamma",                    IPP_TAG_INTEGER },
-                         { "hue",                      IPP_TAG_INTEGER },
-                         { "job-k-limit",              IPP_TAG_INTEGER },
-                         { "job-page-limit",           IPP_TAG_INTEGER },
-                         { "job-priority",             IPP_TAG_INTEGER },
-                         { "job-quota-period",         IPP_TAG_INTEGER },
-                         { "landscape",                IPP_TAG_BOOLEAN },
-                         { "media",                    IPP_TAG_KEYWORD },
-                         { "mirror",                   IPP_TAG_BOOLEAN },
-                         { "natural-scaling",          IPP_TAG_INTEGER },
-                         { "number-up",                IPP_TAG_INTEGER },
-                         { "orientation-requested",    IPP_TAG_ENUM },
-                         { "page-bottom",              IPP_TAG_INTEGER },
-                         { "page-left",                IPP_TAG_INTEGER },
-                         { "page-ranges",              IPP_TAG_RANGE },
-                         { "page-right",               IPP_TAG_INTEGER },
-                         { "page-top",                 IPP_TAG_INTEGER },
-                         { "penwidth",                 IPP_TAG_INTEGER },
-                         { "ppi",                      IPP_TAG_INTEGER },
-                         { "prettyprint",              IPP_TAG_BOOLEAN },
-                         { "printer-resolution",       IPP_TAG_RESOLUTION },
-                         { "print-quality",            IPP_TAG_ENUM },
-                         { "saturation",               IPP_TAG_INTEGER },
-                         { "scaling",                  IPP_TAG_INTEGER },
-                         { "sides",                    IPP_TAG_KEYWORD },
-                         { "wrap",                     IPP_TAG_BOOLEAN }
-                       };
+static const ipp_option_t ipp_options[] = {
+  { "blackplot",               IPP_TAG_BOOLEAN },
+  { "brightness",              IPP_TAG_INTEGER },
+  { "columns",                 IPP_TAG_INTEGER },
+  { "copies",                  IPP_TAG_INTEGER },
+  { "finishings",              IPP_TAG_ENUM },
+  { "fitplot",                 IPP_TAG_BOOLEAN },
+  { "gamma",                   IPP_TAG_INTEGER },
+  { "hue",                     IPP_TAG_INTEGER },
+  { "job-k-limit",             IPP_TAG_INTEGER },
+  { "job-page-limit",          IPP_TAG_INTEGER },
+  { "job-priority",            IPP_TAG_INTEGER },
+  { "job-quota-period",                IPP_TAG_INTEGER },
+  { "landscape",               IPP_TAG_BOOLEAN },
+  { "media",                   IPP_TAG_KEYWORD },
+  { "mirror",                  IPP_TAG_BOOLEAN },
+  { "natural-scaling",         IPP_TAG_INTEGER },
+  { "number-up",               IPP_TAG_INTEGER },
+  { "orientation-requested",   IPP_TAG_ENUM },
+  { "page-bottom",             IPP_TAG_INTEGER },
+  { "page-left",               IPP_TAG_INTEGER },
+  { "page-ranges",             IPP_TAG_RANGE },
+  { "page-right",              IPP_TAG_INTEGER },
+  { "page-top",                        IPP_TAG_INTEGER },
+  { "penwidth",                        IPP_TAG_INTEGER },
+  { "ppi",                     IPP_TAG_INTEGER },
+  { "prettyprint",             IPP_TAG_BOOLEAN },
+  { "printer-resolution",      IPP_TAG_RESOLUTION },
+  { "print-quality",           IPP_TAG_ENUM },
+  { "saturation",              IPP_TAG_INTEGER },
+  { "scaling",                 IPP_TAG_INTEGER },
+  { "sides",                   IPP_TAG_KEYWORD },
+  { "wrap",                    IPP_TAG_BOOLEAN },
+  { "number-up-layout",                IPP_TAG_INTEGER }
+};
 
 
 static ipp_tag_t
@@ -325,14 +435,14 @@ _find_option_tag (const gchar *option)
   result = IPP_TAG_ZERO;
 
   lower_bound = 0;
-  upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
+  upper_bound = num_options = (int) G_N_ELEMENTS (ipp_options) - 1;
   
   while (1)
     {
       int match;
       current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
 
-      match = strcasecmp(option, ipp_options[current_option].name);
+      match = strcasecmp (option, ipp_options[current_option].name);
       if (match == 0)
         {
          result = ipp_options[current_option].value_tag;
@@ -361,15 +471,20 @@ _find_option_tag (const gchar *option)
     }
 }
 
+/*
+ * Note that this function uses IPP_TAG_JOB, so it is
+ * only suitable for IPP Group 2 attributes.
+ * See RFC 2911.
+ */
 void
 gtk_cups_request_encode_option (GtkCupsRequest *request,
-                                const gchar *option,
-                               const gchar *value)
+                                const gchar    *option,
+                               const gchar    *value)
 {
   ipp_tag_t option_tag;
 
-  g_assert (option != NULL);
-  g_assert (value != NULL);
+  g_return_if_fail (option != NULL);
+  g_return_if_fail (value != NULL);
 
   option_tag = _find_option_tag (option);
 
@@ -388,7 +503,7 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
       case IPP_TAG_INTEGER:
       case IPP_TAG_ENUM:
         ippAddInteger (request->ipp_request,
-                       IPP_TAG_OPERATION,
+                       IPP_TAG_JOB,
                        option_tag,
                        option,
                        strtol (value, NULL, 0));
@@ -397,16 +512,18 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
       case IPP_TAG_BOOLEAN:
         {
           char b;
-          b = 0;
-          if (!strcasecmp(value, "true") ||
-             !strcasecmp(value, "on") ||
-             !strcasecmp(value, "yes")
+          
+          if (strcasecmp (value, "true") == 0 ||
+             strcasecmp (value, "on") == 0 ||
+             strcasecmp (value, "yes") == 0
            b = 1;
-         
-          ippAddBoolean(request->ipp_request,
-                        IPP_TAG_OPERATION,
-                        option,
-                        b);
+         else
+            b = 0;
+
+          ippAddBoolean (request->ipp_request,
+                         IPP_TAG_JOB,
+                         option,
+                         b);
         
           break;
         }
@@ -423,12 +540,12 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
              s = (char *)value;
            }
          else
-           lower = strtol(value, &s, 0);
+           lower = strtol (value, &s, 0);
 
          if (*s == '-')
            {
              if (s[1])
-               upper = strtol(s + 1, NULL, 0);
+               upper = strtol (s + 1, NULL, 0);
              else
                upper = 2147483647;
             }
@@ -436,7 +553,7 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
            upper = lower;
          
           ippAddRange (request->ipp_request,
-                       IPP_TAG_OPERATION,
+                       IPP_TAG_JOB,
                        option,
                        lower,
                        upper);
@@ -451,20 +568,20 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
           int yres;
           ipp_res_t units;
           
-          xres = strtol(value, &s, 0);
+          xres = strtol (value, &s, 0);
 
          if (*s == 'x')
-           yres = strtol(s + 1, &s, 0);
+           yres = strtol (s + 1, &s, 0);
          else
            yres = xres;
 
-         if (strcasecmp(s, "dpc") == 0)
+         if (strcasecmp (s, "dpc") == 0)
             units = IPP_RES_PER_CM;
           else
             units = IPP_RES_PER_INCH;
           
           ippAddResolution (request->ipp_request,
-                            IPP_TAG_OPERATION,
+                            IPP_TAG_JOB,
                             option,
                             units,
                             xres,
@@ -474,26 +591,104 @@ gtk_cups_request_encode_option (GtkCupsRequest *request,
         }
 
       default:
-        ippAddString (request->ipp_request,
-                      IPP_TAG_OPERATION,
-                      option_tag,
-                      option,
-                      NULL,
-                      value);
+        {
+          char *values;
+          char *s;
+          int in_quotes;
+          char *next;
+          GPtrArray *strings;
+          
+          values = g_strdup (value);
+          strings = NULL;
+         in_quotes = 0;
+
+          for (s = values, next = s; *s != '\0'; s++)
+            {
+              if (in_quotes != 2 && *s == '\'')
+                {
+                  /* skip quoted value */
+                  if (in_quotes == 0)
+                    in_quotes = 1;
+                  else
+                    in_quotes = 0;
+                }
+              else if (in_quotes != 1 && *s == '\"')
+                {
+                  /* skip quoted value */
+                  if (in_quotes == 0)
+                    in_quotes = 2;
+                  else
+                    in_quotes = 0;
+                }
+              else if (in_quotes == 0 && *s == ',')
+                {
+                  /* found delimiter, add to value array */
+                  *s = '\0';
+                  if (strings == NULL)
+                    strings = g_ptr_array_new ();
+                  g_ptr_array_add (strings, next);
+                  next = s + 1;
+                }
+              else if (in_quotes == 0 && *s == '\\' && s[1] != '\0')
+                {
+                  /* skip escaped character */
+                  s++;
+                }
+            }
+          
+          if (strings == NULL)
+            {
+              /* single value */
+              ippAddString (request->ipp_request,
+                            IPP_TAG_JOB,
+                            option_tag,
+                            option,
+                            NULL,
+                            value);
+            }
+          else
+            {
+              /* multiple values */
+              
+              /* add last value */
+              g_ptr_array_add (strings, next);
+              
+              ippAddStrings (request->ipp_request,
+                             IPP_TAG_JOB,
+                             option_tag,
+                             option,
+                             strings->len,
+                             NULL,
+                             (const char **) strings->pdata);
+              g_ptr_array_free (strings, TRUE);
+            }
+
+          g_free (values);
+        }
 
         break;
     }
 }
                                
+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)
     {
-      request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
+      request->http = httpConnectEncrypt (request->server, 
+                                          ippPort (), 
+                                          cupsEncryption ());
 
       if (request->http == NULL)
         request->attempts++;
@@ -528,28 +723,35 @@ _post_send (GtkCupsRequest *request)
   if (request->data_io != NULL)
     {
       fstat (g_io_channel_unix_get_fd (request->data_io), &data_info);
-      sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
+      sprintf (length, "%lu", (unsigned long) (ippLength (request->ipp_request) + data_info.st_size));
     }
   else
-    {
-      sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
-    }
+    sprintf (length, "%lu", (unsigned long) ippLength (request->ipp_request));
        
-  httpClearFields(request->http);
-  httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
-  httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+  httpClearFields (request->http);
+  httpSetField (request->http, HTTP_FIELD_CONTENT_LENGTH, length);
+  httpSetField (request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+#ifdef HAVE_HTTPGETAUTHSTRING
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
+#else
 #ifdef HAVE_HTTP_AUTHSTRING
-  httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+#endif
 #endif
 
-  if (httpPost(request->http, request->resource))
+  if (httpPost (request->http, request->resource))
     {
-      if (httpReconnect(request->http))
+      if (httpReconnect (request->http))
         {
           request->state = GTK_CUPS_POST_DONE;
           request->poll_state = GTK_CUPS_HTTP_IDLE;
 
-          gtk_cups_result_set_error (request->result, "Failed Post");
+          /* TODO: should add a status or error code for failed post */
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_GENERAL,
+                                     0,
+                                     0,
+                                     "Failed Post");
         }
 
       request->attempts++;
@@ -559,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 
@@ -572,14 +774,20 @@ _post_write_request (GtkCupsRequest *request)
 
   request->poll_state = GTK_CUPS_HTTP_WRITE;
   
-  ipp_status = ippWrite(request->http, request->ipp_request);
+  ipp_status = ippWrite (request->http, request->ipp_request);
 
   if (ipp_status == IPP_ERROR)
     {
+      int cups_error = cupsLastError ();
       request->state = GTK_CUPS_POST_DONE;
       request->poll_state = GTK_CUPS_HTTP_IDLE;
  
-      gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
+      gtk_cups_result_set_error (request->result, 
+                                 GTK_CUPS_ERROR_IPP,
+                                 ipp_status,
+                                 cups_error,
+                                 "%s", 
+                                 ippErrorString (cups_error));
       return;
     }
 
@@ -608,7 +816,7 @@ _post_write_data (GtkCupsRequest *request)
   request->poll_state = GTK_CUPS_HTTP_WRITE;
   
   if (httpCheck (request->http))
-    http_status = httpUpdate(request->http);
+    http_status = httpUpdate (request->http);
   else
     http_status = request->last_status;
 
@@ -635,7 +843,12 @@ _post_write_data (GtkCupsRequest *request)
           request->state = GTK_CUPS_POST_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;
      
-          gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message);
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_IO,
+                                     io_status,
+                                     error->code, 
+                                     "Error reading from cache file: %s",
+                                     error->message);
 
          g_error_free (error);
           return;
@@ -650,25 +863,101 @@ _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 */
+      if (httpWrite2 (request->http, buffer, bytes) < bytes)
         {
+          int http_errno;
+
+          http_errno = httpError (request->http);
+
           request->state = GTK_CUPS_POST_DONE;
          request->poll_state = GTK_CUPS_HTTP_IDLE;
      
-          gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     http_errno, 
+                                     "Error writing to socket in Post %s", 
+                                     g_strerror (http_errno));
           return;
         }
     }
-   else
+  else if (http_status == HTTP_UNAUTHORIZED)
+    {
+      request->state = GTK_CUPS_POST_CHECK;
+      request->poll_state = GTK_CUPS_HTTP_READ;
+
+      request->attempts = 0;
+      return;
+    }
+  else
     {
       request->attempts++;
     }
 }
 
+static void
+_post_auth (GtkCupsRequest *request)
+{
+  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
+    {
+      if (request->password == NULL)
+        {
+          request->state = GTK_CUPS_POST_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     1,
+                                     "Canceled by user");
+        }
+      else
+        request->state = GTK_CUPS_POST_CHECK;
+    }
+}
+
+static void
+_get_auth (GtkCupsRequest *request)
+{
+  if (request->password_state == GTK_CUPS_PASSWORD_HAS)
+    {
+      if (request->password == NULL)
+        {
+          request->state = GTK_CUPS_GET_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     1,
+                                     "Canceled by user");
+        }
+      else
+        request->state = GTK_CUPS_GET_CHECK;
+    }
+}
+
+/* Very ugly hack: cups has a stupid synchronous password callback 
+ * that doesn't even take the request or user data parameters, so 
+ * we have to use a static variable to pass the password to it.
+ * Not threadsafe !
+ * The callback sets cups_password to NULL to signal that the 
+ * password has been used.
+ */
+static char *cups_password = NULL;
+static char *cups_username = NULL;
+
+static const char *
+passwordCB (const char *prompt)
+{
+  char *pwd = cups_password;
+  cups_password = NULL;
+
+  cupsSetUser (cups_username);
+
+  return pwd;
+}
+
 static void 
 _post_check (GtkCupsRequest *request)
 {
@@ -687,22 +976,100 @@ _post_check (GtkCupsRequest *request)
     }
   else if (http_status == HTTP_UNAUTHORIZED)
     {
-      /* TODO: callout for auth */
-      g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
-      request->state = GTK_CUPS_POST_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
+      int auth_result = -1;
+      httpFlush (request->http);
+
+      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;
+
+          return;
+        }
+
+      /* Negotiate */
+      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
+        {
+          auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+        }
+      /* Basic, BasicDigest, Digest and PeerCred */
+      else
+        {
+          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
+            {
+              cups_username = request->username;
+              cupsSetPasswordCB (passwordCB);
+
+              /* This call success for PeerCred authentication */
+              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+
+              if (auth_result != 0)
+                {
+                  /* 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;
+
+                  return;
+                }
+            }
+          else
+            {
+              cups_password = request->password;
+              cups_username = request->username;
+
+              auth_result = cupsDoAuthentication (request->http, "POST", request->resource);
+
+              if (cups_password != NULL)
+                return;
+
+              if (request->password != NULL)
+                {
+                  memset (request->password, 0, strlen (request->password));
+                  g_free (request->password);
+                  request->password = NULL;
+                }
+
+              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
+            }
+        }
+
+      if (auth_result ||
+          httpReconnect (request->http))
+        {
+          /* if the password has been used, reset password_state
+           * so that we ask for a new one next time around
+           */ 
+          if (cups_password == NULL)
+            request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+          request->state = GTK_CUPS_POST_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     0,
+                                     "Not authorized");
+          return;
+        }
       
-      gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
-      return;
+      if (request->data_io != NULL)
+        g_io_channel_seek_position (request->data_io, 0, G_SEEK_SET, NULL);
+
+      request->state = GTK_CUPS_POST_CONNECT;
+      request->poll_state = GTK_CUPS_HTTP_WRITE;
     }
   else if (http_status == HTTP_ERROR)
     {
+      int error = httpError (request->http);
 #ifdef G_OS_WIN32
-      if (request->http->error != WSAENETDOWN && 
-          request->http->error != WSAENETUNREACH)
+      if (error != WSAENETDOWN && error != WSAENETUNREACH)
 #else
-      if (request->http->error != ENETDOWN && 
-          request->http->error != ENETUNREACH)
+      if (error != ENETDOWN && error != ENETUNREACH)     
 #endif /* G_OS_WIN32 */
         {
           request->attempts++;
@@ -713,27 +1080,32 @@ _post_check (GtkCupsRequest *request)
           request->state = GTK_CUPS_POST_DONE;
           request->poll_state = GTK_CUPS_HTTP_IDLE;
      
-          gtk_cups_result_set_error (request->result, "Unknown HTTP error");
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     error, 
+                                     "Unknown HTTP error");
+
           return;
         }
     }
-/* TODO: detect ssl in configure.ac */
-#if HAVE_SSL
   else if (http_status == HTTP_UPGRADE_REQUIRED)
     {
       /* Flush any error message... */
       httpFlush (request->http);
 
+      cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
+      request->state = GTK_CUPS_POST_CONNECT;
+
       /* Reconnect... */
       httpReconnect (request->http);
 
       /* Upgrade with encryption... */
-      httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
+      httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
  
       request->attempts++;
       goto again;
     }
-#endif 
   else if (http_status != HTTP_OK)
     {
       int http_errno;
@@ -745,16 +1117,21 @@ _post_check (GtkCupsRequest *request)
       else
         {
           request->state = GTK_CUPS_POST_DONE;
-          gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
-         request->poll_state = GTK_CUPS_HTTP_IDLE;
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     http_errno, 
+                                     "HTTP Error in POST %s", 
+                                     g_strerror (http_errno));
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
  
-          httpFlush(request->http); 
+          httpFlush (request->http); 
           return;
         }
 
       request->poll_state = GTK_CUPS_HTTP_IDLE;
        
-      httpFlush(request->http); 
+      httpFlush (request->http); 
       
       request->last_status = HTTP_CONTINUE;
       httpClose (request->http);
@@ -794,7 +1171,13 @@ _post_read_response (GtkCupsRequest *request)
 
   if (ipp_status == IPP_ERROR)
     {
-      gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
+      int ipp_error = cupsLastError ();
+      gtk_cups_result_set_error (request->result,  
+                                 GTK_CUPS_ERROR_IPP,
+                                 ipp_status,
+                                 ipp_error,
+                                 "%s",
+                                 ippErrorString (ipp_error));
       
       ippDelete (request->result->ipp_response);
       request->result->ipp_response = NULL;
@@ -819,38 +1202,55 @@ _get_send (GtkCupsRequest *request)
 
   if (request->data_io == NULL)
     {
-      gtk_cups_result_set_error (request->result, "Get requires an open io channel");
+      gtk_cups_result_set_error (request->result,
+                                 GTK_CUPS_ERROR_IO,
+                                 G_IO_STATUS_ERROR,
+                                 G_IO_CHANNEL_ERROR_FAILED, 
+                                 "Get requires an open io channel");
+
       request->state = GTK_CUPS_GET_DONE;
       request->poll_state = GTK_CUPS_HTTP_IDLE;
 
       return;
     }
 
-  httpClearFields(request->http);
+  httpClearFields (request->http);
+#ifdef HAVE_HTTPGETAUTHSTRING
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, httpGetAuthString (request->http));
+#else
 #ifdef HAVE_HTTP_AUTHSTRING
-  httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+  httpSetField (request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+#endif
 #endif
 
-  if (httpGet(request->http, request->resource))
+  if (httpGet (request->http, request->resource))
     {
-      if (httpReconnect(request->http))
+      if (httpReconnect (request->http))
         {
           request->state = GTK_CUPS_GET_DONE;
           request->poll_state = GTK_CUPS_HTTP_IDLE;
-         
-          gtk_cups_result_set_error (request->result, "Failed Get");
+        
+          /* TODO: should add a status or error code for failed GET */ 
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_GENERAL,
+                                     0,
+                                     0,
+                                     "Failed Get");
         }
 
       request->attempts++;
       return;    
     }
+
+  if (httpCheck (request->http))
+    request->last_status = httpUpdate (request->http);
         
   request->attempts = 0;
 
   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 
@@ -871,31 +1271,109 @@ _get_check (GtkCupsRequest *request)
     }
   else if (http_status == HTTP_UNAUTHORIZED)
     {
-      /* TODO: callout for auth */
-      g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
-      request->state = GTK_CUPS_GET_DONE;
-      request->poll_state = GTK_CUPS_HTTP_IDLE;
-      gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
-      return;
+      int auth_result = -1;
+      httpFlush (request->http);
+
+      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;
+
+          return;
+        }
+
+      /* Negotiate */
+      if (strncmp (httpGetField (request->http, HTTP_FIELD_WWW_AUTHENTICATE), "Negotiate", 9) == 0)
+        {
+          auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+        }
+      /* Basic, BasicDigest, Digest and PeerCred */
+      else
+        {
+          if (request->password_state == GTK_CUPS_PASSWORD_NONE)
+            {
+              cups_username = request->username;
+              cupsSetPasswordCB (passwordCB);
+
+              /* This call success for PeerCred authentication */
+              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+
+              if (auth_result != 0)
+                {
+                  /* 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;
+
+                  return;
+                }
+            }
+          else
+            {
+              cups_password = request->password;
+              cups_username = request->username;
+
+              auth_result = cupsDoAuthentication (request->http, "GET", request->resource);
+
+              if (cups_password != NULL)
+                return;
+
+              if (request->password != NULL)
+                {
+                  memset (request->password, 0, strlen (request->password));
+                  g_free (request->password);
+                  request->password = NULL;
+                }
+
+              request->password_state = GTK_CUPS_PASSWORD_APPLIED;
+            }
+        }
+
+      if (auth_result ||
+          httpReconnect (request->http))
+        {
+          /* if the password has been used, reset password_state
+           * so that we ask for a new one next time around
+           */
+          if (cups_password == NULL)
+            request->password_state = GTK_CUPS_PASSWORD_NONE;
+
+          request->state = GTK_CUPS_GET_DONE;
+          request->poll_state = GTK_CUPS_HTTP_IDLE;
+          gtk_cups_result_set_error (request->result, 
+                                     GTK_CUPS_ERROR_AUTH,
+                                     0,
+                                     0,
+                                     "Not authorized");
+          return;
+        }
+
+      request->state = GTK_CUPS_GET_CONNECT;
+      request->last_status = HTTP_CONTINUE;
+
+     return;
     }
-/* TODO: detect ssl in configure.ac */
-#if HAVE_SSL
   else if (http_status == HTTP_UPGRADE_REQUIRED)
     {
       /* Flush any error message... */
       httpFlush (request->http);
 
+      cupsSetEncryption (HTTP_ENCRYPT_REQUIRED);
+      request->state = GTK_CUPS_GET_CONNECT;
+
       /* Reconnect... */
       httpReconnect (request->http);
 
       /* Upgrade with encryption... */
-      httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
+      httpEncryption (request->http, HTTP_ENCRYPT_REQUIRED);
  
       request->attempts++;
       goto again;
     }
-#endif 
   else if (http_status != HTTP_OK)
     {
       int http_errno;
@@ -907,9 +1385,14 @@ _get_check (GtkCupsRequest *request)
       else
         {
           request->state = GTK_CUPS_GET_DONE;
-          gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
+          gtk_cups_result_set_error (request->result,
+                                     GTK_CUPS_ERROR_HTTP,
+                                     http_status,
+                                     http_errno, 
+                                     "HTTP Error in GET %s", 
+                                     g_strerror (http_errno));
           request->poll_state = GTK_CUPS_HTTP_IDLE;
-          httpFlush(request->http);
+          httpFlush (request->http);
 
           return;
         }
@@ -954,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 */
+  bytes = httpRead2 (request->http, buffer, sizeof (buffer));
+  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, 
@@ -980,12 +1452,25 @@ _get_read_data (GtkCupsRequest *request)
 
   if (io_status == G_IO_STATUS_ERROR)
     {
-      request->state = GTK_CUPS_POST_DONE;
+      request->state = GTK_CUPS_GET_DONE;
       request->poll_state = GTK_CUPS_HTTP_IDLE;
     
-      gtk_cups_result_set_error (request->result, error->message);
+      gtk_cups_result_set_error (request->result,
+                                 GTK_CUPS_ERROR_IO,
+                                 io_status,
+                                 error->code, 
+                                 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
@@ -1006,9 +1491,166 @@ gtk_cups_result_get_response (GtkCupsResult *result)
   return result->ipp_response;
 }
 
+GtkCupsErrorType
+gtk_cups_result_get_error_type (GtkCupsResult *result)
+{
+  return result->error_type;
+}
+
+int
+gtk_cups_result_get_error_status (GtkCupsResult *result)
+{
+  return result->error_status;
+}
+
+int
+gtk_cups_result_get_error_code (GtkCupsResult *result)
+{
+  return result->error_code;
+}
+
 const char *
 gtk_cups_result_get_error_string (GtkCupsResult *result)
 {
   return result->error_msg; 
 }
 
+/* This function allocates new instance of GtkCupsConnectionTest() and creates
+ * a socket for communication with a CUPS server 'server'.
+ */
+GtkCupsConnectionTest *
+gtk_cups_connection_test_new (const char *server)
+{
+  GtkCupsConnectionTest *result = NULL;
+  gchar                 *port_str = NULL;
+
+  result = g_new (GtkCupsConnectionTest, 1);
+
+  port_str = g_strdup_printf ("%d", ippPort ());
+
+  if (server != NULL)
+    result->addrlist = httpAddrGetList (server, AF_UNSPEC, port_str);
+  else
+    result->addrlist = httpAddrGetList (cupsServer (), AF_UNSPEC, port_str);
+
+  g_free (port_str);
+
+  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);
+
+  return result;
+}
+
+
+/* A non-blocking test whether it is possible to connect to a CUPS server specified
+ * inside of GtkCupsConnectionTest structure.
+ *  - you need to check it more then once.
+ * The connection is closed after a successful connection.
+ */
+GtkCupsConnectionState 
+gtk_cups_connection_test_get_state (GtkCupsConnectionTest *test)
+{
+  GtkCupsConnectionState result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+  http_addrlist_t       *iter;
+  gint                   error_code;
+  gint                   flags;
+  gint                   code;
+
+  if (test == NULL)
+    return GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+
+  if (test->at_init == GTK_CUPS_CONNECTION_AVAILABLE)
+    {
+      test->at_init = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+      return GTK_CUPS_CONNECTION_AVAILABLE;
+    }
+  else
+    {
+      if (test->socket == -1)
+        {
+          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,
+                                     SOCK_STREAM,
+                                     0);
+
+              if (test->socket >= 0)
+                {
+                  flags = fcntl (test->socket, F_GETFL);
+
+                  if (flags != -1)
+                    flags |= O_NONBLOCK;
+
+                  fcntl (test->socket, F_SETFL, flags);
+              
+                  test->current_addr = iter;
+              
+                  break;
+                }
+               iter = iter->next;
+            }
+        }
+
+      if (test->socket >= 0)
+        {
+          code = connect (test->socket,
+                          &test->current_addr->addr.addr,
+                          httpAddrLength (&test->current_addr->addr));
+
+          error_code = errno;
+
+          if (code == 0 || error_code == EISCONN)
+            {
+              close (test->socket);
+              test->socket = -1;
+              test->current_addr = NULL;
+              result = GTK_CUPS_CONNECTION_AVAILABLE;
+            }
+          else
+            {
+              if (error_code == EALREADY || error_code == EINPROGRESS)
+                result = GTK_CUPS_CONNECTION_IN_PROGRESS;
+              else
+                {
+                  close (test->socket);
+                  test->socket = -1;
+                  test->last_wrong_addr = test->current_addr;
+                  result = GTK_CUPS_CONNECTION_NOT_AVAILABLE;
+                }
+            }
+         }
+
+      return result;
+    }
+}
+
+/* This function frees memory used by the GtkCupsConnectionTest structure.
+ */
+void 
+gtk_cups_connection_test_free (GtkCupsConnectionTest *test)
+{
+  if (test == NULL)
+    return;
+
+  test->current_addr = NULL;
+  test->last_wrong_addr = NULL;
+  httpAddrFreeList (test->addrlist);
+  if (test->socket != -1)
+    {
+      close (test->socket);
+      test->socket = -1;
+    }
+  g_free (test);
+}