[gtk-vnc] Introduce a "vnc-error" signal



commit 73bd11b94dfbe6fc4526beea7185f089538aa824
Author: Daniel P. Berrange <berrange redhat com>
Date:   Mon Aug 15 13:10:59 2016 +0100

    Introduce a "vnc-error" signal
    
    Provide the app uing GTK-VNC with information about what caused
    the error via a new "vnc-error" signal.
    
    Resolves: bz#762223, #768237
    Signed-off-by: Daniel P. Berrange <berrange redhat com>

 examples/gvncviewer.c |    8 +
 src/vncconnection.c   |  440 ++++++++++++++++++++++++++++---------------------
 src/vncconnection.h   |    3 +-
 src/vncdisplay.c      |   25 +++
 4 files changed, 283 insertions(+), 193 deletions(-)
---
diff --git a/examples/gvncviewer.c b/examples/gvncviewer.c
index 8a96709..2ec8e0e 100644
--- a/examples/gvncviewer.c
+++ b/examples/gvncviewer.c
@@ -239,6 +239,12 @@ static void vnc_connected(GtkWidget *vncdisplay G_GNUC_UNUSED)
     connected = 1;
 }
 
+static void vnc_error(GtkWidget *vncdisplay G_GNUC_UNUSED,
+                      const gchar *message)
+{
+    fprintf(stderr, "Error: %s\n", message);
+}
+
 static void vnc_initialized(GtkWidget *vncdisplay, GtkWidget *window)
 {
     printf("Connection initialized\n");
@@ -829,6 +835,8 @@ int main(int argc, char **argv)
                      G_CALLBACK(vnc_initialized), window);
     g_signal_connect(vnc, "vnc-disconnected",
                      G_CALLBACK(vnc_disconnected), NULL);
+    g_signal_connect(vnc, "vnc-error",
+                     G_CALLBACK(vnc_error), NULL);
     g_signal_connect(vnc, "vnc-auth-credential",
                      G_CALLBACK(vnc_credential), NULL);
     g_signal_connect(vnc, "vnc-auth-failure",
diff --git a/src/vncconnection.c b/src/vncconnection.c
index 3d6e77e..52dc6cb 100644
--- a/src/vncconnection.c
+++ b/src/vncconnection.c
@@ -152,6 +152,9 @@ typedef void vnc_connection_tight_compute_predicted_func(VncConnection *conn, gu
 
 typedef void vnc_connection_tight_sum_pixel_func(VncConnection *conn, guint8 *, guint8 *);
 static void vnc_connection_close(VncConnection *conn);
+static void vnc_connection_set_error(VncConnection *conn,
+                                     const char *format,
+                                     ...) G_GNUC_PRINTF(2, 3);
 
 /*
  * A special GSource impl which allows us to wait on a certain
@@ -186,7 +189,8 @@ struct _VncConnectionPrivate
     char *host;
     char *port;
     VncPixelFormat fmt;
-    gboolean has_error;
+    char *error;
+    gboolean coroutine_stop;
     int width;
     int height;
     char *name;
@@ -296,6 +300,7 @@ enum {
     VNC_CONNECTED,
     VNC_INITIALIZED,
     VNC_DISCONNECTED,
+    VNC_ERROR,
 
     VNC_LAST_SIGNAL,
 };
@@ -308,6 +313,7 @@ static guint signals[VNC_LAST_SIGNAL] = { 0, 0, 0, 0,
 #define nibhi(a) (((a) >> 4) & 0x0F)
 #define niblo(a) ((a) & 0x0F)
 
+
 /* Main loop helper functions */
 static gboolean g_io_wait_helper(GSocket *sock G_GNUC_UNUSED,
                                  GIOCondition cond,
@@ -503,6 +509,7 @@ struct signal_data
         unsigned int authUnsupported;
         GValueArray *authCred;
         GValueArray *authTypes;
+        const char *message;
     } params;
 };
 
@@ -615,6 +622,13 @@ static gboolean do_vnc_connection_emit_main_context(gpointer opaque)
                       0);
         break;
 
+    case VNC_ERROR:
+        g_signal_emit(G_OBJECT(data->conn),
+                      signals[data->signum],
+                      0,
+                      data->params.message);
+        break;
+
     default:
         g_warn_if_reached();
     }
@@ -644,6 +658,27 @@ static void vnc_connection_emit_main_context(VncConnection *conn,
 }
 
 
+static G_GNUC_PRINTF(2, 3) void vnc_connection_set_error(VncConnection *conn,
+                                                         const char *format,
+                                                         ...)
+{
+    va_list args;
+    struct signal_data s;
+
+    va_start(args, format);
+
+    g_free(conn->priv->error);
+    conn->priv->error = g_strdup_vprintf(format, args);
+    va_end(args);
+    conn->priv->coroutine_stop = TRUE;
+
+    VNC_DEBUG("Error: %s", conn->priv->error);
+
+    s.params.message = conn->priv->error;
+    vnc_connection_emit_main_context(conn, VNC_ERROR, &s);
+}
+
+
 static gboolean vnc_connection_use_compression(VncConnection *conn)
 {
     VncConnectionPrivate *priv = conn->priv;
@@ -710,7 +745,7 @@ static int vnc_connection_read_wire(VncConnection *conn, void *data, size_t len)
 
  reread:
 
-    if (priv->has_error) return -EINVAL;
+    if (priv->coroutine_stop) return -EINVAL;
 
     if (priv->tls_session) {
         ret = gnutls_read(priv->tls_session, data, len);
@@ -742,7 +777,6 @@ static int vnc_connection_read_wire(VncConnection *conn, void *data, size_t len)
             if (priv->wait_interruptable) {
                 if (!g_io_wait_interruptable(&priv->wait,
                                              priv->sock, G_IO_IN)) {
-                    //VNC_DEBUG("Read blocking interrupted %d", priv->has_error);
                     return -EAGAIN;
                 }
             } else {
@@ -751,13 +785,13 @@ static int vnc_connection_read_wire(VncConnection *conn, void *data, size_t len)
             blocking = FALSE;
             goto reread;
         } else {
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "%s", "Unable to read from server");
             return -errno;
         }
     }
     if (ret == 0) {
         VNC_DEBUG("Closing the connection: vnc_connection_read() - ret=0");
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "%s", "Server closed the connection");
         return -EPIPE;
     }
     //VNC_DEBUG("Read wire %p %d -> %d", data, len, ret);
