From 147094fcc960af10c5c918d205dbfa739bcd436c Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmegg...@redhat.com>
Date: Fri, 13 Jan 2012 14:58:45 -0700
Subject: [PATCH] Ticket #1891 - Rewrite IPA plugins to take advantage of the 
single transaction

Make all ipa slapi plugins aware of slapi transactions.
Allow ipa slapi preoperation and postoperation plugins to be changed to
betxn plugins by changing the nsslapd-plugintype in the plugin config.
https://fedorahosted.org/freeipa/ticket/1891
---
 .../ipa-slapi-plugins/ipa-lockout/ipa_lockout.c    |   25 ++++--
 daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c  |   33 ++++++--
 .../ipa-pwd-extop/ipa_pwd_extop.c                  |   31 +++++--
 daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h   |   11 ++-
 .../ipa-pwd-extop/ipapwd_common.c                  |   40 +++++----
 .../ipa-pwd-extop/ipapwd_prepost.c                 |   72 ++++++++++++----
 daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c      |   89 +++++++++++++++-----
 7 files changed, 214 insertions(+), 87 deletions(-)

diff --git a/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c 
b/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c
index 
78e235dffa0e918513b63214c3d93389203c2248..4f964f7bc8c0bb165d3e5e2812eed6cc03e375d2
 100644
--- a/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c
+++ b/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c
@@ -259,6 +259,7 @@ static int ipalockout_postop(Slapi_PBlock *pb)
     char *lastfail = NULL;
     int tries = 0;
     int failure = 1;
+    void *txn = NULL;
 
     LOG_TRACE("--in-->\n");
 
@@ -302,8 +303,9 @@ static int ipalockout_postop(Slapi_PBlock *pb)
         goto done;
     }
 
-    ldrc = slapi_search_internal_get_entry(sdn, NULL, &target_entry,
-            getPluginID());
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
+    ldrc = slapi_search_internal_get_entry_ext(sdn, NULL, &target_entry,
+            getPluginID(), txn);
 
     if (ldrc != LDAP_SUCCESS) {
             LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", dn, ldrc);
@@ -326,8 +328,8 @@ static int ipalockout_postop(Slapi_PBlock *pb)
         goto done;
     } else {
         pdn = slapi_sdn_new_dn_byref(policy_dn);
-        ldrc = slapi_search_internal_get_entry(pdn, NULL, &policy_entry,
-                getPluginID());
+        ldrc = slapi_search_internal_get_entry_ext(pdn, NULL, &policy_entry,
+                getPluginID(), txn);
         slapi_sdn_free(&pdn);
         if (ldrc != LDAP_SUCCESS) {
             LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", policy_dn, 
ldrc);
@@ -412,6 +414,7 @@ static int ipalockout_postop(Slapi_PBlock *pb)
         getPluginID(), /* PluginID */
         0); /* Flags */
 
+        slapi_pblock_set(pbtm, SLAPI_TXN, txn);
         slapi_modify_internal_pb (pbtm);
         slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
 
@@ -419,8 +422,8 @@ static int ipalockout_postop(Slapi_PBlock *pb)
             LOG_TRACE("WARNING: modify error %d on entry '%s'\n",
                       rc, slapi_entry_get_dn_const(target_entry));
 
-            ldrc = slapi_search_internal_get_entry(sdn, NULL, &target_entry,
-                    getPluginID());
+            ldrc = slapi_search_internal_get_entry_ext(sdn, NULL, 
&target_entry,
+                    getPluginID(), txn);
 
             if (ldrc != LDAP_SUCCESS) {
                 LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", dn, ldrc);
@@ -491,6 +494,7 @@ static int ipalockout_preop(Slapi_PBlock *pb)
     time_t last_failed = 0;
     char *lastfail = NULL;
     char *unlock_time = NULL;
+    void *txn = NULL;
 
     LOG_TRACE("--in-->\n");
 
@@ -520,8 +524,9 @@ static int ipalockout_preop(Slapi_PBlock *pb)
         goto done;
     }
 
