From ce1ac3ee3c148a19bdb6ee94f8d3f8f6e6e4fca5 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 11 Apr 2013 13:43:27 +0200 Subject: [PATCH] More extensible passing of options to join/leave implementations We want to add furhter options here, so make things more manageable. https://bugs.freedesktop.org/show_bug.cgi?id=63434 --- service/Makefile.am | 1 + service/realm-adcli-enroll.c | 5 +++- service/realm-adcli-enroll.h | 2 +- service/realm-example.c | 8 ++----- service/realm-kerberos-membership.h | 6 ----- service/realm-kerberos.c | 40 ++++++------------------------- service/realm-kerberos.h | 3 --- service/realm-options.c | 47 +++++++++++++++++++++++++++++++++++++ service/realm-options.h | 31 ++++++++++++++++++++++++ service/realm-samba-enroll.c | 10 +++++--- service/realm-samba-enroll.h | 3 ++- service/realm-samba.c | 15 ++++++------ service/realm-sssd-ad.c | 17 +++++++------- service/realm-sssd-ipa.c | 5 ++-- 14 files changed, 119 insertions(+), 74 deletions(-) create mode 100644 service/realm-options.c create mode 100644 service/realm-options.h diff --git a/service/Makefile.am b/service/Makefile.am index 6c441fb..c24dd83 100644 --- a/service/Makefile.am +++ b/service/Makefile.am @@ -38,6 +38,7 @@ realmd_SOURCES = \ realm-kerberos-provider.c realm-kerberos-provider.h \ realm-login-name.c realm-login-name.h \ realm-network.c realm-network.h \ + realm-options.c realm-options.h \ realm-packages.c realm-packages.h \ realm-provider.c realm-provider.h \ realm-samba.c realm-samba.h \ diff --git a/service/realm-adcli-enroll.c b/service/realm-adcli-enroll.c index 6a60560..9051058 100644 --- a/service/realm-adcli-enroll.c +++ b/service/realm-adcli-enroll.c @@ -20,6 +20,7 @@ #include "realm-diagnostics.h" #include "realm-errors.h" #include "realm-ini-config.h" +#include "realm-options.h" #include "realm-settings.h" static void @@ -71,12 +72,13 @@ on_join_process (GObject *source, void realm_adcli_enroll_join_async (const gchar *realm, RealmCredential *cred, - const gchar *computer_ou, + GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data) { gchar *environ[] = { "LANG=C", NULL }; + const gchar *computer_ou; GSimpleAsyncResult *async; GBytes *input = NULL; GPtrArray *args; @@ -99,6 +101,7 @@ realm_adcli_enroll_join_async (const gchar *realm, g_ptr_array_add (args, "--domain"); g_ptr_array_add (args, (gpointer)realm); + computer_ou = realm_options_computer_ou (options, realm); if (computer_ou) { g_ptr_array_add (args, "--computer-ou"); g_ptr_array_add (args, (gpointer)computer_ou); diff --git a/service/realm-adcli-enroll.h b/service/realm-adcli-enroll.h index 3075506..2e2dcff 100644 --- a/service/realm-adcli-enroll.h +++ b/service/realm-adcli-enroll.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS void realm_adcli_enroll_join_async (const gchar *realm, RealmCredential *cred, - const gchar *computer_ou, + GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data); diff --git a/service/realm-example.c b/service/realm-example.c index e35e823..a30b6e3 100644 --- a/service/realm-example.c +++ b/service/realm-example.c @@ -160,7 +160,6 @@ on_join_sleep_done (GObject *source, static void realm_example_join_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -260,7 +259,6 @@ static void realm_example_leave_password_async (RealmKerberosMembership *membership, const gchar *name, GBytes *password, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -295,7 +293,6 @@ realm_example_leave_password_async (RealmKerberosMembership *membership, static void realm_example_leave_automatic_async (RealmKerberosMembership *membership, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -331,7 +328,6 @@ realm_example_leave_automatic_async (RealmKerberosMembership *membership, static void realm_example_leave_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -339,11 +335,11 @@ realm_example_leave_async (RealmKerberosMembership *membership, { switch (cred->type) { case REALM_CREDENTIAL_AUTOMATIC: - realm_example_leave_automatic_async (membership, flags, options, invocation, callback, user_data); + realm_example_leave_automatic_async (membership, options, invocation, callback, user_data); break; case REALM_CREDENTIAL_PASSWORD: realm_example_leave_password_async (membership, cred->x.password.name, cred->x.password.value, - flags, options, invocation, callback, user_data); + options, invocation, callback, user_data); break; default: g_return_if_reached (); diff --git a/service/realm-kerberos-membership.h b/service/realm-kerberos-membership.h index 40e4607..9b1e395 100644 --- a/service/realm-kerberos-membership.h +++ b/service/realm-kerberos-membership.h @@ -26,10 +26,6 @@ G_BEGIN_DECLS -typedef enum { - REALM_KERBEROS_ASSUME_PACKAGES = 1 << 5, -} RealmKerberosFlags; - #define REALM_TYPE_KERBEROS_MEMBERSHIP (realm_kerberos_membership_get_type ()) #define REALM_KERBEROS_MEMBERSHIP(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), REALM_TYPE_KERBEROS_MEMBERSHIP, RealmKerberosMembership)) #define REALM_IS_KERBEROS_MEMBERSHIP(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), REALM_TYPE_KERBEROS_MEMBERSHIP)) @@ -43,7 +39,6 @@ struct _RealmKerberosMembershipIface { void (* join_async) (RealmKerberosMembership *realm, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -57,7 +52,6 @@ struct _RealmKerberosMembershipIface { void (* leave_async) (RealmKerberosMembership *realm, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, diff --git a/service/realm-kerberos.c b/service/realm-kerberos.c index a743044..1c88b34 100644 --- a/service/realm-kerberos.c +++ b/service/realm-kerberos.c @@ -26,6 +26,7 @@ #include "realm-kerberos.h" #include "realm-kerberos-membership.h" #include "realm-login-name.h" +#include "realm-options.h" #include "realm-packages.h" #include "realm-provider.h" #include "realm-settings.h" @@ -279,7 +280,6 @@ is_credential_supported (RealmKerberosMembershipIface *iface, static void join_or_leave (RealmKerberos *self, GVariant *credential, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, gboolean join) @@ -322,11 +322,11 @@ join_or_leave (RealmKerberos *self, if (join) { g_return_if_fail (iface->join_finish != NULL); - (iface->join_async) (REALM_KERBEROS_MEMBERSHIP (self), cred, flags, + (iface->join_async) (REALM_KERBEROS_MEMBERSHIP (self), cred, options, invocation, on_enroll_complete, method); } else { g_return_if_fail (iface->leave_finish != NULL); - (iface->leave_async) (REALM_KERBEROS_MEMBERSHIP (self), cred, flags, + (iface->leave_async) (REALM_KERBEROS_MEMBERSHIP (self), cred, options, invocation, on_unenroll_complete, method); } } @@ -340,8 +340,6 @@ handle_join (RealmDbusKerberosMembership *membership, { RealmKerberos *self = REALM_KERBEROS (user_data); gchar hostname[HOST_NAME_MAX + 1]; - RealmKerberosFlags flags = 0; - gboolean assume = FALSE; gint ret; /* Check the host name */ @@ -353,10 +351,7 @@ handle_join (RealmDbusKerberosMembership *membership, return TRUE; } - if (g_variant_lookup (options, REALM_DBUS_OPTION_ASSUME_PACKAGES, "b", &assume) && assume) - flags |= REALM_KERBEROS_ASSUME_PACKAGES; - - join_or_leave (self, credentials, flags, options, invocation, TRUE); + join_or_leave (self, credentials, options, invocation, TRUE); return TRUE; } @@ -368,16 +363,14 @@ handle_leave (RealmDbusKerberosMembership *membership, gpointer user_data) { RealmKerberos *self = REALM_KERBEROS (user_data); - RealmKerberosFlags flags = 0; - const gchar *computer_ou; - if (g_variant_lookup (options, REALM_DBUS_OPTION_COMPUTER_OU, "&s", &computer_ou)) { + if (realm_options_computer_ou (options, NULL)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "The computer-ou argument is not supported when leaving a domain."); return TRUE; } - join_or_leave (self, credentials, flags, options, invocation, FALSE); + join_or_leave (self, credentials, options, invocation, FALSE); return TRUE; } @@ -391,7 +384,7 @@ handle_deconfigure (RealmDbusRealm *realm, credential = g_variant_new ("(ss@v)", "automatic", "none", g_variant_new_variant (g_variant_new_string (""))); - join_or_leave (REALM_KERBEROS (user_data), credential, 0, options, invocation, FALSE); + join_or_leave (REALM_KERBEROS (user_data), credential, options, invocation, FALSE); g_variant_unref (credential); return TRUE; @@ -1118,25 +1111,6 @@ realm_kerberos_set_required_package_sets (RealmKerberos *self, g_strfreev (packages); } -gchar * -realm_kerberos_calculate_join_computer_ou (RealmKerberos *self, - GVariant *options) -{ - const gchar *computer_ou = NULL; - - g_return_val_if_fail (REALM_IS_KERBEROS (self), NULL); - - if (options) { - if (!g_variant_lookup (options, REALM_DBUS_OPTION_COMPUTER_OU, "&s", &computer_ou)) - computer_ou = NULL; - } - - if (!computer_ou) - computer_ou = realm_settings_value (realm_kerberos_get_name (self), REALM_DBUS_OPTION_COMPUTER_OU); - - return g_strdup (computer_ou); -} - static gboolean flush_keytab_entries (krb5_context ctx, krb5_keytab keytab, diff --git a/service/realm-kerberos.h b/service/realm-kerberos.h index 3a9d339..3ff815c 100644 --- a/service/realm-kerberos.h +++ b/service/realm-kerberos.h @@ -131,9 +131,6 @@ void realm_kerberos_set_configured (RealmKerberos *s void realm_kerberos_set_required_package_sets (RealmKerberos *self, const gchar **package_sets); -gchar * realm_kerberos_calculate_join_computer_ou (RealmKerberos *self, - GVariant *options); - G_END_DECLS #endif /* __REALM_KERBEROS_H__ */ diff --git a/service/realm-options.c b/service/realm-options.c new file mode 100644 index 0000000..4201abf --- /dev/null +++ b/service/realm-options.c @@ -0,0 +1,47 @@ +/* realmd -- Realm configuration service + * + * Copyright 2012 Red Hat Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + +#include "config.h" + +#include "realm-dbus-constants.h" +#include "realm-options.h" +#include "realm-settings.h" + +gboolean +realm_options_assume_packages (GVariant *options) +{ + gboolean assume; + + if (!g_variant_lookup (options, REALM_DBUS_OPTION_ASSUME_PACKAGES, "b", &assume)) + assume = FALSE; + + return assume; +} + +const gchar * +realm_options_computer_ou (GVariant *options, + const gchar *realm_name) +{ + const gchar *computer_ou = NULL; + + if (options) { + if (!g_variant_lookup (options, REALM_DBUS_OPTION_COMPUTER_OU, "&s", &computer_ou)) + computer_ou = NULL; + } + + if (!computer_ou) + computer_ou = realm_settings_value (realm_name, REALM_DBUS_OPTION_COMPUTER_OU); + + return g_strdup (computer_ou); +} diff --git a/service/realm-options.h b/service/realm-options.h new file mode 100644 index 0000000..bec1004 --- /dev/null +++ b/service/realm-options.h @@ -0,0 +1,31 @@ +/* realmd -- Realm configuration service + * + * Copyright 2012 Red Hat Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Stef Walter + */ + +#include "config.h" + +#ifndef __REALM_OPTIONS_H__ +#define __REALM_OPTIONS_H__ + +#include + +G_BEGIN_DECLS + +gboolean realm_options_assume_packages (GVariant *options); + +const gchar * realm_options_computer_ou (GVariant *options, + const gchar *realm_name); + +G_END_DECLS + +#endif /* __REALM_OPTIONS_H__ */ diff --git a/service/realm-samba-enroll.c b/service/realm-samba-enroll.c index d2b0c23..6b21364 100644 --- a/service/realm-samba-enroll.c +++ b/service/realm-samba-enroll.c @@ -21,6 +21,7 @@ #include "realm-discovery.h" #include "realm-errors.h" #include "realm-kerberos-discover.h" +#include "realm-options.h" #include "realm-samba-config.h" #include "realm-samba-enroll.h" #include "realm-samba-provider.h" @@ -404,12 +405,14 @@ static void begin_join (GSimpleAsyncResult *async, JoinClosure *join, const gchar *realm, - const gchar *computer_ou, + GVariant *options, GHashTable *discovery) { + const gchar *computer_ou; gchar *strange_ou; GError *error = NULL; + computer_ou = realm_options_computer_ou (options, realm); if (computer_ou != NULL) { strange_ou = realm_samba_util_build_strange_ou (computer_ou, realm); if (strange_ou) { @@ -438,7 +441,7 @@ begin_join (GSimpleAsyncResult *async, void realm_samba_enroll_join_async (const gchar *realm, RealmCredential *cred, - const gchar *computer_ou, + GVariant *options, GHashTable *discovery, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -467,7 +470,7 @@ realm_samba_enroll_join_async (const gchar *realm, g_return_if_reached (); } - begin_join (async, join, realm, computer_ou, discovery); + begin_join (async, join, realm, options, discovery); g_object_unref (async); } @@ -518,6 +521,7 @@ on_leave_complete (GObject *source, void realm_samba_enroll_leave_async (const gchar *realm, RealmCredential *cred, + GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data) diff --git a/service/realm-samba-enroll.h b/service/realm-samba-enroll.h index 3605f4f..8fc5cbe 100644 --- a/service/realm-samba-enroll.h +++ b/service/realm-samba-enroll.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS void realm_samba_enroll_join_async (const gchar *realm, RealmCredential *cred, - const gchar *computer_ou, + GVariant *options, GHashTable *discovery, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -39,6 +39,7 @@ gboolean realm_samba_enroll_join_finish (GAsyncResult *result void realm_samba_enroll_leave_async (const gchar *realm, RealmCredential *cred, + GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, gpointer user_data); diff --git a/service/realm-samba.c b/service/realm-samba.c index 89eaa03..0b65101 100644 --- a/service/realm-samba.c +++ b/service/realm-samba.c @@ -23,6 +23,7 @@ #include "realm-kerberos.h" #include "realm-kerberos-discover.h" #include "realm-kerberos-membership.h" +#include "realm-options.h" #include "realm-packages.h" #include "realm-provider.h" #include "realm-samba.h" @@ -139,7 +140,7 @@ lookup_login_prefix (RealmSamba *self) typedef struct { GDBusMethodInvocation *invocation; - gchar *computer_ou; + GVariant *options; gchar *realm_name; RealmCredential *cred; } EnrollClosure; @@ -149,7 +150,7 @@ enroll_closure_free (gpointer data) { EnrollClosure *enroll = data; g_free (enroll->realm_name); - g_free (enroll->computer_ou); + g_variant_unref (enroll->options); realm_credential_unref (enroll->cred); g_object_unref (enroll->invocation); g_slice_free (EnrollClosure, enroll); @@ -229,7 +230,7 @@ on_install_do_join (GObject *source, realm_packages_install_finish (result, &error); if (error == NULL) { realm_samba_enroll_join_async (enroll->realm_name, enroll->cred, - enroll->computer_ou, realm_kerberos_get_discovery (kerberos), + enroll->options, realm_kerberos_get_discovery (kerberos), enroll->invocation, on_join_do_winbind, g_object_ref (res)); } else { @@ -262,7 +263,6 @@ validate_membership_options (GVariant *options, static void realm_samba_join_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -281,7 +281,7 @@ realm_samba_join_async (RealmKerberosMembership *membership, enroll = g_slice_new0 (EnrollClosure); enroll->realm_name = g_strdup (realm_kerberos_get_realm_name (realm)); enroll->invocation = g_object_ref (invocation); - enroll->computer_ou = realm_kerberos_calculate_join_computer_ou (realm, options); + enroll->options = g_variant_ref (options); enroll->cred = realm_credential_ref (cred); g_simple_async_result_set_op_res_gpointer (res, enroll, enroll_closure_free); @@ -297,7 +297,7 @@ realm_samba_join_async (RealmKerberosMembership *membership, g_simple_async_result_complete_in_idle (res); } else { - if (flags & REALM_KERBEROS_ASSUME_PACKAGES) + if (realm_options_assume_packages (options)) packages = NO_PACKAGES; else packages = SAMBA_PACKAGES; @@ -399,7 +399,6 @@ on_leave_do_deconfigure (GObject *source, static void realm_samba_leave_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -432,7 +431,7 @@ realm_samba_leave_async (RealmKerberosMembership *membership, switch (cred->type) { case REALM_CREDENTIAL_PASSWORD: leave = g_simple_async_result_get_op_res_gpointer (async); - realm_samba_enroll_leave_async (leave->realm_name, cred, + realm_samba_enroll_leave_async (leave->realm_name, cred, options, leave->invocation, on_leave_do_deconfigure, g_object_ref (async)); break; diff --git a/service/realm-sssd-ad.c b/service/realm-sssd-ad.c index 2912cc9..cdf5fe2 100644 --- a/service/realm-sssd-ad.c +++ b/service/realm-sssd-ad.c @@ -20,6 +20,7 @@ #include "realm-diagnostics.h" #include "realm-errors.h" #include "realm-kerberos-membership.h" +#include "realm-options.h" #include "realm-packages.h" #include "realm-samba-enroll.h" #include "realm-service.h" @@ -97,7 +98,7 @@ realm_sssd_ad_constructed (GObject *obj) typedef struct { GDBusMethodInvocation *invocation; RealmCredential *cred; - gchar *computer_ou; + GVariant *options; gchar *realm_name; gboolean use_adcli; const gchar **packages; @@ -110,7 +111,7 @@ join_closure_free (gpointer data) g_free (join->realm_name); g_object_unref (join->invocation); realm_credential_unref (join->cred); - g_free (join->computer_ou); + g_variant_ref (join->options); g_slice_free (JoinClosure, join); } @@ -280,14 +281,14 @@ on_install_do_join (GObject *source, if (join->use_adcli) { realm_adcli_enroll_join_async (join->realm_name, join->cred, - join->computer_ou, + join->options, join->invocation, on_join_do_sssd, g_object_ref (async)); } else { realm_samba_enroll_join_async (join->realm_name, join->cred, - join->computer_ou, + join->options, realm_kerberos_get_discovery (kerberos), join->invocation, on_join_do_sssd, g_object_ref (async)); @@ -392,7 +393,6 @@ parse_join_options (JoinClosure *join, static void realm_sssd_ad_join_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -408,7 +408,7 @@ realm_sssd_ad_join_async (RealmKerberosMembership *membership, join = g_slice_new0 (JoinClosure); join->realm_name = g_strdup (realm_kerberos_get_realm_name (realm)); join->invocation = g_object_ref (invocation); - join->computer_ou = realm_kerberos_calculate_join_computer_ou (realm, options); + join->options = g_variant_ref (options); join->cred = realm_credential_ref (cred); g_simple_async_result_set_op_res_gpointer (async, join, join_closure_free); @@ -429,7 +429,7 @@ realm_sssd_ad_join_async (RealmKerberosMembership *membership, /* Prepared successfully without an error */ } else { - if (flags & REALM_KERBEROS_ASSUME_PACKAGES) + if (realm_options_assume_packages (options)) join->packages = NO_PACKAGES; realm_packages_install_async (join->packages, join->invocation, on_install_do_join, g_object_ref (async)); @@ -478,7 +478,6 @@ on_leave_do_deconfigure (GObject *source, static void realm_sssd_ad_leave_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -510,7 +509,7 @@ realm_sssd_ad_leave_async (RealmKerberosMembership *membership, leave->realm_name = g_strdup (realm_kerberos_get_realm_name (REALM_KERBEROS (self))); leave->invocation = g_object_ref (invocation); g_simple_async_result_set_op_res_gpointer (async, leave, leave_closure_free); - realm_samba_enroll_leave_async (leave->realm_name, cred, invocation, + realm_samba_enroll_leave_async (leave->realm_name, cred, options, invocation, on_leave_do_deconfigure, g_object_ref (async)); break; default: diff --git a/service/realm-sssd-ipa.c b/service/realm-sssd-ipa.c index 2e85757..cd5fcea 100644 --- a/service/realm-sssd-ipa.c +++ b/service/realm-sssd-ipa.c @@ -23,6 +23,7 @@ #include "realm-ipa-discover.h" #include "realm-kerberos.h" #include "realm-kerberos-membership.h" +#include "realm-options.h" #include "realm-packages.h" #include "realm-provider.h" #include "realm-service.h" @@ -271,7 +272,6 @@ push_arg (GPtrArray *argv, static void realm_sssd_ipa_join_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, @@ -317,7 +317,7 @@ realm_sssd_ipa_join_async (RealmKerberosMembership *membership, } else { packages = IPA_PACKAGES; - if (flags & REALM_KERBEROS_ASSUME_PACKAGES) + if (realm_options_assume_packages (options)) packages = NO_PACKAGES; argv = g_ptr_array_new (); @@ -399,7 +399,6 @@ on_ipa_client_do_disable (GObject *source, static void realm_sssd_ipa_leave_async (RealmKerberosMembership *membership, RealmCredential *cred, - RealmKerberosFlags flags, GVariant *options, GDBusMethodInvocation *invocation, GAsyncReadyCallback callback, -- 1.8.1.4