@@ -796,9 +830,9 @@ static int vnc_connection_read_sasl(VncConnection *conn)
                           &priv->saslDecoded, &priv->saslDecodedLength);
         g_free(encoded);
         if (err != SASL_OK) {
-            VNC_DEBUG("Failed to decode SASL data %s",
-                      sasl_errstring(err, NULL, NULL));
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn,
+                                     "Failed to decode SASL data %s",
+                                     sasl_errstring(err, NULL, NULL));
             return -EINVAL;
         }
         priv->saslDecodedOffset = 0;
@@ -816,7 +850,7 @@ static int vnc_connection_read_sasl(VncConnection *conn)
         priv->saslDecodedLength = priv->saslDecodedOffset = 0;
         priv->saslDecoded = NULL;
     }
-    //VNC_DEBUG("Done read write %d - %d", want, priv->has_error);
+
     return want;
 }
 #endif
@@ -842,7 +876,6 @@ static int vnc_connection_read_buf(VncConnection *conn)
 #ifdef HAVE_SASL
     VncConnectionPrivate *priv = conn->priv;
 
-    //VNC_DEBUG("Start read %d", priv->has_error);
     if (priv->saslconn)
         return vnc_connection_read_sasl(conn);
     else
@@ -861,7 +894,7 @@ static int vnc_connection_read(VncConnection *conn, void *data, size_t len)
     char *ptr = data;
     size_t offset = 0;
 
-    if (priv->has_error) return -EINVAL;
+    if (priv->coroutine_stop) return -EINVAL;
 
     while (offset < len) {
         size_t tmp;
@@ -871,8 +904,7 @@ static int vnc_connection_read(VncConnection *conn, void *data, size_t len)
         if (vnc_connection_use_compression(conn)) {
             int ret = vnc_connection_zread(conn, ptr + offset, len);
             if (ret == -1) {
-                VNC_DEBUG("Closing the connection: vnc_connection_read() - zread() failed");
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "%s", "Failure decompressing data");
                 return -errno;
             }
             offset += ret;
@@ -913,7 +945,7 @@ static void vnc_connection_flush_wire(VncConnection *conn,
         int ret;
         gboolean blocking = FALSE;
 
-        if (priv->has_error) return;
+        if (priv->coroutine_stop) return;
 
         if (priv->tls_session) {
             ret = gnutls_write(priv->tls_session,
@@ -943,14 +975,12 @@ static void vnc_connection_flush_wire(VncConnection *conn,
             if (blocking) {
                 g_io_wait(priv->sock, G_IO_OUT);
             } else {
-                VNC_DEBUG("Closing the connection: vnc_connection_flush %d", errno);
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "%s", "Failed to flush data");
                 return;
             }
         }
         if (ret == 0) {
-            VNC_DEBUG("Closing the connection: vnc_connection_flush");
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "%s", "Failed to any flush data");
             return;
         }
         offset += ret;
@@ -975,9 +1005,8 @@ static void vnc_connection_flush_sasl(VncConnection *conn)
                       priv->write_offset,
                       &output, &outputlen);
     if (err != SASL_OK) {
-        VNC_DEBUG("Failed to encode SASL data %s",
-                  sasl_errstring(err, NULL, NULL));
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Failed to encode SASL data %s",
+                                 sasl_errstring(err, NULL, NULL));
         return;
     }
     //VNC_DEBUG("Flush SASL %d: %p %d", priv->write_offset, output, outputlen);
@@ -1006,7 +1035,6 @@ static void vnc_connection_flush(VncConnection *conn)
 {
     VncConnectionPrivate *priv = conn->priv;
 
-    //VNC_DEBUG("Start flush write %d", priv->has_error);
 #ifdef HAVE_SASL
     if (priv->saslconn)
         vnc_connection_flush_sasl(conn);
@@ -1356,70 +1384,78 @@ static int vnc_connection_validate_certificate(VncConnection *conn)
 
     VNC_DEBUG("Validating");
     if ((ret = gnutls_certificate_verify_peers2 (priv->tls_session, &status)) < 0) {
-        VNC_DEBUG("Verify failed %s", gnutls_strerror(ret));
+        vnc_connection_set_error(conn, "Failed to verify peer %s", gnutls_strerror(ret));
         return FALSE;
     }
 
     if ((now = time(NULL)) == ((time_t)-1)) {
+        vnc_connection_set_error(conn, "%s", "Failed to get current time");
         return FALSE;
     }
 
     if (status != 0) {
-        if (status & GNUTLS_CERT_INVALID)
-            VNC_DEBUG ("The certificate is not trusted.");
-
-        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
-            VNC_DEBUG ("The certificate hasn't got a known issuer.");
-
-        if (status & GNUTLS_CERT_REVOKED)
-            VNC_DEBUG ("The certificate has been revoked.");
-
-        if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
-            VNC_DEBUG ("The certificate uses an insecure algorithm");
-
+        if (status & GNUTLS_CERT_INVALID) {
+            vnc_connection_set_error(conn, "%s", "The certificate is not trusted.");
+        } else if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+            vnc_connection_set_error(conn, "%s", "The certificate hasn't got a known issuer.");
+        } else if (status & GNUTLS_CERT_REVOKED) {
+            vnc_connection_set_error(conn, "%s", "The certificate has been revoked.");
+        } else if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+            vnc_connection_set_error(conn, "%s", "The certificate uses an insecure algorithm");
+        } else {
+            vnc_connection_set_error(conn, "%s", "The certificate is not valid");
+        }
         return FALSE;
     } else {
         VNC_DEBUG("Certificate is valid.");
     }
 
-    if (gnutls_certificate_type_get(priv->tls_session) != GNUTLS_CRT_X509)
+    if (gnutls_certificate_type_get(priv->tls_session) != GNUTLS_CRT_X509) {
+        vnc_connection_set_error(conn, "%s", "Only x509 certificates are supported");
         return FALSE;
+    }
 
