+ ASSERT_IS_WUI_THREAD ();
+
+ /* Generate the vms URI from the base URI. */
+ len = strlen (uri) + 4 + 1;
+ vms_uri = g_alloca (len);
+ snprintf (vms_uri, len, "%s/vms", uri);
+
+ DEBUG ("vms URI %s", vms_uri);
+
+ /* Set the URI for libcurl. */
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, vms_uri));
+
+ /* We want to capture the output, so tell our write function
+ * to place the output into a buffer.
+ */
+ write_fn_start_capture ();
+
+ /* Try to fetch the URI. */
+ r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
+ if (r != CURLE_OK) {
+ /* Signal an error back to the main thread. */
+ error_str = g_strdup (curl_easy_strerror (r));
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+ }
+
+ CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
+ DEBUG ("HTTP return code is %ld", code);
+ switch (code)
+ {
+ case 200: break;
+
+ /* Hmm - even though we previously logged in, the server is
+ * rejecting our attempts now with an authorization error.
+ * We move to the logged out state.
+ */
+ case 401:
+ set_logged_in (FALSE);
+ error_str = g_strdup ("server rejected the username or password");
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+
+ default:
+ /* XXX If only glib had g_asprintf. */
+ error_str = g_strdup ("unexpected HTTP return code from server");
+ g_idle_add (main_status_error, error_str);
+ return FALSE;
+ }
+
+ /* If we got here then we appear to have a correct
+ * XML document describing the list of VMs.
+ */
+ secs_between_refresh <<= 1;
+
+ xml = write_fn_finish_capture ();
+
+ /*DEBUG ("XML from /vms =\n%s", xml);*/
+ parse_vmlist_from_xml (xml);
+ g_free (xml);
+
+ return TRUE;
+}
+
+/* Functions to deal with the list of VMs, parsing it from the XML, etc.
+ *
+ * A vmlist is a GSList (glib singly-linked list) of vm structures.
+ * The caller must call free_vmlist to free up this list correctly.
+ */
+
+static void
+free_vm (gpointer _vm, gpointer data)
+{
+ struct vm *vm = (struct vm *) _vm;
+
+ g_free (vm->description);
+ g_free (vm->state);
+ g_free (vm->uuid);
+ g_free (vm->mac_addr);
+ g_free (vm);
+}
+
+void
+free_vmlist (GSList *vmlist)
+{
+ g_slist_foreach (vmlist, free_vm, NULL);
+ g_slist_free (vmlist);
+}
+
+static struct vm *
+copy_vm (struct vm *vm)
+{
+ struct vm *vm2;
+
+ vm2 = g_memdup (vm, sizeof (*vm));
+ vm2->description = g_strdup (vm->description);
+ vm2->state = g_strdup (vm->state);
+ vm2->uuid = g_strdup (vm->uuid);
+ vm2->mac_addr = g_strdup (vm->mac_addr);
+ return vm2;
+}
+
+static int
+compare_vm (gconstpointer _vm1, gconstpointer _vm2)
+{
+ const struct vm *vm1 = (const struct vm *) _vm1;
+ const struct vm *vm2 = (const struct vm *) _vm2;
+
+ return strcmp (vm1->description, vm2->description);
+}
+
+static GSList *vmlist = NULL;
+static gboolean vmlist_valid = FALSE;
+static GStaticMutex vmlist_mutex;
+
+/* Called from the main thread to find out if we have a valid vmlist. */
+gboolean
+wui_thread_has_valid_vmlist (void)
+{
+ gboolean ret;
+
+ ASSERT_IS_MAIN_THREAD ();
+
+ g_static_mutex_lock (&vmlist_mutex);
+ ret = vmlist_valid;
+ g_static_mutex_unlock (&vmlist_mutex);
+ return ret;
+}
+
+/* Called from the main thread to find return the current vmlist. This
+ * actually returns a deep copy of it that the caller must free.
+ */
+static void
+duplicate_and_insert_vm (gpointer _vm, gpointer _ret)
+{
+ struct vm *vm = (struct vm *) _vm;
+ GSList **ret = (GSList **) _ret;
+
+ *ret = g_slist_prepend (*ret, copy_vm (vm));
+}
+
+gboolean
+wui_thread_get_vmlist (GSList **ret)
+{
+ gboolean r;
+
+ ASSERT_IS_MAIN_THREAD ();
+
+ r = FALSE;
+ *ret = NULL;
+
+ g_static_mutex_lock (&vmlist_mutex);
+ if (!vmlist_valid) goto done;
+
+ g_slist_foreach (vmlist, duplicate_and_insert_vm, ret);
+ *ret = g_slist_sort (*ret, compare_vm);
+
+ r = TRUE;
+ done:
+ g_static_mutex_unlock (&vmlist_mutex);
+ return r;
+}
+
+/* Called from the message loop in the WUI thread, with an XML document
+ * which we turn into a list of VM structures, and update the vmlist
+ * if we can.
+ */
+static void
+parse_vmlist_from_xml (const char *xml)
+{
+
+
+
+
+
+
+