-    ldrc = slapi_search_internal_get_entry(sdn, NULL, &target_entry,
-            getPluginID());
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
+    ldrc = slapi_search_internal_get_entry_ext(sdn, NULL, &target_entry,
+            getPluginID(), txn);
 
     if (ldrc != LDAP_SUCCESS) {
         LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", dn, ldrc);
@@ -544,8 +549,8 @@ static int ipalockout_preop(Slapi_PBlock *pb)
         goto done;
     } else {
         pdn = slapi_sdn_new_dn_byref(policy_dn);
-        ldrc = slapi_search_internal_get_entry(pdn, NULL, &policy_entry,
-                getPluginID());
+        ldrc = slapi_search_internal_get_entry_ext(pdn, NULL, &policy_entry,
+                getPluginID(), txn);
         slapi_sdn_free(&pdn);
         if (ldrc != LDAP_SUCCESS) {
             LOG_FATAL("Failed to retrieve entry \"%s\": %d\n", policy_dn, 
ldrc);
diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c 
b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
index 
70a4ea82144829015e663c37212af7196a9ad72a..a9e6ffd22083813d9efbad9f0a0661b54c37d2f1
 100644
--- a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
+++ b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
@@ -201,9 +201,26 @@ ipamodrdn_init(Slapi_PBlock *pb)
 {
     int status = EOK;
     char *plugin_identity = NULL;
+    Slapi_Entry *plugin_entry = NULL;
+    char *plugin_type = NULL;
+    int postadd = SLAPI_PLUGIN_POST_ADD_FN;
+    int postmod = SLAPI_PLUGIN_POST_MODIFY_FN;
+    int postdel = SLAPI_PLUGIN_POST_DELETE_FN;
+    int postmdn = SLAPI_PLUGIN_POST_MODRDN_FN;
 
     LOG_TRACE("--in-->\n");
 
+    if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) 
&&
+        plugin_entry &&
+        (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, 
"nsslapd-plugintype")) &&
+        plugin_type && strstr(plugin_type, "betxn")) {
+        postadd = SLAPI_PLUGIN_BE_TXN_POST_ADD_FN;
+        postmod = SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN;
+        postdel = SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN;
+        postmdn = SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN;
+    }
+    slapi_ch_free_string(&plugin_type);
+
         /**
         * Store the plugin identity for later use.
         * Used for internal operations
@@ -221,13 +238,13 @@ ipamodrdn_init(Slapi_PBlock *pb)
                          (void *) ipamodrdn_start) != 0 ||
         slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
                          (void *) ipamodrdn_close) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+        slapi_pblock_set(pb, postadd,
                          (void *) ipamodrdn_config_check_post_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+        slapi_pblock_set(pb, postmdn,
                          (void *) ipamodrdn_post_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+        slapi_pblock_set(pb, postdel,
                          (void *) ipamodrdn_config_check_post_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+        slapi_pblock_set(pb, postmod,
                          (void *) ipamodrdn_config_check_post_op) != 0) {
         LOG_FATAL("failed to register plugin\n");
         status = EFAIL;
@@ -630,7 +647,7 @@ static int ipamodrdn_dn_is_config(char *dn)
 
 static int
 ipamodrdn_change_attr(struct configEntry *cfgentry,
-                      char *targetdn, const char *value)
+                      char *targetdn, const char *value, void *txn)
 {
     Slapi_PBlock *mod_pb = slapi_pblock_new();
     LDAPMod mod;
@@ -659,6 +676,7 @@ ipamodrdn_change_attr(struct configEntry *cfgentry,
     /* Perform the modify operation. */
     slapi_modify_internal_set_pb(mod_pb, targetdn, mods,
                                  0, 0, getPluginID(), 0);
+    slapi_pblock_set(mod_pb, SLAPI_TXN, txn);
     slapi_modify_internal_pb(mod_pb);
     slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
     if (ret != LDAP_SUCCESS) {
@@ -691,6 +709,7 @@ static int ipamodrdn_post_op(Slapi_PBlock *pb)
     Slapi_Attr *sattr = NULL;
     Slapi_Attr *tattr = NULL;
     int ret = LDAP_SUCCESS;
+    void *txn = NULL;
 
     LOG_TRACE("--in-->\n");
 
@@ -709,6 +728,8 @@ static int ipamodrdn_post_op(Slapi_PBlock *pb)
         goto done;
     }
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
+
     ipamodrdn_read_lock();
 
     if (!PR_CLIST_IS_EMPTY(ipamodrdn_global_config)) {
@@ -754,7 +775,7 @@ static int ipamodrdn_post_op(Slapi_PBlock *pb)
                 }
                 strval = slapi_value_get_string(val);
 
-                ret = ipamodrdn_change_attr(cfgentry, dn, strval);
+                ret = ipamodrdn_change_attr(cfgentry, dn, strval, txn);
                 if (ret != EOK) {
                     LOG_FATAL("Failed to set target attr %s for %s\n",
                               cfgentry->tattr, dn);
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c 
b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
index 
65c5834595f89aee8502347311f247be058c3416..064eabd499703f80a880370e857190f805a358c4
 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
@@ -67,7 +67,13 @@
 #define KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1"
 #define KEYTAB_RET_OID "2.16.840.1.113730.3.8.10.2"
 
-
+#ifdef USE_BETXN_PLUGINS
+#define IPAPWD_PREOP_TYPE "betxnpreoperation"
+#define IPAPWD_POSTOP_TYPE "betxnpostoperation"
+#else
+#define IPAPWD_PREOP_TYPE "preoperation"
+#define IPAPWD_POSTOP_TYPE "postoperation"
+#endif
 
 /* base DN of IPA realm tree */
 const char *ipa_realm_tree;
@@ -158,7 +164,9 @@ static int ipapwd_chpwop(Slapi_PBlock *pb, struct 
ipapwd_krbcfg *krbcfg)
        struct ipapwd_data pwdata;
        int is_krb, is_smb;
     char *principal = NULL;
+    void *txn = NULL;
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
        /* Get the ber value of the extended operation */
        slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
 
@@ -292,7 +300,7 @@ parse_req_done:
         }
 
         /* Now we have the DN, look for the entry */