-    if (!(certs = gnutls_certificate_get_peers(priv->tls_session, &nCerts)))
+    if (!(certs = gnutls_certificate_get_peers(priv->tls_session, &nCerts))) {
+        vnc_connection_set_error(conn, "%s", "Unable to query certificate peers");
         return FALSE;
+    }
 
     for (i = 0 ; i < nCerts ; i++) {
         gnutls_x509_crt_t cert;
         VNC_DEBUG ("Checking chain %d", i);
-        if (gnutls_x509_crt_init (&cert) < 0)
+        if (gnutls_x509_crt_init (&cert) < 0) {
+            vnc_connection_set_error(conn, "%s", "Unable to initialize cert");
             return FALSE;
+        }
 
         if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
             gnutls_x509_crt_deinit (cert);
+            vnc_connection_set_error(conn, "%s", "Unable to import certificate");
             return FALSE;
         }
 
         if (gnutls_x509_crt_get_expiration_time (cert) < now) {
-            VNC_DEBUG("The certificate has expired");
+            vnc_connection_set_error(conn, "%s", "The certificate has expired");
             gnutls_x509_crt_deinit (cert);
             return FALSE;
         }
 
         if (gnutls_x509_crt_get_activation_time (cert) > now) {
-            VNC_DEBUG("The certificate is not yet activated");
+            vnc_connection_set_error(conn, "%s", "The certificate is not yet activated");
             gnutls_x509_crt_deinit (cert);
             return FALSE;
         }
 
         if (i == 0) {
             if (!priv->host) {
-                VNC_DEBUG ("No hostname provided for certificate verification");
+                vnc_connection_set_error(conn, "%s", "No hostname provided for certificate verification");
                 gnutls_x509_crt_deinit (cert);
                 return FALSE;
             }
             if (!gnutls_x509_crt_check_hostname (cert, priv->host)) {
-                VNC_DEBUG ("The certificate's owner does not match hostname '%s'",
-                           priv->host);
+                vnc_connection_set_error(conn,
+                                         "The certificate's owner does not match hostname '%s'",
+                                         priv->host);
                 gnutls_x509_crt_deinit (cert);
                 return FALSE;
             }
@@ -1485,7 +1521,7 @@ gboolean vnc_connection_has_error(VncConnection *conn)
 {
     VncConnectionPrivate *priv = conn->priv;
 
-    return priv->has_error;
+    return priv->coroutine_stop;
 }
 
 /**
@@ -2685,7 +2721,7 @@ static void vnc_connection_tight_update_jpeg(VncConnection *conn, guint16 x, gui
     GdkPixbuf *p;
 
     if (!gdk_pixbuf_loader_write(loader, data, length, NULL)) {
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "%s", "Unable to decode jpeg data");
         return;
     }
 
@@ -2776,8 +2812,8 @@ static void vnc_connection_tight_update(VncConnection *conn,
             vnc_connection_tight_update_gradient(conn, x, y, width, height);
             break;
         default: /* error */
-            VNC_DEBUG("Closing the connection: vnc_connection_tight_update() - filter_id unknown");
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "Unexpected tight filter id %d",
+                                     filter_id);
             break;
         }
 
@@ -2808,9 +2844,8 @@ static void vnc_connection_tight_update(VncConnection *conn,
                                          jpeg_data, length);
         g_free(jpeg_data);
     } else {
-        /* error */
-        VNC_DEBUG("Closing the connection: vnc_connection_tight_update() - ccontrol unknown");
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Unexpected tight ccontrol %d",
+                                 ccontrol);
     }
 }
 
@@ -2819,7 +2854,7 @@ static void vnc_connection_update(VncConnection *conn, int x, int y, int width,
     VncConnectionPrivate *priv = conn->priv;
     struct signal_data sigdata;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     VNC_DEBUG("Notify update area (%dx%d) at location %d,%d", width, height, x, y);
@@ -2837,7 +2872,7 @@ static void vnc_connection_bell(VncConnection *conn)
     VncConnectionPrivate *priv = conn->priv;
     struct signal_data sigdata;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     VNC_DEBUG("Server beep");
@@ -2853,7 +2888,7 @@ static void vnc_connection_server_cut_text(VncConnection *conn,
     struct signal_data sigdata;
     GString *text;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     text = g_string_new_len ((const gchar *)data, len);
@@ -2869,7 +2904,7 @@ static void vnc_connection_resize(VncConnection *conn, int width, int height)
     VncConnectionPrivate *priv = conn->priv;
     struct signal_data sigdata;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     priv->width = width;
@@ -2885,7 +2920,7 @@ static void vnc_connection_pixel_format(VncConnection *conn)
     VncConnectionPrivate *priv = conn->priv;
     struct signal_data sigdata;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     sigdata.params.pixelFormat = &priv->fmt;
@@ -2903,7 +2938,7 @@ static void vnc_connection_pointer_type_change(VncConnection *conn, gboolean abs
         return;
     priv->absPointer = absPointer;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     sigdata.params.absPointer = absPointer;
@@ -2954,7 +2989,7 @@ static void vnc_connection_rich_cursor(VncConnection *conn, int x, int y, int wi
         priv->cursor = vnc_cursor_new(pixbuf, x, y, width, height);
     }
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     sigdata.params.cursor = priv->cursor;
@@ -3009,7 +3044,7 @@ static void vnc_connection_xcursor(VncConnection *conn, int x, int y, int width,
         priv->cursor = vnc_cursor_new(pixbuf, x, y, width, height);
     }
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return;
 
     sigdata.params.cursor = priv->cursor;
@@ -3033,9 +3068,9 @@ static gboolean vnc_connection_validate_boundary(VncConnection *conn,
     VncConnectionPrivate *priv = conn->priv;
 
     if ((x + width) > priv->width || (y + height) > priv->height) {
-        VNC_DEBUG("Framebuffer update %dx%d at %d,%d outside boundary %dx%d",
-                  width, height, x, y, priv->width, priv->height);
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Framebuffer update %dx%d at %d,%d "
+                                 "outside boundary %dx%d",
+                                 width, height, x, y, priv->width, priv->height);
     }
 
     return !vnc_connection_has_error(conn);
@@ -3130,8 +3165,7 @@ static gboolean vnc_connection_framebuffer_update(VncConnection *conn, gint32 et
             vnc_connection_audio_enable(conn);
         break;
     default:
-        VNC_DEBUG("Received an unknown encoding type: %d", etype);
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Received an unknown encoding type: %d", etype);
         break;
     }
 
@@ -3306,18 +3340,12 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
         vnc_connection_read(conn, pad, 3);
         n_text = vnc_connection_read_u32(conn);
         if (n_text > (32 << 20)) {
-            VNC_DEBUG("Closing the connection: vnc_connection_server_message() - cutText > allowed");
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "Cut text length %u longer than permitted %u",
+                                     n_text, (32 << 20));
             break;
         }
 
         data = g_new(char, n_text + 1);
-        if (data == NULL) {
-            VNC_DEBUG("Closing the connection: vnc_connection_server_message() - cutText - !data");
-            priv->has_error = TRUE;
-            break;
-        }
-
         vnc_connection_read(conn, data, n_text);
         data[n_text] = 0;
 
@@ -3329,7 +3357,7 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
 
         n_type = vnc_connection_read_u8(conn);
 
-        if (priv->has_error)
+        if (priv->coroutine_stop)
             break;
 
         switch (n_type) {
@@ -3342,25 +3370,22 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
             case VNC_CONNECTION_SERVER_MESSAGE_QEMU_AUDIO_DATA:
                 n_length = vnc_connection_read_u32(conn);
                 if (n_length > (1024*1024)) {
-                    VNC_DEBUG("Received audio message that is too large %u", n_length);
-                    priv->has_error = TRUE;
+                    vnc_connection_set_error(conn,
+                                             "Audio sample length %d longer than permitted %u",
+                                             n_length, 1024 * 1024);
                     break;
                 }
-                if (priv->has_error)
+                if (priv->coroutine_stop)
                     break;
 
                 if (!priv->audio) {
-                    VNC_DEBUG("No audio playback available");
-                    priv->has_error = TRUE;
+                    vnc_connection_set_error(conn, "%s",
+                                             "No audio playback sink configured");
                     break;
                 }
                 if (priv->audio_sample &&
                     ((priv->audio_sample->capacity - priv->audio_sample->length) < n_length)) {
                     g_source_remove(priv->audio_timer);
-                    fprintf(stderr, "%u %u %u\n",
-                            priv->audio_sample->capacity,
-                            priv->audio_sample->length,
-                            n_length);
                     vnc_connection_audio_action(conn, VNC_AUDIO_PLAYBACK_DATA);
                     vnc_audio_sample_free(priv->audio_sample);
                     priv->audio_sample = NULL;
@@ -3381,7 +3406,7 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
                 if (priv->audio)
                     vnc_connection_audio_action(conn, VNC_AUDIO_PLAYBACK_START);
                 else
-                    priv->has_error = TRUE;
+                    vnc_connection_set_error(conn, "%s", "No audio sink configured");
                 break;
             case VNC_CONNECTION_SERVER_MESSAGE_QEMU_AUDIO_STOP:
                 if (priv->audio) {
@@ -3393,23 +3418,20 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
                     }
                     vnc_connection_audio_action(conn, VNC_AUDIO_PLAYBACK_STOP);
                 } else {
-                    priv->has_error = TRUE;
+                    vnc_connection_set_error(conn, "%s", "No audio sink configured");
                 }
                 break;
             default:
-                VNC_DEBUG("Received unknown QEMU audio message: %u", (int)n_subtype);
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "Received unknown QEMU audio message: %u", (int)n_subtype);
                 break;
             }
         }       break;
         default:
