| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952 |
- /** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2013 Red Hat, Inc.
- * All rights reserved.
- *
- * License: GPL (version 3 or any later version).
- * See LICENSE for details.
- * END COPYRIGHT BLOCK **/
- #include "sync.h"
- static SyncOpInfo *new_SyncOpInfo(int flag, PRThread *tid, Sync_Cookie *cookie);
- static int sync_extension_type;
- static int sync_extension_handle;
- static PRBool allow_openldap_compat;
- static SyncOpInfo *sync_get_operation_extension(Slapi_PBlock *pb);
- static void sync_set_operation_extension(Slapi_PBlock *pb, SyncOpInfo *spec);
- static int sync_find_ref_by_uuid(Sync_UpdateNode *updates, int stop, char *uniqueid);
- static void sync_free_update_nodes(Sync_UpdateNode **updates, int count);
- Slapi_Entry *sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry);
- static int sync_feature_allowed(Slapi_PBlock *pb);
- static int
- sync_feature_allowed(Slapi_PBlock *pb)
- {
- int isroot = 0;
- int ldapcode = LDAP_SUCCESS;
- slapi_pblock_get(pb, SLAPI_REQUESTOR_ISROOT, &isroot);
- if (!isroot) {
- char *dn;
- Slapi_Entry *feature = NULL;
- /* Fetch the feature entry and see if the requestor is allowed access. */
- dn = slapi_ch_smprintf("dn: oid=%s,cn=features,cn=config", LDAP_CONTROL_SYNC);
- if ((feature = slapi_str2entry(dn, 0)) != NULL) {
- char *dummy_attr = "1.1";
- ldapcode = slapi_access_allowed(pb, feature, dummy_attr, NULL, SLAPI_ACL_READ);
- }
- /* If the feature entry does not exist, deny use of the control. Only
- * the root DN will be allowed to use the control in this case. */
- if ((feature == NULL) || (ldapcode != LDAP_SUCCESS)) {
- ldapcode = LDAP_INSUFFICIENT_ACCESS;
- }
- slapi_ch_free((void **)&dn);
- slapi_entry_free(feature);
- }
- return (ldapcode);
- }
- int
- sync_srch_refresh_pre_search(Slapi_PBlock *pb)
- {
- LDAPControl **requestcontrols;
- struct berval *psbvp;
- Sync_Cookie *client_cookie = NULL;
- Sync_Cookie *session_cookie = NULL;
- int rc = 0;
- int sync_persist = 0;
- PRThread *tid = NULL;
- int entries_sent = 0;
- slapi_pblock_get(pb, SLAPI_REQCONTROLS, &requestcontrols);
- if (slapi_control_present(requestcontrols, LDAP_CONTROL_SYNC, &psbvp, NULL)) {
- char *cookie = NULL;
- int32_t mode = 1;
- int32_t refresh = 0;
- PRBool cookie_refresh = PR_FALSE;
- if (sync_parse_control_value(psbvp, &mode,
- &refresh, &cookie) != LDAP_SUCCESS) {
- rc = 1;
- goto error_return;
- } else {
- /* control is valid, check if usere is allowed to perform sync searches */
- rc = sync_feature_allowed(pb);
- if (rc) {
- sync_result_err(pb, rc, NULL);
- goto error_return;
- }
- }
- if (mode == 1 || mode == 3) {
- /*
- * OpenLDAP violates rfc4533 by sending a "rid=" in it's initial cookie sync, even
- * when using their changelog mode. As a result, we parse the cookie to handle this
- * shenangians to determine if this is valid.
- */
- client_cookie = sync_cookie_parse(cookie, &cookie_refresh, &allow_openldap_compat);
- /*
- * we need to return a cookie in the result message
- * indicating a state to be used in future sessions
- * as starting point - create it now. We need to provide
- * the client_cookie so we understand if we are in
- * openldap mode or not, and to get the 'rid' of the
- * consumer.
- */
- session_cookie = sync_cookie_create(pb, client_cookie);
- if (session_cookie == NULL) {
- /* In some rare case access to the retroCL fails.
- * It can happen when retroCL is just created and
- * does not contain any record.
- * As we are not able to retrieve the last changenumber
- * just return a failure.
- * Another option would be to set cookie_change_info=0
- * if we can not retrieve any record in retroCL
- * (in sync_cookie_create)
- */
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
- "sync_srch_refresh_pre_search - fails to create a session cookie\n");
- rc = LDAP_OPERATIONS_ERROR;
- sync_result_err(pb, rc, "Unable to create a session cookie: last changelog record unreachable");
- goto error_return;
- }
- /*
- * if mode is persist we need to setup the persit handler
- * to catch the mods while the refresh is done
- */
- if (mode == 3) {
- if (client_cookie && client_cookie->openldap_compat == PR_TRUE) {
- /* We don't allow this. */
- rc = LDAP_UNWILLING_TO_PERFORM;
- sync_result_err(pb, rc, "Invalid session state, openldap compat not supported with persistence");
- goto error_return;
- }
- /* Launch the thread. */
- tid = sync_persist_add(pb);
- if (tid)
- sync_persist = 1;
- else {
- rc = LDAP_UNWILLING_TO_PERFORM;
- sync_result_err(pb, rc, "Too many active synchronization sessions");
- goto error_return;
- }
- }
- /*
- * now handle the refresh request
- * there are two scenarios
- * 1. no cookie is provided this means send all entries matching the search request
- * 2. a cookie is provided: send all entries changed since the cookie was issued
- * -- return an error if the cookie is invalid
- * -- return e-syncRefreshRequired if the data referenced in the cookie are no
- * longer in the history
- */
- if (!cookie_refresh) {
- if (sync_cookie_isvalid(client_cookie, session_cookie)) {
- rc = sync_refresh_update_content(pb, client_cookie, session_cookie);
- if (rc == 0) {
- entries_sent = 1;
- }
- if (sync_persist) {
- rc = sync_intermediate_msg(pb, LDAP_TAG_SYNC_REFRESH_DELETE, session_cookie, NULL);
- } else {
- rc = sync_result_msg(pb, session_cookie);
- }
- } else {
- rc = E_SYNC_REFRESH_REQUIRED;
- sync_result_err(pb, rc, "Invalid session cookie");
- }
- } else {
- rc = sync_refresh_initial_content(pb, sync_persist, tid, session_cookie);
- if (rc == 0 && !sync_persist) {
- /* maintained in postop code */
- session_cookie = NULL;
- }
- /* if persis it will be handed over to persist code */
- }
- if (rc) {
- if (sync_persist) {
- sync_persist_terminate(tid);
- }
- goto error_return;
- } else if (sync_persist) {
- Slapi_Operation *operation;
- slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
- if (client_cookie) {
- rc = sync_persist_startup(tid, session_cookie);
- }
- if (rc == 0) {
- session_cookie = NULL; /* maintained in persist code */
- slapi_operation_set_flag(operation, OP_FLAG_SYNC_PERSIST);
- }
- }
- } else {
- /* unknown mode, return an error */
- rc = 1;
- }
- error_return:
- sync_cookie_free(&client_cookie);
- sync_cookie_free(&session_cookie);
- slapi_ch_free((void **)&cookie);
- }
- /* if we sent the entries
- * return "error" to abort normal search
- */
- if (entries_sent > 0) {
- return (1);
- } else {
- return (rc);
- }
- }
- int
- sync_srch_refresh_post_search(Slapi_PBlock *pb)
- {
- int rc = 0;
- SyncOpInfo *info = sync_get_operation_extension(pb);
- if (!info) {
- return (0); /* nothing to do */
- }
- if (info->send_flag & SYNC_FLAG_SEND_INTERMEDIATE) {
- rc = sync_intermediate_msg(pb, LDAP_TAG_SYNC_REFRESH_DELETE, info->cookie, NULL);
- /* the refresh phase is over, now the post op
- * plugins will create the state control
- * depending on the operation type, reset flag
- */
- info->send_flag &= ~SYNC_FLAG_ADD_STATE_CTRL;
- /* activate the persistent phase thread*/
- sync_persist_startup(info->tid, info->cookie);
- }
- if (info->send_flag & SYNC_FLAG_ADD_DONE_CTRL) {
- LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl *));
- char *cookiestr = sync_cookie2str(info->cookie);
- /*
- * RFC4533
- * If refreshDeletes of syncDoneValue is FALSE, the new copy includes
- * all changed entries returned by the reissued Sync Operation, as well
- * as all unchanged entries identified as being present by the reissued
- * Sync Operation, but whose content is provided by the previous Sync
- * Operation. The unchanged entries not identified as being present are
- * deleted from the client content. They had been either deleted,
- * moved, or otherwise scoped-out from the content.
- *
- * If refreshDeletes of syncDoneValue is TRUE, the new copy includes all
- * changed entries returned by the reissued Sync Operation, as well as
- * all other entries of the previous copy except for those that are
- * identified as having been deleted from the content.
- *
- * Confused yet? Don't worry so am I. I have no idea what this means or
- * what it will do. The best I can see from wireshark is that if refDel is
- * false, then anything *not* present will be purged from the change that
- * was supplied. Which probably says a lot about how confusing syncrepl is
- * that we've hardcoded this to false for literally years and no one has
- * complained, probably because every client is broken in their own ways
- * as no one can actually interpret that dense statement above.
- *
- * Point is, if we set refresh to true for openldap mode, it works, and if
- * it's false, the moment we send a single intermediate delete message, we
- * delete literally everything 🔥.
- *
- * See README.md for more about how this works.
- */
- if (info->cookie->openldap_compat) {
- sync_create_sync_done_control(&ctrl[0], 1, cookiestr);
- } else {
- sync_create_sync_done_control(&ctrl[0], 0, cookiestr);
- }
- slapi_pblock_set(pb, SLAPI_RESCONTROLS, ctrl);
- slapi_ch_free((void **)&cookiestr);
- }
- return (rc);
- }
- int
- sync_srch_refresh_pre_entry(Slapi_PBlock *pb)
- {
- int rc = 0;
- SyncOpInfo *info = sync_get_operation_extension(pb);
- if (!info) {
- rc = 0; /* nothing to do */
- } else if (info->send_flag & SYNC_FLAG_ADD_STATE_CTRL) {
- Slapi_Entry *e;
- PRBool openldap_compat = PR_FALSE;
- if (info->cookie) {
- openldap_compat = info->cookie->openldap_compat;
- }
- slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &e);
- LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl *));
- rc = sync_create_state_control(e, &ctrl[0], LDAP_SYNC_ADD, NULL, openldap_compat);
- slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, ctrl);
- }
- return (rc);
- }
- int
- sync_srch_refresh_pre_result(Slapi_PBlock *pb)
- {
- SyncOpInfo *info = sync_get_operation_extension(pb);
- if (!info) {
- return 0; /* nothing to do */
- }
- if (info->send_flag & SYNC_FLAG_NO_RESULT) {
- return (1);
- } else {
- return (0);
- }
- }
- static void
- sync_free_update_nodes(Sync_UpdateNode **updates, int count)
- {
- int i;
- for (i = 0; i < count; i++) {
- /* ch free checks for null for us. */
- slapi_ch_free((void **)&((*updates)[i].upd_uuid));
- slapi_ch_free((void **)&((*updates)[i].upd_euuid));
- if ((*updates)[i].upd_e) {
- slapi_entry_free((*updates)[i].upd_e);
- }
- }
- slapi_ch_free((void **)updates);
- }
- int
- sync_refresh_update_content(Slapi_PBlock *pb, Sync_Cookie *client_cookie, Sync_Cookie *server_cookie)
- {
- Slapi_PBlock *seq_pb;
- char *filter;
- Sync_CallBackData cb_data;
- int rc = LDAP_SUCCESS;
- PR_ASSERT(client_cookie);
- /*
- * We have nothing to send, move along.
- * Should be caught by cookie is valid though if the server < client, but if
- * they are equal, we return.
- */
- PR_ASSERT(server_cookie->cookie_change_info >= client_cookie->cookie_change_info);
- if (server_cookie->cookie_change_info == client_cookie->cookie_change_info) {
- return rc;
- }
- int chg_count = (server_cookie->cookie_change_info - client_cookie->cookie_change_info) + 1;
- PR_ASSERT(chg_count > 0);
- cb_data.cb_updates = (Sync_UpdateNode *)slapi_ch_calloc(chg_count, sizeof(Sync_UpdateNode));
- seq_pb = slapi_pblock_new();
- slapi_pblock_init(seq_pb);
- cb_data.orig_pb = pb;
- cb_data.change_start = client_cookie->cookie_change_info;
- cb_data.openldap_compat = server_cookie->openldap_compat;
- /*
- * The client has already seen up to AND including change_info, so this should
- * should reflect that. originally was:
- *
- * filter = slapi_ch_smprintf("(&(changenumber>=%lu)(changenumber<=%lu))",
- * client_cookie->cookie_change_info,
- * server_cookie->cookie_change_info);
- *
- * which would create a situation where if the previous cn was say 5, and the next
- * is 6, we'd get both 5 and 6, even though the client has already seen 5. But worse
- * if 5 was an "add" of the entry, and 6 was a "delete" of the same entry then sync
- * would over-optimise and remove the sync value because it things the add/delete was
- * in the same operation so we'd never send it. But the client HAD seen the add, and
- * now we'd never send the delete so this would be a bug. This created some confusion
- * for me in the tests, but the sync repl tests now correctly work and reflect the behaviour
- * expected.
- */
- if (server_cookie->openldap_compat) {
- /* In openldap compat we only want items that have an entryuuid, else we can't sync them */
- filter = slapi_ch_smprintf("(&(changenumber>=%lu)(changenumber<=%lu)(" CL_ATTR_ENTRYUUID "=*))",
- client_cookie->cookie_change_info + 1,
- server_cookie->cookie_change_info);
- } else {
- filter = slapi_ch_smprintf("(&(changenumber>=%lu)(changenumber<=%lu))",
- client_cookie->cookie_change_info + 1,
- server_cookie->cookie_change_info);
- }
- slapi_search_internal_set_pb(
- seq_pb,
- CL_SRCH_BASE,
- LDAP_SCOPE_ONE,
- filter,
- NULL,
- 0,
- NULL, NULL,
- plugin_get_default_component_id(),
- 0);
- rc = slapi_search_internal_callback_pb(
- seq_pb, &cb_data, NULL, sync_read_entry_from_changelog, NULL);
- slapi_pblock_destroy(seq_pb);
- /* Now send the deleted entries in a sync info message
- * and the modified entries as single entries
- */
- sync_send_deleted_entries(pb, cb_data.cb_updates, chg_count, server_cookie);
- sync_send_modified_entries(pb, cb_data.cb_updates, chg_count, server_cookie);
- sync_free_update_nodes(&cb_data.cb_updates, chg_count);
- slapi_ch_free((void **)&filter);
- return (rc);
- }
- int
- sync_refresh_initial_content(Slapi_PBlock *pb, int sync_persist, PRThread *tid, Sync_Cookie *sc)
- {
- /* the entries will be sent in the normal search process, but
- * - a control has to be sent with each entry
- * if sync persist:
- * - an intermediate response has to be sent
- * - no result message must be sent
- *
- * else
- * - a result message with a sync done control has to be sent
- *
- * setup on operation extension to take care of in
- * pre_entry, pre_result and post_search plugins
- */
- SyncOpInfo *info;
- if (sc->openldap_compat == PR_TRUE) {
- /*
- * If this is true we need to adjust the filter to
- * include a wrapping entryuuid condition. This is
- * because openldap demands entryuuid == syncuuid so
- * we must only send entries with an entryuuid.
- */
- struct slapi_filter *filter = NULL;
- slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, (void *)&filter);
- PR_ASSERT(filter);
- /* We need to alloc this due to how str2filter manips the str. If it's
- * static we cause a segfault because it's in a protected section.
- */
- char *buf = slapi_ch_strdup("(entryUUID=*)");
- struct slapi_filter *euuid_filter = slapi_str2filter(buf);
- PR_ASSERT(euuid_filter);
- struct slapi_filter *wrapped_filter = slapi_filter_join(LDAP_FILTER_AND, filter, euuid_filter);
- PR_ASSERT(wrapped_filter);
- slapi_pblock_set(pb, SLAPI_SEARCH_FILTER, (void *)wrapped_filter);
- slapi_ch_free_string(&buf);
- }
- if (sync_persist) {
- info = new_SyncOpInfo(SYNC_FLAG_ADD_STATE_CTRL |
- SYNC_FLAG_SEND_INTERMEDIATE |
- SYNC_FLAG_NO_RESULT,
- tid,
- sc);
- } else {
- info = new_SyncOpInfo(SYNC_FLAG_ADD_STATE_CTRL |
- SYNC_FLAG_ADD_DONE_CTRL,
- tid,
- sc);
- }
- sync_set_operation_extension(pb, info);
- return (0);
- }
- static int
- sync_str2chgreq(char *chgtype)
- {
- if (chgtype == NULL) {
- return (-1);
- }
- if (strcasecmp(chgtype, "add") == 0) {
- return (LDAP_REQ_ADD);
- } else if (strcasecmp(chgtype, "modify") == 0) {
- return (LDAP_REQ_MODIFY);
- } else if (strcasecmp(chgtype, "modrdn") == 0) {
- return (LDAP_REQ_MODRDN);
- } else if (strcasecmp(chgtype, "delete") == 0) {
- return (LDAP_REQ_DELETE);
- } else {
- return (-1);
- }
- }
- static char *
- sync_get_attr_value_from_entry(Slapi_Entry *cl_entry, char *attrtype)
- {
- Slapi_Value *sval = NULL;
- const struct berval *value;
- char *strvalue = NULL;
- if (NULL != cl_entry) {
- Slapi_Attr *chattr = NULL;
- sval = NULL;
- value = NULL;
- if (slapi_entry_attr_find(cl_entry, attrtype, &chattr) == 0) {
- slapi_attr_first_value(chattr, &sval);
- if (NULL != sval) {
- value = slapi_value_get_berval(sval);
- if (NULL != value && NULL != value->bv_val &&
- '\0' != value->bv_val[0]) {
- strvalue = slapi_ch_strdup(value->bv_val);
- }
- }
- }
- }
- return (strvalue);
- }
- static int
- sync_find_ref_by_uuid(Sync_UpdateNode *updates, int stop, char *uniqueid)
- {
- int rc = -1;
- int i;
- for (i = 0; i < stop; i++) {
- if (updates[i].upd_uuid && (0 == strcmp(uniqueid, updates[i].upd_uuid))) {
- rc = i;
- break;
- }
- }
- return (rc);
- }
- static int
- sync_is_entry_in_scope(Slapi_PBlock *pb, Slapi_Entry *db_entry)
- {
- Slapi_Filter *origfilter;
- slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &origfilter);
- if (db_entry &&
- sync_is_active(db_entry, pb) &&
- (slapi_vattr_filter_test(pb, db_entry, origfilter, 1) == 0)) {
- return (1);
- } else {
- return (0);
- }
- }
- Slapi_Entry *
- sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry)
- {
- Slapi_Entry *db_entry = NULL;
- char *entrydn = NULL;
- char *uniqueid = NULL;
- entrydn = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_ENTRYDN);
- uniqueid = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_UNIQUEID);
- /* when the Retro CL can provide the deleted entry
- * the entry will be taken from th RCL.
- * For now. just create an entry to holde the nsuniqueid
- */
- db_entry = slapi_entry_alloc();
- slapi_entry_init(db_entry, entrydn, NULL);
- slapi_entry_add_string(db_entry, "nsuniqueid", uniqueid);
- slapi_ch_free((void **)&uniqueid);
- return (db_entry);
- }
- int
- sync_read_entry_from_changelog(Slapi_Entry *cl_entry, void *cb_data)
- {
- char *uniqueid = NULL;
- char *entryuuid = NULL;
- char *chgtype = NULL;
- char *chgnr = NULL;
- int chg_req;
- int prev = 0;
- int index = 0;
- unsigned long chgnum = 0;
- Sync_CallBackData *cb = (Sync_CallBackData *)cb_data;
- if (cb == NULL) {
- return (1);
- }
- uniqueid = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_UNIQUEID);
- if (uniqueid == NULL) {
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
- "sync_read_entry_from_changelog - Retro Changelog does not provide nsuniquedid."
- "Check 'cn=Retro Changelog Plugin,cn=plugins,cn=config' contains 'nsslapd-attribute: nsuniqueid:targetUniqueId'\n");
- return (1);
- }
- /* If we were requested to do openldap mode, get the targetEntryUuid too */
- if (cb->openldap_compat == PR_TRUE) {
- entryuuid = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_ENTRYUUID);
- if (entryuuid == NULL) {
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
- "sync_read_entry_from_changelog - Retro Changelog does not provide entryuuid."
- "Check 'cn=Retro Changelog Plugin,cn=plugins,cn=config' contains 'nsslapd-attribute: entryuuid:targetEntryUUID'\n");
- return (1);
- }
- }
- chgnr = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_CHANGENUMBER);
- chgnum = sync_number2ulong(chgnr);
- if (SYNC_INVALID_CHANGENUM == chgnum) {
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
- "sync_read_entry_from_changelog - Change number provided by Retro Changelog is invalid: %s\n", chgnr);
- slapi_ch_free_string(&chgnr);
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- return (1);
- }
- if (chgnum < cb->change_start) {
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM,
- "sync_read_entry_from_changelog - "
- "Change number provided by Retro Changelog %s is less than the initial number %lu\n",
- chgnr, cb->change_start);
- slapi_ch_free_string(&chgnr);
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- return (1);
- }
- index = chgnum - cb->change_start;
- chgtype = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_CHGTYPE);
- chg_req = sync_str2chgreq(chgtype);
- switch (chg_req) {
- case LDAP_REQ_ADD:
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_ADD\n", uniqueid);
- /* nsuniqueid cannot exist, just add reference */
- cb->cb_updates[index].upd_chgtype = LDAP_REQ_ADD;
- cb->cb_updates[index].upd_uuid = uniqueid;
- cb->cb_updates[index].upd_euuid = entryuuid;
- break;
- case LDAP_REQ_MODIFY:
- /* check if we have seen this uuid already */
- prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid);
- if (prev == -1) {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODIFY\n", uniqueid);
- cb->cb_updates[index].upd_chgtype = LDAP_REQ_MODIFY;
- cb->cb_updates[index].upd_uuid = uniqueid;
- cb->cb_updates[index].upd_euuid = entryuuid;
- } else {
- /* was add or mod, keep it */
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODIFY (already queued)\n", uniqueid);
- cb->cb_updates[index].upd_uuid = NULL;
- cb->cb_updates[index].upd_euuid = NULL;
- cb->cb_updates[index].upd_chgtype = 0;
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- }
- break;
- case LDAP_REQ_MODRDN: {
- /* if it is a modrdn, we finally need to decide if this will
- * trigger a present or delete state, keep the info that
- * the entry was subject to a modrdn
- */
- int new_scope = 0;
- int old_scope = 0;
- Slapi_DN *original_dn;
- char *newsuperior = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_NEWSUPERIOR);
- char *entrydn = sync_get_attr_value_from_entry(cl_entry, CL_ATTR_ENTRYDN);
- /* if newsuperior is set we need to checkif the entry has been moved into
- * or moved out of the scope of the synchronization request
- */
- original_dn = slapi_sdn_new_dn_byref(entrydn);
- old_scope = sync_is_active_scope(original_dn, cb->orig_pb);
- slapi_sdn_free(&original_dn);
- slapi_ch_free_string(&entrydn);
- if (newsuperior) {
- Slapi_DN *newbase;
- newbase = slapi_sdn_new_dn_byref(newsuperior);
- new_scope = sync_is_active_scope(newbase, cb->orig_pb);
- slapi_ch_free_string(&newsuperior);
- slapi_sdn_free(&newbase);
- } else {
- /* scope didn't change */
- new_scope = old_scope;
- }
- prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid);
- if (old_scope && new_scope) {
- /* nothing changed, it's just a MOD */
- if (prev == -1) {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODRDN\n", uniqueid);
- cb->cb_updates[index].upd_chgtype = LDAP_REQ_MODIFY;
- cb->cb_updates[index].upd_uuid = uniqueid;
- cb->cb_updates[index].upd_euuid = entryuuid;
- } else {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODRDN (already queued)\n", uniqueid);
- cb->cb_updates[index].upd_uuid = NULL;
- cb->cb_updates[index].upd_euuid = NULL;
- cb->cb_updates[index].upd_chgtype = 0;
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- }
- } else if (old_scope) {
- /* it was moved out of scope, handle as DEL */
- if (prev == -1) {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODRDN -> LDAP_REQ_DELETE\n", uniqueid);
- cb->cb_updates[index].upd_chgtype = LDAP_REQ_DELETE;
- cb->cb_updates[index].upd_uuid = uniqueid;
- cb->cb_updates[index].upd_euuid = entryuuid;
- cb->cb_updates[index].upd_e = sync_deleted_entry_from_changelog(cl_entry);
- } else {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODRDN -> LDAP_REQ_DELETE (already queued)\n", uniqueid);
- cb->cb_updates[prev].upd_chgtype = LDAP_REQ_DELETE;
- cb->cb_updates[prev].upd_e = sync_deleted_entry_from_changelog(cl_entry);
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- }
- } else if (new_scope) {
- /* moved into scope, handle as ADD */
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_MODRDN -> LDAP_REQ_ADD\n", uniqueid);
- cb->cb_updates[index].upd_chgtype = LDAP_REQ_ADD;
- cb->cb_updates[index].upd_uuid = uniqueid;
- cb->cb_updates[index].upd_euuid = entryuuid;
- } else {
- /* nothing to do */
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- }
- slapi_sdn_free(&original_dn);
- break;
- }
- case LDAP_REQ_DELETE:
- /* check if we have seen this uuid already */
- prev = sync_find_ref_by_uuid(cb->cb_updates, index, uniqueid);
- if (prev == -1) {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_DELETE\n", uniqueid);
- cb->cb_updates[index].upd_chgtype = LDAP_REQ_DELETE;
- cb->cb_updates[index].upd_uuid = uniqueid;
- cb->cb_updates[index].upd_euuid = entryuuid;
- cb->cb_updates[index].upd_e = sync_deleted_entry_from_changelog(cl_entry);
- } else {
- /* if it was added since last cookie state, we
- * can ignore it */
- if (cb->cb_updates[prev].upd_chgtype == LDAP_REQ_ADD) {
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_DELETE -> NO-OP\n", uniqueid);
- slapi_ch_free_string(&(cb->cb_updates[prev].upd_uuid));
- cb->cb_updates[prev].upd_uuid = NULL;
- cb->cb_updates[prev].upd_euuid = NULL;
- cb->cb_updates[index].upd_uuid = NULL;
- cb->cb_updates[index].upd_euuid = NULL;
- } else {
- /* ignore previous mod */
- slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM, "sync_read_entry_from_changelog - %s LDAP_REQ_DELETE (already queued, updating)\n", uniqueid);
- cb->cb_updates[index].upd_uuid = NULL;
- cb->cb_updates[index].upd_euuid = NULL;
- cb->cb_updates[prev].upd_chgtype = LDAP_REQ_DELETE;
- cb->cb_updates[prev].upd_e = sync_deleted_entry_from_changelog(cl_entry);
- }
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- }
- break;
- default:
- slapi_ch_free_string(&uniqueid);
- slapi_ch_free_string(&entryuuid);
- }
- slapi_ch_free_string(&chgtype);
- slapi_ch_free_string(&chgnr);
- return (0);
- }
- #define SYNC_MAX_DELETED_UUID_BATCH 50
- void
- sync_send_deleted_entries(Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count, Sync_Cookie *cookie)
- {
- char *syncUUIDs[SYNC_MAX_DELETED_UUID_BATCH + 1] = {0};
- struct berval *ber_syncUUIDs[SYNC_MAX_DELETED_UUID_BATCH + 1] = {0};
- size_t uuid_index = 0;
- PR_ASSERT(cookie);
- syncUUIDs[0] = NULL;
- for (size_t index = 0; index < chg_count; index++) {
- if (upd[index].upd_chgtype == LDAP_REQ_DELETE && upd[index].upd_uuid) {
- if (uuid_index < SYNC_MAX_DELETED_UUID_BATCH) {
- if (upd[index].upd_euuid) {
- /* Only occurs in openldap mode, swap to the entryuuid */
- syncUUIDs[uuid_index] = sync_entryuuid2uuid(upd[index].upd_euuid);
- } else {
- /* Normal mode */
- syncUUIDs[uuid_index] = sync_nsuniqueid2uuid(upd[index].upd_uuid);
- }
- uuid_index++;
- } else {
- /* max number of uuids to be sent in one sync info message */
- syncUUIDs[uuid_index] = NULL;
- for (size_t i = 0; i < uuid_index; i++) {
- ber_syncUUIDs[i] = (struct berval *) slapi_ch_malloc(sizeof(struct berval));
- ber_syncUUIDs[i]->bv_val = syncUUIDs[i];
- ber_syncUUIDs[i]->bv_len = 16;
- }
- sync_intermediate_msg(pb, LDAP_TAG_SYNC_ID_SET, cookie, ber_syncUUIDs);
- for (size_t i = 0; i < uuid_index; i++) {
- slapi_ch_free((void **)&syncUUIDs[i]);
- slapi_ch_free((void **)&ber_syncUUIDs[i]);
- syncUUIDs[i] = NULL;
- }
- uuid_index = 0;
- }
- }
- }
- if (uuid_index > 0 && syncUUIDs[uuid_index - 1]) {
- /* more entries to send */
- syncUUIDs[uuid_index] = NULL;
- for (size_t i = 0; i < uuid_index; i++) {
- ber_syncUUIDs[i] = (struct berval *) slapi_ch_malloc(sizeof(struct berval));
- ber_syncUUIDs[i]->bv_val = syncUUIDs[i];
- ber_syncUUIDs[i]->bv_len = 16;
- }
- sync_intermediate_msg(pb, LDAP_TAG_SYNC_ID_SET, cookie, ber_syncUUIDs);
- for (size_t i = 0; i < uuid_index; i++) {
- slapi_ch_free((void **)&syncUUIDs[i]);
- slapi_ch_free((void **)&ber_syncUUIDs[i]);
- syncUUIDs[i] = NULL;
- }
- }
- }
- void
- sync_send_modified_entries(Slapi_PBlock *pb, Sync_UpdateNode *upd, int chg_count, Sync_Cookie *cookie)
- {
- for (size_t index = 0; index < chg_count; index++) {
- if (upd[index].upd_chgtype != LDAP_REQ_DELETE && upd[index].upd_uuid) {
- sync_send_entry_from_changelog(pb, upd[index].upd_chgtype, upd[index].upd_uuid, cookie);
- }
- }
- }
- int
- sync_send_entry_from_changelog(Slapi_PBlock *pb, int chg_req __attribute__((unused)), char *uniqueid, Sync_Cookie *cookie)
- {
- Slapi_Entry *db_entry = NULL;
- int chg_type = LDAP_SYNC_ADD;
- int rv = LDAP_SUCCESS;
- Slapi_PBlock *search_pb = NULL;
- Slapi_Entry **entries = NULL;
- char *origbase;
- char *filter = slapi_ch_smprintf("(nsuniqueid=%s)", uniqueid);
- slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET_DN, &origbase);
- search_pb = slapi_pblock_new();
- slapi_search_internal_set_pb(search_pb, origbase,
- LDAP_SCOPE_SUBTREE, filter,
- NULL, 0, NULL, NULL, plugin_get_default_component_id(), 0);
- slapi_search_internal_pb(search_pb);
- slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rv);
- if (rv == LDAP_SUCCESS) {
- slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
- if (entries) {
- db_entry = *entries; /* there can only be one */
- }
- }
- if (db_entry && sync_is_entry_in_scope(pb, db_entry)) {
- LDAPControl **ctrl = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl *));
- rv = sync_create_state_control(db_entry, &ctrl[0], chg_type, NULL, cookie->openldap_compat);
- if (rv != LDAP_SUCCESS) {
- ldap_controls_free(ctrl);
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM, "Terminating sync_send_entry_from_changelog due to error code -> %d\n", rv);
- goto senddone;
- }
- slapi_send_ldap_search_entry(pb, db_entry, ctrl, NULL, 0);
- ldap_controls_free(ctrl);
- }
- senddone:
- slapi_free_search_results_internal(search_pb);
- slapi_pblock_destroy(search_pb);
- slapi_ch_free((void **)&filter);
- return rv;
- }
- static SyncOpInfo *
- new_SyncOpInfo(int flag, PRThread *tid, Sync_Cookie *cookie)
- {
- SyncOpInfo *spec = (SyncOpInfo *)slapi_ch_calloc(1, sizeof(SyncOpInfo));
- spec->send_flag = flag;
- spec->cookie = cookie;
- spec->tid = tid;
- return spec;
- }
- /* consumer operation extension constructor */
- static void *
- sync_operation_extension_ctor(void *object __attribute__((unused)), void *parent __attribute__((unused)))
- {
- /* we only set the extension value explicitly if the
- client requested the control - see deref_pre_search */
- return NULL; /* we don't set anything in the ctor */
- }
- /* consumer operation extension destructor */
- static void
- sync_delete_SyncOpInfo(SyncOpInfo **info)
- {
- if (info && *info) {
- sync_cookie_free(&((*info)->cookie));
- slapi_ch_free((void **)info);
- }
- }
- static void
- sync_operation_extension_dtor(void *ext, void *object __attribute__((unused)), void *parent __attribute__((unused)))
- {
- SyncOpInfo *spec = (SyncOpInfo *)ext;
- sync_delete_SyncOpInfo(&spec);
- }
- static SyncOpInfo *
- sync_get_operation_extension(Slapi_PBlock *pb)
- {
- Slapi_Operation *op;
- slapi_pblock_get(pb, SLAPI_OPERATION, &op);
- return (SyncOpInfo *)slapi_get_object_extension(sync_extension_type,
- op, sync_extension_handle);
- }
- static void
- sync_set_operation_extension(Slapi_PBlock *pb, SyncOpInfo *spec)
- {
- Slapi_Operation *op;
- slapi_pblock_get(pb, SLAPI_OPERATION, &op);
- slapi_set_object_extension(sync_extension_type, op,
- sync_extension_handle, (void *)spec);
- }
- void
- sync_register_allow_openldap_compat(PRBool allow)
- {
- /* This is synced by virtue of the plugin locking/loading. */
- allow_openldap_compat = allow;
- }
- int
- sync_register_operation_extension(void)
- {
- return slapi_register_object_extension(SYNC_PLUGIN_SUBSYSTEM,
- SLAPI_EXT_OPERATION,
- sync_operation_extension_ctor,
- sync_operation_extension_dtor,
- &sync_extension_type,
- &sync_extension_handle);
- }
- int
- sync_unregister_operation_entension(void)
- {
- int rc = slapi_unregister_object_extension(SYNC_PLUGIN_SUBSYSTEM,
- SLAPI_EXT_OPERATION,
- &sync_extension_type,
- &sync_extension_handle);
- return rc;
- }
|