-        ret = ipapwd_getEntry(dn, &targetEntry, attrlist);
+        ret = ipapwd_getEntry(dn, &targetEntry, attrlist, txn);
         /* If we can't find the entry, then that's an error */
         if (ret) {
                /* Couldn't find the entry, fail */
@@ -459,7 +467,7 @@ parse_req_done:
     }
 
        /* check the policy */
-       ret = ipapwd_CheckPolicy(&pwdata);
+       ret = ipapwd_CheckPolicy(&pwdata, txn);
        if (ret) {
                errMesg = "Password Fails to meet minimum strength criteria";
                if (ret == IPAPWD_POLICY_ERROR) {
@@ -474,7 +482,7 @@ parse_req_done:
        }
 
        /* Now we're ready to set the kerberos key material */
-       ret = ipapwd_SetPassword(krbcfg, &pwdata, is_krb);
+       ret = ipapwd_SetPassword(krbcfg, &pwdata, is_krb, txn);
        if (ret != LDAP_SUCCESS) {
                /* Failed to modify the password,
                 * e.g. because insufficient access allowed */
@@ -495,7 +503,7 @@ parse_req_done:
     } else {
         principal = slapi_ch_smprintf("root/admin@%s", krbcfg->realm);
     }
-    ipapwd_set_extradata(pwdata.dn, principal, pwdata.timeNow);
+    ipapwd_set_extradata(pwdata.dn, principal, pwdata.timeNow, txn);
 
        /* Free anything that we allocated above */
 free_and_return:
@@ -562,6 +570,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct 
ipapwd_krbcfg *krbcfg)
        time_t time_now = time(NULL);
        char *pw = NULL;
        Slapi_Value *objectclass;
+       void *txn = NULL;
 
        svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *));
        if (!svals) {
@@ -675,6 +684,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct 
ipapwd_krbcfg *krbcfg)
        /* get Entry by krbPrincipalName */
        filter = slapi_ch_smprintf("(krbPrincipalName=%s)", serviceName);
 
+       slapi_pblock_get(pb, SLAPI_TXN, &txn);
        pbte = slapi_pblock_new();
        slapi_search_internal_set_pb(pbte,
                bdn, scope, filter, attrlist, 0,
@@ -683,6 +693,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct 
ipapwd_krbcfg *krbcfg)
                ipapwd_plugin_id,
                0); /* Flags */
 
+       slapi_pblock_set(pbte, SLAPI_TXN, txn);
        /* do search the tree */
        ret = slapi_search_internal_pb(pbte);
        slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_RESULT, &res);
@@ -994,7 +1005,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct 
ipapwd_krbcfg *krbcfg)
        slapi_value_free(&objectclass);
 
        /* commit changes */