-            VNC_DEBUG("Received an unknown QEMU message: %u", n_type);
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "Received an unknown QEMU message: %u", n_type);
         }
     } break;
     default:
-        VNC_DEBUG("Received an unknown message: %u", msg);
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Received an unknown message: %u", msg);
         break;
     }
 
@@ -3421,7 +3443,7 @@ static gboolean vnc_connection_has_credentials(gpointer data)
     VncConnection *conn = data;
     VncConnectionPrivate *priv = conn->priv;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return TRUE;
     if (priv->want_cred_username && !priv->cred_username)
         return FALSE;
@@ -3444,7 +3466,7 @@ static gboolean vnc_connection_gather_credentials(VncConnection *conn)
 {
     VncConnectionPrivate *priv = conn->priv;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     if (!vnc_connection_has_credentials(conn)) {
@@ -3479,7 +3501,7 @@ static gboolean vnc_connection_gather_credentials(VncConnection *conn)
 
         g_value_array_free(authCred);
 
-        if (priv->has_error)
+        if (priv->coroutine_stop)
             return FALSE;
         VNC_DEBUG("Waiting for missing credentials");
         g_condition_wait(vnc_connection_has_credentials, conn);
@@ -3510,16 +3532,18 @@ static gboolean vnc_connection_check_auth_result(VncConnection *conn)
         vnc_connection_read(conn, reason, len);
         reason[len] = '\0';
         VNC_DEBUG("Fail %s", reason);
-        if (!priv->has_error) {
+        if (!priv->coroutine_stop) {
             struct signal_data sigdata;
             sigdata.params.authReason = reason;
+            vnc_connection_set_error(conn, "%s", reason);
             vnc_connection_emit_main_context(conn, VNC_AUTH_FAILURE, &sigdata);
         }
     } else {
         VNC_DEBUG("Fail auth no result");
-        if (!priv->has_error) {
+        if (!priv->coroutine_stop) {
             struct signal_data sigdata;
             sigdata.params.authReason = "Unknown authentication failure";
+            vnc_connection_set_error(conn, "%s", "Unknown authentication failure");
             vnc_connection_emit_main_context(conn, VNC_AUTH_FAILURE, &sigdata);
         }
     }
@@ -3935,32 +3959,37 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
     err = sasl_client_init(NULL);
     VNC_DEBUG("Client initialize SASL authentication %d", err);
     if (err != SASL_OK) {
-        VNC_DEBUG("failed to initialize SASL library: %d (%s)",
-                  err, sasl_errstring(err, NULL, NULL));
+        vnc_connection_set_error(conn,
+                                 "failed to initialize SASL library: %d (%s)",
+                                 err, sasl_errstring(err, NULL, NULL));
         goto error;
     }
 
     /* Get local address in form  IPADDR:PORT */
     addr = g_socket_get_local_address(priv->sock, NULL);
     if (!addr) {
-        VNC_DEBUG("failed to get local address");
+        vnc_connection_set_error(conn, "%s", "failed to get local address");
         goto error;
     }
     if ((g_socket_address_get_family(addr) == G_SOCKET_FAMILY_IPV4 ||
          g_socket_address_get_family(addr) == G_SOCKET_FAMILY_IPV6) &&
-        (localAddr = vnc_connection_addr_to_string(addr)) == NULL)
+        (localAddr = vnc_connection_addr_to_string(addr)) == NULL) {
+        vnc_connection_set_error(conn, "%s", "Unable to format address as string");
         goto error;
+    }
 
     /* Get remote address in form  IPADDR:PORT */
     addr = g_socket_get_remote_address(priv->sock, NULL);
     if (!addr) {
-        VNC_DEBUG("failed to get peer address");
+        vnc_connection_set_error(conn, "%s", "failed to get peer address");
         goto error;
     }
     if ((g_socket_address_get_family(addr) == G_SOCKET_FAMILY_IPV4 ||
          g_socket_address_get_family(addr) == G_SOCKET_FAMILY_IPV6) &&
-        (remoteAddr = vnc_connection_addr_to_string(addr)) == NULL)
+        (remoteAddr = vnc_connection_addr_to_string(addr)) == NULL) {
+        vnc_connection_set_error(conn, "%s", "Unable to format address as string");
         goto error;
+    }
 
     VNC_DEBUG("Client SASL new host:'%s' local:'%s' remote:'%s'", priv->host, localAddr, remoteAddr);
 
@@ -3976,8 +4005,9 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
     g_free(remoteAddr);
 
     if (err != SASL_OK) {
-        VNC_DEBUG("Failed to create SASL client context: %d (%s)",
-                  err, sasl_errstring(err, NULL, NULL));
+        vnc_connection_set_error(conn,
+                                 "Failed to create SASL client context: %d (%s)",
+                                 err, sasl_errstring(err, NULL, NULL));
         goto error;
     }
 
@@ -3987,7 +4017,8 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
 
         cipher = gnutls_cipher_get(priv->tls_session);
         if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
-            VNC_DEBUG("%s", "invalid cipher size for TLS session");
+            vnc_connection_set_error(conn, "%s",
+                                     "invalid cipher size for TLS session");
             goto error;
         }
         ssf *= 8; /* key size is bytes, sasl wants bits */
