service_get_secrets (NMExportedConnection *exported,
                     const gchar *setting_name,
                     const gchar **hints,
                     gboolean request_new,
                     DBusGMethodInvocation *context)
{
	NMConnection *connection;
	GError *error = NULL;
	GHashTable *settings = NULL;
	GHashTable *secrets = NULL;
	NMSettingConnection *s_con;
	NMSetting *setting;
	const char *connection_id;
	const char *connection_type;

	connection = nm_exported_connection_get_connection (exported);

	setting = nm_connection_get_setting_by_name (connection, setting_name);
	if (!setting) {
		g_set_error (&error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
		             "%s.%d - Connection didn't have requested setting '%s'.",
		             __FILE__, __LINE__, setting_name);
		secrets_return_error (context, error);
		return;
	}

	s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
	connection_id = s_con ? nm_setting_connection_get_id (s_con) : NULL;
	connection_type = s_con ? nm_setting_connection_get_connection_type (s_con) : NULL;

	if (!s_con || !connection_id || !strlen (connection_id) || !connection_type) {
		g_set_error (&error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
		             "%s.%d - Connection didn't have required '"
		             NM_SETTING_CONNECTION_SETTING_NAME
		             "' setting , or the connection name was invalid.",
		             __FILE__, __LINE__);
		secrets_return_error (context, error);
		return;
	}

	/* VPN passwords are handled by the VPN plugin's auth dialog */
	if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME))
		goto get_secrets;

	if (request_new) {
		nm_info ("New secrets for %s/%s requested; ask the user",
		         connection_id, setting_name);
		nm_connection_clear_secrets (connection);
		goto get_secrets;
	}

	secrets = nm_gconf_get_keyring_items (connection, setting_name, FALSE, &error);
	if (!secrets) {
		if (error) {
			secrets_return_error (context, error);
			return;
		}
		nm_info ("No keyring secrets found for %s/%s; asking user.",
		         connection_id, setting_name);
		goto get_secrets;
	}

	if (g_hash_table_size (secrets) == 0) {
		g_hash_table_destroy (secrets);
		nm_warning ("%s.%d - Secrets were found for setting '%s' but none"
				  " were valid.", __FILE__, __LINE__, setting_name);
		goto get_secrets;
	}

	/* If there were hints, and none of the hints were returned by the keyring,
	 * get some new secrets.
	 */
	if (hints && g_strv_length ((char **) hints)) {
		FindHintsInfo info = { .found = FALSE, .hints = hints };

		g_hash_table_foreach (secrets, find_hints_in_secrets, &info);
		if (info.found == FALSE) {
			g_hash_table_destroy (secrets);
			goto get_secrets;
		}
	}

	/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
	 * will contain all the individual settings hashes.
	 */
	settings = g_hash_table_new_full (g_str_hash, g_str_equal,
	                                  g_free,
	                                  (GDestroyNotify) g_hash_table_destroy);
	g_hash_table_insert (settings, g_strdup (setting_name), secrets);
	dbus_g_method_return (context, settings);
	g_hash_table_destroy (settings);
	return;

get_secrets:
	g_signal_emit (exported,
	               signals[NEW_SECRETS_REQUESTED],
	               0,
	               setting_name,
	               hints,
	               request_new,
	               context);
}