-       ret = ipapwd_apply_mods(slapi_entry_get_dn_const(targetEntry), smods);
+       ret = ipapwd_apply_mods(slapi_entry_get_dn_const(targetEntry), smods, 
txn);
 
        if (ret != LDAP_SUCCESS) {
                slapi_mods_free(&smods);
@@ -1004,7 +1015,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct 
ipapwd_krbcfg *krbcfg)
        slapi_mods_free(&smods);
 
     ipapwd_set_extradata(slapi_entry_get_dn_const(targetEntry),
-                         serviceName, time_now);
+                         serviceName, time_now, txn);
 
        /* Format of response
         *
@@ -1190,7 +1201,7 @@ static int ipapwd_start( Slapi_PBlock *pb )
         goto done;
     }
 
-    if (ipapwd_getEntry(config_dn, &config_entry, NULL) != LDAP_SUCCESS) {
+    if (ipapwd_getEntry(config_dn, &config_entry, NULL, NULL) != LDAP_SUCCESS) 
{
         LOG_FATAL("No config Entry extop?\n");
         ret = LDAP_SUCCESS;
         goto done;
@@ -1301,12 +1312,12 @@ int ipapwd_init( Slapi_PBlock *pb )
         return -1;
     }
 
-    slapi_register_plugin("preoperation", 1,
+    slapi_register_plugin(IPAPWD_PREOP_TYPE, 1,
                           "ipapwd_pre_init", ipapwd_pre_init,
                           "IPA pwd pre ops", NULL,
                           ipapwd_plugin_id);
 
-    slapi_register_plugin("postoperation", 1,
+    slapi_register_plugin(IPAPWD_POSTOP_TYPE, 1,
                           "ipapwd_post_init", ipapwd_post_init,
                           "IPA pwd post ops", NULL,
                           ipapwd_plugin_id);
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h 
b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
index 
787ed500a080674d4a8e1002468006b020eb1578..8f3c91f9c99d8df47fb8f826c0e9624db4134577
 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd.h
@@ -117,17 +117,18 @@ int ipapwd_entry_checks(Slapi_PBlock *pb, struct 
slapi_entry *e,
                         char *attr, int access);
 int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
                       struct ipapwd_krbcfg **config, int check_flags);
-int ipapwd_CheckPolicy(struct ipapwd_data *data);
-int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist);
+int ipapwd_CheckPolicy(struct ipapwd_data *data, void *txn);
+int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist, void 
*txn);
 int ipapwd_get_cur_kvno(Slapi_Entry *target);
 int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
-                       struct ipapwd_data *data, int is_krb);
+                       struct ipapwd_data *data, int is_krb, void *txn);
 Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods,
                                         struct ipapwd_data *data);
-int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods);
+int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods, void *txn);
 int ipapwd_set_extradata(const char *dn,
                          const char *principal,
-                         time_t unixtime);
+                         time_t unixtime,
+                         void *txn);
 void ipapwd_free_slapi_value_array(Slapi_Value ***svals);
 void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg);
 
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c 
b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c
index 
9e203be2763b13328e2d392c76e8545ba7ab549a..a502b240d7d7af5e3003a44553e3f1ec986812c3
 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_common.c
@@ -67,7 +67,7 @@ static const char *ipapwd_def_encsalts[] = {
     NULL
 };
 
-static struct ipapwd_krbcfg *ipapwd_getConfig(void)
+static struct ipapwd_krbcfg *ipapwd_getConfig(void *txn)
 {
     krb5_error_code krberr;
     struct ipapwd_krbcfg *config = NULL;
@@ -111,7 +111,7 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void)
     }
 
     /* get the Realm Container entry */
-    ret = ipapwd_getEntry(ipa_realm_dn, &realm_entry, NULL);
+    ret = ipapwd_getEntry(ipa_realm_dn, &realm_entry, NULL, txn);
     if (ret != LDAP_SUCCESS) {
         LOG_FATAL("No realm Entry?\n");
         goto free_and_error;
@@ -216,7 +216,7 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void)
     slapi_entry_free(realm_entry);
 
     /* get the Realm Container entry */