@@ -3995,8 +4026,9 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
         VNC_DEBUG("Setting external SSF %d", ssf);
         err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
         if (err != SASL_OK) {
-            VNC_DEBUG("cannot set external SSF %d (%s)",
-                      err, sasl_errstring(err, NULL, NULL));
+            vnc_connection_set_error(conn,
+                                     "cannot set external SSF %d (%s)",
+                                     err, sasl_errstring(err, NULL, NULL));
             goto error;
         }
     }
@@ -4012,41 +4044,32 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
 
     err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
     if (err != SASL_OK) {
-        VNC_DEBUG("cannot set security props %d (%s)",
-                  err, sasl_errstring(err, NULL, NULL));
+        vnc_connection_set_error(conn,
+                                 "cannot set security props %d (%s)",
+                                 err, sasl_errstring(err, NULL, NULL));
         goto error;
     }
 
     /* Get the supported mechanisms from the server */
     mechlistlen = vnc_connection_read_u32(conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         goto error;
     if (mechlistlen > SASL_MAX_MECHLIST_LEN) {
-        VNC_DEBUG("mechlistlen %d too long", mechlistlen);
+        vnc_connection_set_error(conn,
+                                 "mechlistlen %d too long",
+                                 mechlistlen);
         goto error;
     }
 
     mechlist = g_malloc(mechlistlen+1);
     vnc_connection_read(conn, mechlist, mechlistlen);
     mechlist[mechlistlen] = '\0';
-    if (priv->has_error) {
+    if (priv->coroutine_stop) {
         g_free(mechlist);
         mechlist = NULL;
         goto error;
     }
 
-#if 0
-    if (wantmech) {
-        if (strstr(mechlist, wantmech) == NULL) {
-            VNC_DEBUG("SASL mechanism %s not supported by server",
-                      wantmech);
-            VIR_FREE(iret.mechlist);
-            goto error;
-        }
-        mechlist = wantmech;
-    }
-#endif
-
  restart:
     /* Start the auth negotiation on the client end first */
     VNC_DEBUG("Client start negotiation mechlist '%s'", mechlist);
@@ -4057,8 +4080,9 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
                             &clientoutlen,
                             &mechname);
     if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
-        VNC_DEBUG("Failed to start SASL negotiation: %d (%s)",
-                  err, sasl_errdetail(saslconn));
+        vnc_connection_set_error(conn,
+                                 "Failed to start SASL negotiation: %d (%s)",
+                                 err, sasl_errdetail(saslconn));
         g_free(mechlist);
         mechlist = NULL;
         goto error;
@@ -4068,7 +4092,8 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
     if (err == SASL_INTERACT) {
         if (!vnc_connection_gather_sasl_credentials(conn,
                                                     interact)) {
-            VNC_DEBUG("%s", "Failed to collect auth credentials");
+            vnc_connection_set_error(conn, "%s",
+                                     "Failed to collect auth credentials");
             goto error;
         }
         goto restart;
@@ -4078,8 +4103,9 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
               mechname, clientoutlen, clientout, clientout);
 
     if (clientoutlen > SASL_MAX_DATA_LEN) {
-        VNC_DEBUG("SASL negotiation data too long: %d bytes",
-                  clientoutlen);
+        vnc_connection_set_error(conn,
+                                 "SASL negotiation data too long: %d bytes",
+                                 clientoutlen);
         goto error;
     }
 
@@ -4095,18 +4121,19 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
         vnc_connection_write_u32(conn, 0);
     }
     vnc_connection_flush(conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         goto error;
 
 
     VNC_DEBUG("%s", "Getting sever start negotiation reply");
     /* Read the 'START' message reply from server */
     serverinlen = vnc_connection_read_u32(conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         goto error;
     if (serverinlen > SASL_MAX_DATA_LEN) {
-        VNC_DEBUG("SASL negotiation data too long: %d bytes",
-                  clientoutlen);
+        vnc_connection_set_error(conn,
+                                 "SASL negotiation data too long: %d bytes",
+                                 clientoutlen);
         goto error;
     }
 
@@ -4120,7 +4147,7 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
         serverin = NULL;
     }
     complete = vnc_connection_read_u8(conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         goto error;
 
     VNC_DEBUG("Client start result complete: %d. Data %d bytes %p '%s'",
@@ -4143,8 +4170,9 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
                                &clientout,
                                &clientoutlen);
         if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
-            VNC_DEBUG("Failed SASL step: %d (%s)",
-                      err, sasl_errdetail(saslconn));
+            vnc_connection_set_error(conn,
+                                     "Failed SASL step: %d (%s)",
+                                     err, sasl_errdetail(saslconn));
             goto error;
         }
 
@@ -4152,7 +4180,8 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
         if (err == SASL_INTERACT) {
             if (!vnc_connection_gather_sasl_credentials(conn,
                                                         interact)) {
-                VNC_DEBUG("%s", "Failed to collect auth credentials");
+                vnc_connection_set_error(conn, "%s",
+                                         "Failed to collect auth credentials");
                 goto error;
             }
             goto restep;
@@ -4179,17 +4208,18 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
             vnc_connection_write_u32(conn, 0);
         }
         vnc_connection_flush(conn);
-        if (priv->has_error)
+        if (priv->coroutine_stop)
             goto error;
 
         VNC_DEBUG("Server step with %d bytes %p", clientoutlen, clientout);
 
         serverinlen = vnc_connection_read_u32(conn);
-        if (priv->has_error)
+        if (priv->coroutine_stop)
             goto error;
         if (serverinlen > SASL_MAX_DATA_LEN) {
-            VNC_DEBUG("SASL negotiation data too long: %d bytes",
-                      clientoutlen);
+            vnc_connection_set_error(conn,
+                                     "SASL negotiation data too long: %d bytes",
+                                     clientoutlen);
             goto error;
         }
 
@@ -4203,7 +4233,7 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
             serverin = NULL;
         }
         complete = vnc_connection_read_u8(conn);