-    ret = ipapwd_getEntry(ipa_pwd_config_dn, &config_entry, NULL);
+    ret = ipapwd_getEntry(ipa_pwd_config_dn, &config_entry, NULL, txn);
     if (ret != LDAP_SUCCESS) {
         LOG_FATAL("No config Entry? Impossible!\n");
         goto free_and_error;
@@ -238,7 +238,7 @@ static struct ipapwd_krbcfg *ipapwd_getConfig(void)
     /* get the ipa etc/ipaConfig entry */
     config->allow_lm_hash = false;
     config->allow_nt_hash = false;
-    ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL);
+    ret = ipapwd_getEntry(ipa_etc_config_dn, &config_entry, NULL, txn);
     if (ret != LDAP_SUCCESS) {
         LOG_FATAL("No config Entry?\n");
         goto free_and_error;
@@ -331,7 +331,8 @@ static int ipapwd_rdn_count(const char *dn)
 
 int ipapwd_getPolicy(const char *dn,
                      Slapi_Entry *target,
-                     struct ipapwd_policy *policy)
+                     struct ipapwd_policy *policy,
+                     void *txn)
 {
     const char *krbPwdPolicyReference;
     const char *pdn;
@@ -390,7 +391,7 @@ int ipapwd_getPolicy(const char *dn,
                                  NULL, /* UniqueID */
                                  ipapwd_plugin_id,
                                  0); /* Flags */
-
+    slapi_pblock_set(pb, SLAPI_TXN, txn);
     /* do search the tree */
     ret = slapi_search_internal_pb(pb);
     slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
@@ -550,9 +551,11 @@ int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
     const Slapi_DN *psdn;
     Slapi_DN *sdn;
     char *dn = NULL;
+    void *txn = NULL;
 
     LOG_TRACE("=>\n");
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
 #ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
     if (check_flags & IPAPWD_CHECK_CONN_SECURE) {
        /* Allow password modify on all connections with a Security Strength
@@ -601,7 +604,7 @@ int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg,
     }
 
     /* get the kerberos context and master key */
-    *config = ipapwd_getConfig();
+    *config = ipapwd_getConfig(txn);
     if (NULL == *config) {
         LOG_FATAL("Error Retrieving Master Key");
         *errMesg = "Fatal Internal Error";
@@ -613,7 +616,7 @@ done:
 }
 
 /* check password strenght and history */
-int ipapwd_CheckPolicy(struct ipapwd_data *data)
+int ipapwd_CheckPolicy(struct ipapwd_data *data, void *txn)
 {
     struct ipapwd_policy pol = {0};
     time_t acct_expiration;
@@ -638,7 +641,7 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data)
     } else {
 
         /* find the entry with the password policy */
-        ret = ipapwd_getPolicy(data->dn, data->target, &pol);
+        ret = ipapwd_getPolicy(data->dn, data->target, &pol, txn);
         if (ret) {
             LOG_TRACE("No password policy, use defaults");
         }
@@ -685,7 +688,7 @@ int ipapwd_CheckPolicy(struct ipapwd_data *data)
  *  If found    : fills in slapi_entry structure and returns 0
  *  If NOT found : returns the search result as LDAP_NO_SUCH_OBJECT
  */
-int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist)
+int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char **attrlist, void 
*txn)
 {
     Slapi_DN *sdn;
     int search_result = 0;
@@ -693,8 +696,8 @@ int ipapwd_getEntry(const char *dn, Slapi_Entry **e2, char 
**attrlist)
     LOG_TRACE("=>\n");
 
     sdn = slapi_sdn_new_dn_byref(dn);
-    search_result = slapi_search_internal_get_entry(sdn, attrlist, e2,
-                                                    ipapwd_plugin_id);
+    search_result = slapi_search_internal_get_entry_ext(sdn, attrlist, e2,
+                                                        ipapwd_plugin_id, txn);
     if (search_result != LDAP_SUCCESS) {
         LOG_TRACE("No such entry-(%s), err (%d)\n", dn, search_result);
     }
@@ -760,7 +763,7 @@ next:
 
 /* Modify the Password attributes of the entry */
 int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
-                       struct ipapwd_data *data, int is_krb)
+                       struct ipapwd_data *data, int is_krb, void *txn)
 {
     int ret = 0;
     Slapi_Mods *smods = NULL;
@@ -889,7 +892,7 @@ int ipapwd_SetPassword(struct ipapwd_krbcfg *krbcfg,
      * fail if that's the case */
 
     /* commit changes */
-    ret = ipapwd_apply_mods(data->dn, smods);
+    ret = ipapwd_apply_mods(data->dn, smods, txn);
 
     LOG_TRACE("<= result: %d\n", ret);
 
@@ -954,7 +957,7 @@ done:
 /* Construct Mods pblock and perform the modify operation
  * Sets result of operation in SLAPI_PLUGIN_INTOP_RESULT
  */
-int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
+int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods, void *txn)
 {
     Slapi_PBlock *pb;
     int ret;
@@ -972,7 +975,7 @@ int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
                                  NULL, /* UniqueID */
                                  ipapwd_plugin_id, /* PluginID */
                                  0); /* Flags */
-
+    slapi_pblock_set(pb, SLAPI_TXN, txn);
     ret = slapi_modify_internal_pb(pb);
     if (ret) {
         LOG_TRACE("WARNING: modify error %d on entry '%s'\n", ret, dn);
@@ -994,7 +997,8 @@ int ipapwd_apply_mods(const char *dn, Slapi_Mods *mods)
 
 int ipapwd_set_extradata(const char *dn,
                          const char *principal,
-                         time_t unixtime)
+                         time_t unixtime,
+                         void *txn)
 {
     Slapi_Mods *smods;
     Slapi_Value *va[2] = { NULL };
@@ -1034,7 +1038,7 @@ int ipapwd_set_extradata(const char *dn,
 
     slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbExtraData", va);
 
-    ret = ipapwd_apply_mods(dn, smods);
+    ret = ipapwd_apply_mods(dn, smods, txn);
 
     slapi_value_free(&va[0]);
     slapi_mods_free(&smods);
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c 
b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
index 
961678acb4f7a3265b5b221acedb96a63cea5855..2428b01ccc72d2f32822d7632df93e4056d31be4
 100644
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipapwd_prepost.c
@@ -113,7 +113,7 @@ int ipapwd_ext_init(void)
 }
 
 
-static char *ipapwd_getIpaConfigAttr(const char *attr)
+static char *ipapwd_getIpaConfigAttr(const char *attr, void *txn)
 {
     /* check if migrtion is enabled */
     Slapi_Entry *entry = NULL;
@@ -128,7 +128,7 @@ static char *ipapwd_getIpaConfigAttr(const char *attr)
         goto done;
     }
 
-    ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list);
+    ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list, txn);
     if (ret) {
         LOG("failed to retrieve config entry: %s\n", dn);
         goto done;
@@ -166,6 +166,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
     int is_repl_op, is_root, is_krb, is_smb;
     int ret;
     int rc = LDAP_SUCCESS;
+    void *txn = NULL;
 
     LOG_TRACE("=>\n");
 
@@ -192,6 +193,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
        return 0;
     }
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
     /* Ok this is interesting,
      * Check this is a clear text password, or refuse operation */
     if ('{' == userpw[0]) {
@@ -225,7 +227,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
                 /* we don't have access to the clear text password;
                  * let it slide if migration is enabled, but don't
                  * generate kerberos keys */
-                char *enabled = ipapwd_getIpaConfigAttr("ipamigrationenabled");
+                char *enabled = ipapwd_getIpaConfigAttr("ipamigrationenabled", 
txn);
                 if (NULL == enabled) {
                     LOG("no ipaMigrationEnabled in config, assuming FALSE\n");
                 } else if (0 == strcmp(enabled, "TRUE")) {
@@ -300,7 +302,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
     pwdop->pwdata.timeNow = time(NULL);
     pwdop->pwdata.target = e;
 
-    ret = ipapwd_CheckPolicy(&pwdop->pwdata);
+    ret = ipapwd_CheckPolicy(&pwdop->pwdata, txn);
     if (ret) {
         errMesg = "Password Fails to meet minimum strength criteria";
         rc = LDAP_CONSTRAINT_VIOLATION;
@@ -402,6 +404,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
     int has_history = 0;
     int gen_krb_keys = 0;
     int ret, rc;
+    void *txn = NULL;
 
     LOG_TRACE( "=>\n");
 
@@ -418,6 +421,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
         goto done;
     }
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
     /* grab the mods - we'll put them back later with
      * our modifications appended
      */
@@ -504,7 +508,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
          *
          slapi_pblock_get( pb, SLAPI_MODIFY_EXISTING_ENTRY, &e);
          */
-        ret = slapi_search_internal_get_entry(tmp_dn, 0, &e, ipapwd_plugin_id);
+        ret = slapi_search_internal_get_entry_ext(tmp_dn, 0, &e, 
ipapwd_plugin_id, txn);
         slapi_sdn_free(&tmp_dn);
         if (ret != LDAP_SUCCESS) {
             LOG("Failed to retrieve entry?!\n");
@@ -738,7 +742,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
      * policies have been properly checked already, so we check them only
      * if no krb keys are available */
     if (has_krb_keys == 0) {
-        ret = ipapwd_CheckPolicy(&pwdop->pwdata);
+        ret = ipapwd_CheckPolicy(&pwdop->pwdata, txn);
         if (ret) {
             errMesg = "Password Fails to meet minimum strength criteria";
             rc = LDAP_CONSTRAINT_VIOLATION;
@@ -833,6 +837,7 @@ static int ipapwd_post_op(Slapi_PBlock *pb)
     struct ipapwd_krbcfg *krbcfg = NULL;
     char *principal = NULL;
     Slapi_Value *ipahost;
+    void *txn = NULL;
 
     LOG_TRACE("=>\n");
 
@@ -870,6 +875,8 @@ static int ipapwd_post_op(Slapi_PBlock *pb)
         return 0;
     }
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
+
     /* prepare changes that can be made only as root */
     smods = slapi_mods_new();
 
@@ -878,9 +885,9 @@ static int ipapwd_post_op(Slapi_PBlock *pb)
     if (IPAPWD_OP_MOD == pwdop->pwd_op && !pwdop->skip_history) {
         Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(pwdop->pwdata.dn);
         if (tmp_dn) {
-            ret = slapi_search_internal_get_entry(tmp_dn, 0,
-                                                  &pwdop->pwdata.target,
-                                                  ipapwd_plugin_id);
+            ret = slapi_search_internal_get_entry_ext(tmp_dn, 0,
+                                                      &pwdop->pwdata.target,
+                                                      ipapwd_plugin_id, txn);
             slapi_sdn_free(&tmp_dn);
             if (ret != LDAP_SUCCESS) {
                 LOG("Failed to retrieve entry?!\n");
@@ -934,7 +941,7 @@ static int ipapwd_post_op(Slapi_PBlock *pb)
         slapi_value_free(&ipahost);
     }
 
-    ret = ipapwd_apply_mods(pwdop->pwdata.dn, smods);
+    ret = ipapwd_apply_mods(pwdop->pwdata.dn, smods, txn);
     if (ret)
         LOG("Failed to set additional password attributes in the post-op!\n");
 
@@ -945,7 +952,7 @@ static int ipapwd_post_op(Slapi_PBlock *pb)
         } else {
             principal = slapi_ch_smprintf("root/admin@%s", krbcfg->realm);
         }
-        ipapwd_set_extradata(pwdop->pwdata.dn, principal, 
pwdop->pwdata.timeNow);
+        ipapwd_set_extradata(pwdop->pwdata.dn, principal, 
pwdop->pwdata.timeNow, txn);
     }
 
 done:
@@ -978,6 +985,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
     Slapi_Value *objectclass;
     int method; /* authentication method */
     int ret = 0;
+    void *txn = NULL;
 
     LOG_TRACE("=>\n");
 
@@ -994,6 +1002,8 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
     if (method != LDAP_AUTH_SIMPLE)
         goto done;
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
+
     /* list of attributes to retrieve */
     const char *attrs_list[] = {SLAPI_USERPWD_ATTR, "krbprincipalkey", "uid",
                                 "krbprincipalname", "objectclass",
@@ -1001,7 +1011,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
                                 NULL};
 
     /* retrieve user entry */
-    ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list);
+    ret = ipapwd_getEntry(dn, &entry, (char **) attrs_list, txn);
     if (ret) {
         LOG("failed to retrieve user entry: %s\n", dn);
         goto done;
@@ -1102,7 +1112,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
     }
 
     /* check password policy */
-    ret = ipapwd_CheckPolicy(&pwdata);
+    ret = ipapwd_CheckPolicy(&pwdata, txn);
     if (ret) {
         /* Password fails to meet IPA password policy,
          * force user to change his password next time he logs in. */
@@ -1112,7 +1122,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
     }
 
     /* generate kerberos keys */
-    ret = ipapwd_SetPassword(krbcfg, &pwdata, 1);
+    ret = ipapwd_SetPassword(krbcfg, &pwdata, 1, txn);
     if (ret) {
         LOG("failed to set kerberos key for user entry: %s\n", dn);
         goto done;
@@ -1135,12 +1145,25 @@ done:
 int ipapwd_pre_init(Slapi_PBlock *pb)
 {
     int ret;
+    int addfn = SLAPI_PLUGIN_PRE_ADD_FN;
+    int modfn = SLAPI_PLUGIN_PRE_MODIFY_FN;
+    Slapi_Entry *plugin_entry = NULL;
+    char *plugin_type = NULL;
+
+    if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) 
&&
+        plugin_entry &&
+        (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, 
"nsslapd-plugintype")) &&
+        plugin_type && strstr(plugin_type, "betxn")) {
+        addfn = SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN;
+        modfn = SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN;
+    }
+    slapi_ch_free_string(&plugin_type);
 
     ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void 
*)&ipapwd_plugin_desc);
     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_BIND_FN, (void 
*)ipapwd_pre_bind);
-    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void 
*)ipapwd_pre_add);
-    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void 
*)ipapwd_pre_mod);
+    if (!ret) ret = slapi_pblock_set(pb, addfn, (void *)ipapwd_pre_add);
+    if (!ret) ret = slapi_pblock_set(pb, modfn, (void *)ipapwd_pre_mod);
 
     return ret;
 }