-        if (priv->has_error)
+        if (priv->coroutine_stop)
             goto error;
 
         VNC_DEBUG("Client step result complete: %d. Data %d bytes %p '%s'",
@@ -4222,14 +4252,16 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
     if (!priv->tls_session) {
         err = sasl_getprop(saslconn, SASL_SSF, &val);
         if (err != SASL_OK) {
-            VNC_DEBUG("cannot query SASL ssf on connection %d (%s)",
-                      err, sasl_errstring(err, NULL, NULL));
+            vnc_connection_set_error(conn,
+                                     "cannot query SASL ssf on connection %d (%s)",
+                                     err, sasl_errstring(err, NULL, NULL));
             goto error;
         }
         ssf = *(const int *)val;
         VNC_DEBUG("SASL SSF value %d", ssf);
         if (ssf < 56) { /* 56 == DES level, good for Kerberos */
-            VNC_DEBUG("negotiation SSF %d was not strong enough", ssf);
+            vnc_connection_set_error(conn,
+                                     "negotiated SSF %d was not strong enough", ssf);
             goto error;
         }
     }
@@ -4243,7 +4275,6 @@ static gboolean vnc_connection_perform_auth_sasl(VncConnection *conn)
     return ret;
 
  error:
-    priv->has_error = TRUE;
     if (saslconn)
         sasl_dispose(&saslconn);
     return FALSE;
@@ -4259,19 +4290,18 @@ static gboolean vnc_connection_start_tls(VncConnection *conn, int anonTLS)
 
     VNC_DEBUG("Do TLS handshake");
     if (vnc_connection_tls_initialize() < 0) {
-        VNC_DEBUG("Failed to init TLS");
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "%s", "Failed to init TLS");
         return FALSE;
     }
     if (priv->tls_session == NULL) {
         if (gnutls_init(&priv->tls_session, GNUTLS_CLIENT) < 0) {
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "%s", "Failed to allocate client session");
             return FALSE;
         }
 
         if (gnutls_priority_set_direct(priv->tls_session, priority, NULL) < 0) {
             gnutls_deinit(priv->tls_session);
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "%s", "Failed to set priority");
             return FALSE;
         }
 
@@ -4279,12 +4309,12 @@ static gboolean vnc_connection_start_tls(VncConnection *conn, int anonTLS)
             gnutls_anon_client_credentials anon_cred = vnc_connection_tls_initialize_anon_cred();
             if (!anon_cred) {
                 gnutls_deinit(priv->tls_session);
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "%s", "Failed to allocate credentials");
                 return FALSE;
             }
             if (gnutls_credentials_set(priv->tls_session, GNUTLS_CRD_ANON, anon_cred) < 0) {
                 gnutls_deinit(priv->tls_session);
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "%s", "Failed to initialize credentials");
                 return FALSE;
             }
         } else {
@@ -4297,12 +4327,12 @@ static gboolean vnc_connection_start_tls(VncConnection *conn, int anonTLS)
             gnutls_certificate_credentials_t x509_cred = vnc_connection_tls_initialize_cert_cred(conn);
             if (!x509_cred) {
                 gnutls_deinit(priv->tls_session);
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "%s", "Failed to allocate credentials");
                 return FALSE;
             }
             if (gnutls_credentials_set(priv->tls_session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
                 gnutls_deinit(priv->tls_session);
-                priv->has_error = TRUE;
+                vnc_connection_set_error(conn, "%s", "Failed to initialize credentials");
                 return FALSE;
             }
         }
@@ -4322,10 +4352,10 @@ static gboolean vnc_connection_start_tls(VncConnection *conn, int anonTLS)
                 g_io_wait(priv->sock, G_IO_OUT);
             goto retry;
         }
-        VNC_DEBUG("Handshake failed %s", gnutls_strerror(ret));
         gnutls_deinit(priv->tls_session);
         priv->tls_session = NULL;
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Failed to complete handshake %s",
+                                  gnutls_strerror(ret));
         return FALSE;
     }
 
@@ -4335,8 +4365,6 @@ static gboolean vnc_connection_start_tls(VncConnection *conn, int anonTLS)
         return TRUE;
     } else {
         if (!vnc_connection_validate_certificate(conn)) {
-            VNC_DEBUG("Certificate validation failed");
-            priv->has_error = TRUE;
             return FALSE;
         }
         return TRUE;
@@ -4348,7 +4376,7 @@ static gboolean vnc_connection_has_auth_subtype(gpointer data)
     VncConnection *conn = data;
     VncConnectionPrivate *priv = conn->priv;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return TRUE;
     if (priv->auth_subtype == VNC_CONNECTION_AUTH_INVALID)
         return FALSE;
@@ -4411,8 +4439,7 @@ static gboolean vnc_connection_perform_auth_tls(VncConnection *conn)
     }
 
     if (nauth > sizeof(auth)) {
-        VNC_DEBUG("Too many (%d) auth types", nauth);
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Too many (%d) auth types", nauth);
         return FALSE;
     }
     for (i = 0 ; i < nauth ; i++) {
@@ -4423,15 +4450,15 @@ static gboolean vnc_connection_perform_auth_tls(VncConnection *conn)
         VNC_DEBUG("Possible TLS sub-auth %d", auth[i]);
     }
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
     vnc_connection_choose_auth(conn, VNC_AUTH_CHOOSE_SUBTYPE, nauth, auth);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     VNC_DEBUG("Waiting for TLS auth subtype");
     g_condition_wait(vnc_connection_has_auth_subtype, conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     VNC_DEBUG("Choose auth %d", priv->auth_subtype);
@@ -4451,6 +4478,8 @@ static gboolean vnc_connection_perform_auth_tls(VncConnection *conn)
         return vnc_connection_perform_auth_sasl(conn);
 #endif
     default:
+        vnc_connection_set_error(conn, "Auth subtype %d is not supported",
+                                 priv->auth_subtype);
         return FALSE;
     }
 
@@ -4469,7 +4498,7 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
 
     if (major != 0 &&
         minor != 2) {
-        VNC_DEBUG("Unsupported VeNCrypt version %d %d", major, minor);
+        vnc_connection_set_error(conn, "Unsupported VeNCrypt version %d %d", major, minor);
         return FALSE;
     }
 
@@ -4478,13 +4507,13 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
     vnc_connection_flush(conn);
     status = vnc_connection_read_u8(conn);
     if (status != 0) {
-        VNC_DEBUG("Server refused VeNCrypt version %d %d", major, minor);
+        vnc_connection_set_error(conn, "Server refused VeNCrypt version %d %d", major, minor);
         return FALSE;
     }
 
     nauth = vnc_connection_read_u8(conn);
     if (nauth > (sizeof(auth)/sizeof(auth[0]))) {
-        VNC_DEBUG("Too many (%d) auth types", nauth);
+        vnc_connection_set_error(conn, "Too many (%d) auth types", nauth);
         return FALSE;
     }
 