@@ -1149,11 +1172,24 @@ int ipapwd_pre_init(Slapi_PBlock *pb)
 int ipapwd_post_init(Slapi_PBlock *pb)
 {
     int ret;
+    int addfn = SLAPI_PLUGIN_POST_ADD_FN;
+    int modfn = SLAPI_PLUGIN_POST_MODIFY_FN;
+    Slapi_Entry *plugin_entry = NULL;
+    char *plugin_type = NULL;
+
+    if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) 
&&
+        plugin_entry &&
+        (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, 
"nsslapd-plugintype")) &&
+        plugin_type && strstr(plugin_type, "betxn")) {
+        addfn = SLAPI_PLUGIN_BE_TXN_POST_ADD_FN;
+        modfn = SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN;
+    }
+    slapi_ch_free_string(&plugin_type);
 
     ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
     if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void 
*)&ipapwd_plugin_desc);
-    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, (void 
*)ipapwd_post_op);
-    if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void 
*)ipapwd_post_op);
+    if (!ret) ret = slapi_pblock_set(pb, addfn, (void *)ipapwd_post_op);
+    if (!ret) ret = slapi_pblock_set(pb, modfn, (void *)ipapwd_post_op);
 
     return ret;
 }
diff --git a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c 
b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c
index 
5430de4c99d9c472a1dfbf52fd5d2374d7a07380..e7e661875dd04921739099b40093383724d9b190
 100644
--- a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c
+++ b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c
@@ -212,9 +212,24 @@ ipauuid_init(Slapi_PBlock *pb)
 {
     int status = EOK;
     char *plugin_identity = NULL;
+    Slapi_Entry *plugin_entry = NULL;
+    char *plugin_type = NULL;
+    int preadd = SLAPI_PLUGIN_PRE_ADD_FN;
+    int premod = SLAPI_PLUGIN_PRE_MODIFY_FN;
+    int is_betxn_plugin = 0;
 
     LOG_TRACE("--in-->\n");
 
+    if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) 
&&
+        plugin_entry &&
+        (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, 
"nsslapd-plugintype")) &&
+        plugin_type && strstr(plugin_type, "betxn")) {
+        preadd = SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN;
+        premod = SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN;
+        is_betxn_plugin = 1;
+    }
+    slapi_ch_free_string(&plugin_type);
+
         /**
         * Store the plugin identity for later use.
         * Used for internal operations
@@ -232,10 +247,15 @@ ipauuid_init(Slapi_PBlock *pb)
                          (void *) ipauuid_close) != 0 ||
         slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
                          (void *) &pdesc) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+        slapi_pblock_set(pb, premod,
                          (void *) ipauuid_mod_pre_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
-                         (void *) ipauuid_add_pre_op) != 0 ||
+        slapi_pblock_set(pb, preadd,
+                         (void *) ipauuid_add_pre_op) != 0) {
+        LOG_FATAL("failed to register plugin\n");
+        status = EFAIL;
+    }
+
+    if ((status == EOK) && !is_betxn_plugin &&
         /* internal preoperation */
         slapi_register_plugin("internalpreoperation",  /* op type */
                               1,        /* Enabled */
@@ -244,19 +264,28 @@ ipauuid_init(Slapi_PBlock *pb)
                               IPAUUID_INT_PREOP_DESC,      /* plugin desc */
                               NULL,     /* ? */
                               plugin_identity   /* access control */
-        ) ||
+        )) {
+        LOG_FATAL("failed to register internalpreoperation plugin\n");
+        status = EFAIL;
+    }
+
+    if (status == EOK) {
+        plugin_type = "postoperation";
+        if (is_betxn_plugin) {
+            plugin_type = "betxnpostoperation";
+        }
         /* the config change checking post op */
-        slapi_register_plugin("postoperation",  /* op type */
-                              1,        /* Enabled */
-                              "ipauuid_init",   /* this function desc */
-                              ipauuid_postop_init,  /* init func for post op */
-                              IPAUUID_POSTOP_DESC,      /* plugin desc */
-                              NULL,     /* ? */
-                              plugin_identity   /* access control */
-        )
-        ) {
-        LOG_FATAL("failed to register plugin\n");
-        status = EFAIL;
+        if (slapi_register_plugin(plugin_type,  /* op type */
+                                  1,        /* Enabled */
+                                  "ipauuid_init",   /* this function desc */
+                                  ipauuid_postop_init,  /* init func for post 
op */
+                                  IPAUUID_POSTOP_DESC,      /* plugin desc */
+                                  NULL,     /* ? */
+                                  plugin_identity   /* access control */
+        )) {
+            LOG_FATAL("failed to register postoperation plugin\n");
+            status = EFAIL;
+        }
     }
 
     LOG_TRACE("<--out--\n");