@@ -4496,15 +4525,15 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
         VNC_DEBUG("Possible VeNCrypt sub-auth %d", auth[i]);
     }
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
     vnc_connection_choose_auth(conn, VNC_AUTH_CHOOSE_SUBTYPE, nauth, auth);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     VNC_DEBUG("Waiting for VeNCrypt auth subtype");
     g_condition_wait(vnc_connection_has_auth_subtype, conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     VNC_DEBUG("Choose auth %d", priv->auth_subtype);
@@ -4514,7 +4543,8 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
 
 #ifndef DEBUG
     if (priv->auth_subtype == VNC_CONNECTION_AUTH_VENCRYPT_PLAIN) {
-        VNC_DEBUG("Cowardly refusing to transmit plain text password");
+        vnc_connection_set_error(conn, "%s",
+                                 "Cowardly refusing to transmit plain text password");
         return FALSE;
     }
 #endif
@@ -4523,7 +4553,8 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
     vnc_connection_flush(conn);
     status = vnc_connection_read_u8(conn);
     if (status != 1) {
-        VNC_DEBUG("Server refused VeNCrypt auth %d %d", priv->auth_subtype, status);
+        vnc_connection_set_error(conn,
+                                 "Server refused VeNCrypt auth %d %d", priv->auth_subtype, status);
         return FALSE;
     }
 
@@ -4539,7 +4570,6 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
     }
 
     if (!vnc_connection_start_tls(conn, anonTLS)) {
-        VNC_DEBUG("Could not start TLS");
         return FALSE;
     }
     VNC_DEBUG("Completed TLS setup, do subauth %d", priv->auth_subtype);
@@ -4566,7 +4596,7 @@ static gboolean vnc_connection_perform_auth_vencrypt(VncConnection *conn)
 #endif
 
     default:
-        VNC_DEBUG("Unknown auth subtype %d", priv->auth_subtype);
+        vnc_connection_set_error(conn, "Unknown auth subtype %d", priv->auth_subtype);
         return FALSE;
     }
 }
@@ -4576,7 +4606,7 @@ static gboolean vnc_connection_has_auth_type(gpointer data)
     VncConnection *conn = data;
     VncConnectionPrivate *priv = conn->priv;
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return TRUE;
     if (priv->auth_type == VNC_CONNECTION_AUTH_INVALID)
         return FALSE;
@@ -4601,7 +4631,8 @@ static gboolean vnc_connection_perform_auth(VncConnection *conn)
             return vnc_connection_check_auth_result(conn);
 
         if (nauth > sizeof(auth)) {
-            priv->has_error = TRUE;
+            vnc_connection_set_error(conn, "Too many auth types %u",
+                                     nauth);
             return FALSE;
         }
         for (i = 0 ; i < nauth ; i++)
@@ -4612,15 +4643,15 @@ static gboolean vnc_connection_perform_auth(VncConnection *conn)
         VNC_DEBUG("Possible auth %u", auth[i]);
     }
 
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
     vnc_connection_choose_auth(conn, VNC_AUTH_CHOOSE_TYPE, nauth, auth);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     VNC_DEBUG("Waiting for auth type");
     g_condition_wait(vnc_connection_has_auth_type, conn);
-    if (priv->has_error)
+    if (priv->coroutine_stop)
         return FALSE;
 
     VNC_DEBUG("Choose auth %u", priv->auth_type);
@@ -4641,8 +4672,10 @@ static gboolean vnc_connection_perform_auth(VncConnection *conn)
         return vnc_connection_perform_auth_vnc(conn);
 
     case VNC_CONNECTION_AUTH_TLS:
-        if (priv->minor < 7)
+        if (priv->minor < 7) {
+            vnc_connection_set_error(conn, "%s", "TLS auth requires protocol 3.8");
             return FALSE;
+        }
         return vnc_connection_perform_auth_tls(conn);
 
     case VNC_CONNECTION_AUTH_VENCRYPT:
@@ -4664,8 +4697,8 @@ static gboolean vnc_connection_perform_auth(VncConnection *conn)
             struct signal_data sigdata;
             sigdata.params.authUnsupported = priv->auth_type;
             vnc_connection_emit_main_context(conn, VNC_AUTH_UNSUPPORTED, &sigdata);
-            priv->has_error = TRUE;
         }
+        vnc_connection_set_error(conn, "Unsupported auth type %d", priv->auth_type);
         return FALSE;
     }
 
@@ -4893,6 +4926,16 @@ static void vnc_connection_class_init(VncConnectionClass *klass)
                       g_cclosure_marshal_VOID__VOID,
                       G_TYPE_NONE,
                       0);
+    signals[VNC_ERROR] =
+        g_signal_new ("vnc-error",
+                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (VncConnectionClass, vnc_error),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__STRING,
+                      G_TYPE_NONE,
+                      1,
+                      G_TYPE_STRING);
 
 
     g_type_class_add_private(klass, sizeof(VncConnectionPrivate));
@@ -5028,7 +5071,9 @@ static void vnc_connection_close(VncConnection *conn)
 
     memset(&priv->fmt, 0, sizeof(priv->fmt));
 
-    priv->has_error = FALSE;
+    g_free(priv->error);
+    priv->error = NULL;
+    priv->coroutine_stop = FALSE;
 }
 
 
@@ -5052,8 +5097,8 @@ void vnc_connection_shutdown(VncConnection *conn)
     }
 
     priv->fd = -1;