@@ -286,18 +315,35 @@ static int
 ipauuid_postop_init(Slapi_PBlock *pb)
 {
     int status = EOK;
+    Slapi_Entry *plugin_entry = NULL;
+    char *plugin_type = NULL;
+    int postadd = SLAPI_PLUGIN_POST_ADD_FN;
+    int postmod = SLAPI_PLUGIN_POST_MODIFY_FN;
+    int postdel = SLAPI_PLUGIN_POST_DELETE_FN;
+    int postmdn = SLAPI_PLUGIN_POST_MODRDN_FN;
+
+    if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) 
&&
+        plugin_entry &&
+        (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, 
"nsslapd-plugintype")) &&
+        plugin_type && strstr(plugin_type, "betxn")) {
+        postadd = SLAPI_PLUGIN_BE_TXN_POST_ADD_FN;
+        postmod = SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN;
+        postdel = SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN;
+        postmdn = SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN;
+    }
+    slapi_ch_free_string(&plugin_type);
 
     if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
                          SLAPI_PLUGIN_VERSION_01) != 0 ||
         slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
                          (void *) &pdesc) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+        slapi_pblock_set(pb, postadd,
                          (void *) ipauuid_config_check_post_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+        slapi_pblock_set(pb, postmdn,
                          (void *) ipauuid_config_check_post_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+        slapi_pblock_set(pb, postdel,
                          (void *) ipauuid_config_check_post_op) != 0 ||
-        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+        slapi_pblock_set(pb, postmod,
                          (void *) ipauuid_config_check_post_op) != 0) {
         LOG_FATAL("failed to register plugin\n");
         status = EFAIL;
@@ -766,6 +812,7 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype)
     bool set_attr;
     int is_repl_op;
     int is_config_dn;
+    void *txn = NULL;
 
     LOG_TRACE("--in-->\n");
 
@@ -798,6 +845,8 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype)
         goto done;
     }
 
+    slapi_pblock_get(pb, SLAPI_TXN, &txn);
+
     if (LDAP_CHANGETYPE_ADD == modtype) {
         slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
     } else {
@@ -814,7 +863,7 @@ static int ipauuid_pre_op(Slapi_PBlock *pb, int modtype)
          */
         Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn);
         if (tmp_dn) {
-            ret = slapi_search_internal_get_entry(tmp_dn, NULL, &e, 
getPluginID());
+            ret = slapi_search_internal_get_entry_ext(tmp_dn, NULL, &e, 
getPluginID(), txn);
             slapi_sdn_free(&tmp_dn);
 
             if (ret) {
-- 
1.7.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to