-    priv->has_error = 1;
-    VNC_DEBUG("Waking up couroutine to shutdown gracefully");
+    priv->coroutine_stop = TRUE;
+    VNC_DEBUG("Waking up coroutine to shutdown gracefully");
     g_io_wakeup(&priv->wait);
 
     /* Closing the socket triggers an I/O error in the
@@ -5144,14 +5189,16 @@ static gboolean vnc_connection_initialize(VncConnection *conn)
 
     ret = sscanf(version, "RFB %03d.%03d\n", &priv->major, &priv->minor);
     if (ret != 2) {
-        VNC_DEBUG("Error while parsing server version");
+        vnc_connection_set_error(conn, "%s",
+                                 "Error while parsing server version");
         goto fail;
     }
 
     VNC_DEBUG("Server version: %d.%d", priv->major, priv->minor);
 
     if (vnc_connection_before_version(conn, 3, 3)) {
-        VNC_DEBUG("Server version is not supported (%d.%d)", priv->major, priv->minor);
+        vnc_connection_set_error(conn,
+                                 "Server version is not supported (%d.%d)", priv->major, priv->minor);
         goto fail;
     } else if (vnc_connection_before_version(conn, 3, 7)) {
         priv->minor = 3;
@@ -5183,8 +5230,11 @@ static gboolean vnc_connection_initialize(VncConnection *conn)
     vnc_connection_read_pixel_format(conn, &priv->fmt);
 
     n_name = vnc_connection_read_u32(conn);
-    if (n_name > 4096)
+    if (n_name > 4096) {
+        vnc_connection_set_error(conn, "Name length %u too long",
+                                 n_name);
         goto fail;
+    }
 
     priv->name = g_new(char, n_name + 1);
 
@@ -5204,7 +5254,6 @@ static gboolean vnc_connection_initialize(VncConnection *conn)
     return !vnc_connection_has_error(conn);
 
  fail:
-    priv->has_error = TRUE;
     return !vnc_connection_has_error(conn);
 }
 
@@ -5292,6 +5341,8 @@ static gboolean vnc_connection_open_addr_internal(VncConnection *conn)
     VNC_DEBUG("Connecting with addr %p", priv->addr);
 
     sock = vnc_connection_connect_socket(&priv->wait, priv->addr, &conn_error);
+    vnc_connection_set_error(conn, "Unable to connect: %s",
+                             conn_error->message);
     g_clear_error(&conn_error);
     if (sock) {
         priv->sock = sock;
@@ -5330,6 +5381,10 @@ static gboolean vnc_connection_open_host_internal(VncConnection *conn)
         g_object_unref(sockaddr);
     }
     g_object_unref(enumerator);
+    if (!sock) {
+        vnc_connection_set_error(conn, "Unable to connect: %s",
+                                 conn_error->message);
+    }
     g_clear_error(&conn_error);
     if (sock) {
         priv->sock = sock;
@@ -5568,7 +5623,7 @@ gboolean vnc_connection_set_auth_type(VncConnection *conn, unsigned int type)
 
     VNC_DEBUG("Thinking about auth type %u", type);
     if (priv->auth_type != VNC_CONNECTION_AUTH_INVALID) {
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "%s", "Auth type has already been set");
         return !vnc_connection_has_error(conn);
     }
     if (type != VNC_CONNECTION_AUTH_NONE &&
@@ -5578,9 +5633,9 @@ gboolean vnc_connection_set_auth_type(VncConnection *conn, unsigned int type)
         type != VNC_CONNECTION_AUTH_TLS &&
         type != VNC_CONNECTION_AUTH_VENCRYPT &&
         type != VNC_CONNECTION_AUTH_SASL) {
-        VNC_DEBUG("Unsupported auth type %u", type);
+        vnc_connection_set_error(conn, "Auth type %d is not supported",
+                                 type);
         g_signal_emit(conn, VNC_AUTH_UNSUPPORTED, 0, type);
-        priv->has_error = TRUE;
         return !vnc_connection_has_error(conn);
     }
     VNC_DEBUG("Decided on auth type %u", type);
@@ -5608,11 +5663,12 @@ gboolean vnc_connection_set_auth_subtype(VncConnection *conn, unsigned int type)
     VNC_DEBUG("Requested auth subtype %d", type);
     if (priv->auth_type != VNC_CONNECTION_AUTH_VENCRYPT &&
         priv->auth_type != VNC_CONNECTION_AUTH_TLS) {
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Auth type %d does not support subauth",
+                                 priv->auth_type);
         return !vnc_connection_has_error(conn);
     }
     if (priv->auth_subtype != VNC_CONNECTION_AUTH_INVALID) {
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "%s", "Auth subtype has already been set");
         return !vnc_connection_has_error(conn);
     }
     priv->auth_subtype = type;
@@ -5719,7 +5775,7 @@ gboolean vnc_connection_set_credential(VncConnection *conn, int type, const gcha
         return vnc_connection_set_credential_x509(conn, data);
 
     default:
-        priv->has_error = TRUE;
+        vnc_connection_set_error(conn, "Unknown credential type %d", type);
     }
 
     return !vnc_connection_has_error(conn);
diff --git a/src/vncconnection.h b/src/vncconnection.h
index 72d3f38..8c1fa28 100644
--- a/src/vncconnection.h
+++ b/src/vncconnection.h
@@ -79,12 +79,13 @@ struct _VncConnectionClass
     void (*vnc_initialized)(VncConnection *conn);
     void (*vnc_disconnected)(VncConnection *conn);
     void (*vnc_led_state)(VncConnection *conn);
+    void (*vnc_error)(VncConnection *conn, const char *message);
 
     /*
      * If adding fields to this struct, remove corresponding
      * amount of padding to avoid changing overall struct size
      */
-    gpointer _vnc_reserved[VNC_PADDING_LARGE - 4];
+    gpointer _vnc_reserved[VNC_PADDING_LARGE - 5];
 };
 
 
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index bc3a4ef..00714bd 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -139,6 +139,7 @@ typedef enum
 
         VNC_SERVER_CUT_TEXT,
         VNC_BELL,
+        VNC_ERROR,
 
         LAST_SIGNAL
     } vnc_display_signals;
@@ -1648,6 +1649,17 @@ static void on_connected(VncConnection *conn G_GNUC_UNUSED,
 }
 
 
+static void on_error(VncConnection *conn G_GNUC_UNUSED,
+                     const char *message,
+                     gpointer opaque)
+{
+    VncDisplay *obj = VNC_DISPLAY(opaque);
+
+    g_signal_emit(G_OBJECT(obj), signals[VNC_ERROR], 0, message);
+    VNC_DEBUG("VNC server error");
+}
+
+
 static void on_initialized(VncConnection *conn G_GNUC_UNUSED,
                            gpointer opaque)
 {
@@ -2303,6 +2315,17 @@ static void vnc_display_class_init(VncDisplayClass *klass)
                       G_TYPE_NONE,
                       0);
 
+    signals[VNC_ERROR] =
+        g_signal_new ("vnc-error",
+                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      0,
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__STRING,
+                      G_TYPE_NONE,
+                      1,
+                      G_TYPE_STRING);
+
     signals[VNC_AUTH_CREDENTIAL] =
         g_signal_new ("vnc-auth-credential",
                       G_OBJECT_CLASS_TYPE (object_class),
@@ -2547,6 +2570,8 @@ static void vnc_display_init(VncDisplay *display)
                      G_CALLBACK(on_initialized), display);
     g_signal_connect(G_OBJECT(priv->conn), "vnc-disconnected",
                      G_CALLBACK(on_disconnected), display);
+    g_signal_connect(G_OBJECT(priv->conn), "vnc-error",
+                     G_CALLBACK(on_error), display);
 
     priv->keycode_map = vnc_display_keymap_gdk2rfb_table(&priv->keycode_maplen);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]