From: <svn...@op...> - 2009-07-27 20:39:23
|
Author: scriptor Date: Mon Jul 27 22:39:10 2009 New Revision: 5708 URL: http://www.opensync.org/changeset/5708 Log: I have modified the ldap-sync plugin in a way, so that it can talk to the LDAP server "ns-slapd" from the Fedora Directory Server package. This is only the first step in trying to achieve compatibility with the Fedora Directory Server. The ldap-sync plugin in its current fashion is still to be linked against libldap from openldap. Most notable differences between slapd from openldap and ns-slapd from the Fedora Directory Server: 1. Searching for the available objectclasses works differently: slapd: ldapsearch (...) -LLL -b 'cn=Subschema' -s base '(objectclass=subschema)' objectClasses ns-slapd: ldapsearch (...) -LLL -b "cn=schema" -s base "objectclass=*" objectclasses | egrep -o "NAME[ ]*['][^']+[']" | cut -d\' -f2 2. ns-slapd does not know the scope type "LDAP_SCOPE_CHILDREN". Only "LDAP_SCOPE_SUB" and "LDAP_SCOPE_ONE", and - as it seems - LDAP_SCOPE_BASE. Cf. http://www.redhat.com/docs/manuals/dir-server/ag/8.0/Finding_Directory_Entries-Using_ldapsearch.html#Using_ldapsearch-Commonly_Used_ldapsearch_Options Modified: plugins/ldap-sync/src/ldap_connect.c plugins/ldap-sync/src/ldap_plugin.c plugins/ldap-sync/src/ldap_plugin.h Modified: plugins/ldap-sync/src/ldap_connect.c ============================================================================== --- plugins/ldap-sync/src/ldap_connect.c Mon Jul 27 22:38:57 2009 (r5707) +++ plugins/ldap-sync/src/ldap_connect.c Mon Jul 27 22:39:10 2009 (r5708) @@ -183,7 +183,18 @@ - +/** + * @brief This function checks whether the LDAP server supports + * the LDAP schema that has been configured for usage with + * the object type "contact". + * + * @param ctx The libopensync context. + * @param sinkenv The object type environment. + * @param error The libopensync error pointer. + * + * @returns TRUE on success, FALSE on error. + * + */ osync_bool ldap_plugin_check_contact_format_support(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) { @@ -379,7 +390,7 @@ if (!ldap_plugin_get_sinkenv(ctx, sink, userdata, &sinkenv, &error)) { if (!osync_error_is_set(&error)) - osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: has failed.", __FILE__, __LINE__); + osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_plugin_get_sinkenv() has failed.", __FILE__, __LINE__); goto error; } @@ -460,7 +471,7 @@ - // Bind to LDAP server + // Bind with LDAP server if (!ldap_plugin_makebind(ctx, sinkenv, &error)) { if (!osync_error_is_set(&error)) { osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Could not bind with LDAP server.", __FILE__, __LINE__); @@ -471,6 +482,17 @@ + if (!ldap_plugin_check_for_entryCSN(ctx, sinkenv, &error)) { + if (!osync_error_is_set(&error)) { + osync_error_set(&error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_check_for entryCSN() has failed.", __FILE__, __LINE__); + } + + goto error; + } + + + + if (!strcmp(osync_objtype_sink_get_name(sink), "contact")) { // Check whether the format chosen by configuration is supported // by the LDAP server, as well (currently only for objtype "contact"): @@ -614,7 +636,7 @@ if (!ldap_plugin_get_sinkenv(ctx, sink, userdata, &sinkenv, &error)) { if (!osync_error_is_set(&error)) - osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: has failed.", __FILE__, __LINE__); + osync_error_set(&error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_plugin_get_sinkenv() has failed.", __FILE__, __LINE__); goto error; } @@ -764,9 +786,9 @@ if (ldap_error == NULL) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore.\n", __FILE__, __LINE__, sinkenv->url); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore.\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername); } else { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore: \"%s\"\n", __FILE__, __LINE__, sinkenv->url, ldap_error); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: ERROR: ldap_start_tls_s() has failed. Could not establish a connection to the LDAP server \"%s\", therefore: \"%s\"\n", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, ldap_error); } goto error; @@ -790,7 +812,7 @@ /** - * @brief Binds to the LDAP server. + * @brief Binds with the LDAP server. * * Quoting from LDAP admin guide, section 13.3.1: * @@ -883,16 +905,16 @@ if (passwd.bv_len == 0) { if (ldap_error == NULL) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind to \"%s\" as \"%s\" with an empty password. Maybe the LDAP server does not allow anonymous binds.", __FILE__, __LINE__, sinkenv->servername, binddn); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind with \"%s\" as \"%s\" with an empty password. Maybe the LDAP server does not allow anonymous binds.", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, binddn); } else { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind to \"%s\" as \"%s\" with an empty password. Maybe the LDAP server does not allow anonymous binds: \"%s\"", __FILE__, __LINE__, sinkenv->servername, binddn, ldap_error); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind with \"%s\" as \"%s\" with an empty password. Maybe the LDAP server does not allow anonymous binds: \"%s\"", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, binddn, ldap_error); } } else { if (ldap_error == NULL) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind to \"%s\" as \"%s\".", __FILE__, __LINE__, sinkenv->servername, binddn); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind with \"%s\" as \"%s\".", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, binddn); } else { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind to \"%s\" as \"%s\": \"%s\"", __FILE__, __LINE__, sinkenv->servername, binddn, ldap_error); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and bind with \"%s\" as \"%s\": \"%s\"", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, binddn, ldap_error); } } @@ -975,18 +997,18 @@ if (passwd.bv_len == 0) { if (ldap_error == NULL) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind to \"%s\" as \"%s\" with an empty password, using \"%s\" as authentication mechanism. Maybe the LDAP server does not allow an anonymous bind.", __FILE__, __LINE__, sinkenv->servername, sinkenv->authcid, tmp_authmech); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind with \"%s\" as \"%s\" with an empty password, using \"%s\" as authentication mechanism. Maybe the LDAP server does not allow an anonymous bind.", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, sinkenv->authcid, tmp_authmech); } else { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind to \"%s\" as \"%s\" with an empty password, using \"%s\" as authentication mechanism. Maybe the LDAP server does not allow an anonymous bind: \"%s\"", __FILE__, __LINE__, sinkenv->servername, sinkenv->authcid, tmp_authmech, ldap_error); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind with \"%s\" as \"%s\" with an empty password, using \"%s\" as authentication mechanism. Maybe the LDAP server does not allow an anonymous bind: \"%s\"", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, sinkenv->authcid, tmp_authmech, ldap_error); } } else { if (ldap_error == NULL) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind to \"%s\" as \"%s\" using \"%s\" as authentication mechanism.", __FILE__, __LINE__, sinkenv->servername, sinkenv->authcid, tmp_authmech); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind with \"%s\" as \"%s\" using \"%s\" as authentication mechanism.", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, sinkenv->authcid, tmp_authmech); } else { if (extra_error && extra_error[0]) { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind to \"%s\" as \"%s\" using \"%s\" as authentication mechanism: \"%s\". %s", __FILE__, __LINE__, sinkenv->servername, sinkenv->authcid, tmp_authmech, ldap_error, extra_error); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind with \"%s\" as \"%s\" using \"%s\" as authentication mechanism: \"%s\". %s", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, sinkenv->authcid, tmp_authmech, ldap_error, extra_error); } else { - osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind to \"%s\" as \"%s\" using \"%s\" as authentication mechanism: \"%s\"", __FILE__, __LINE__, sinkenv->servername, sinkenv->authcid, tmp_authmech, ldap_error); + osync_error_set(error, OSYNC_ERROR_NO_CONNECTION, "%s:%i: Unable to connect and sasl bind with \"%s\" as \"%s\" using \"%s\" as authentication mechanism: \"%s\"", __FILE__, __LINE__, sinkenv->url ? sinkenv->url : sinkenv->servername, sinkenv->authcid, tmp_authmech, ldap_error); } } } @@ -1129,8 +1151,8 @@ *result = FALSE; - // Look for all the LDAP subschemata: - if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, "cn=Subschema", "(objectClass=subschema)", LDAP_SCOPE_BASE, OBJECTCLASSES, sinkenv, FALSE, &res, error)) { + // Look for all the LDAP subschemata (as used by slapd from openldap): + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, "cn=Subschema", "(objectClass=subschema)", LDAP_SCOPE_BASE, OBJECTCLASSES, sinkenv, TRUE, &res, error)) { if (!osync_error_is_set(error)) { osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); } @@ -1140,7 +1162,6 @@ if (res == NULL) { - // TODO: Produce osync error message. osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() must have failed.\n", __FILE__, __LINE__); ldap_plugin_dump_ldap_error_message(sinkenv, "cn=Subschema", "(objectClass=subschema)", "ldap_plugin_call_ldap_search()"); @@ -1151,28 +1172,220 @@ res2 = ldap_first_entry(sinkenv->ld, res); if (!res2) { /* No objectClass entries found */ +/* osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: Could not find any objectClass entry while searching for LDAP subschemata.", __FILE__, __LINE__); goto error; - } +*/ + if (res) + ldap_msgfree(res); - ber = ldap_get_values_len(sinkenv->ld, res2, "objectClasses"); + // Look for all the LDAP schemata (as used by ns-slapd from the + // fedora directory server): + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, "cn=schema", "(objectClass=subschema)", LDAP_SCOPE_BASE, OBJECTCLASSES, sinkenv, TRUE, &res, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + } - while (ber[i]) { - if (strstr(ber[i]->bv_val, ldap_schema)) { - *result = TRUE; - break; + goto error; + } + + if (res == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() must have failed.\n", __FILE__, __LINE__); + ldap_plugin_dump_ldap_error_message(sinkenv, "cn=schema", "(objectClass=subschema)", "ldap_plugin_call_ldap_search()"); + + goto error; } - i++; - } + res2 = ldap_first_entry(sinkenv->ld, res); + if (!res2) { + /* No objectClass entries found */ + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: Could not find any objectClass entry while searching for LDAP schemata.", __FILE__, __LINE__); + goto error; + } else { + ber = ldap_get_values_len(sinkenv->ld, res2, "objectClasses"); + } + } else { + ber = ldap_get_values_len(sinkenv->ld, res2, "objectClasses"); + } + + + if (ber) { + while (ber[i]) { + if (strstr(ber[i]->bv_val, ldap_schema)) { + *result = TRUE; + break; + } + i++; + } + } else { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ber = NULL. This should have been unreachable code.", __FILE__, __LINE__); + goto error; + } + + + if (ber) + ldap_value_free_len(ber); + + if (res) + ldap_msgfree(res); + + + + osync_trace(TRACE_EXIT, "%s", __func__); + return TRUE; + + +error: if (ber) ldap_value_free_len(ber); if (res) ldap_msgfree(res); + if (!osync_error_is_set(error)) + osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); + + osync_context_report_osyncwarning(ctx, *error); + osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); + return FALSE; +} + + + + +/* + * @brief This function checks whether the LDAP server supports the + * operational attribute "entryCSN". While slapd from openldap + * does so, ns-slapd from the Fedora Directory Server does not. + * entryCSN and modifyTimestamp in the latter case are used to + * build up a unique hash value for the current state of an + * LDAP entry. @sa ldap_plugin_get_hash_from_ldap_server() + * + * @param ctx The libopensync context. + * @param sinkenv The object type environment. As a result of this function + * sinkenv->has_entryCSN is set to either TRUE or FALSE. + * @param error The libopensync error pointer. + * + * @returns TRUE on success, FALSE on error. + */ + +osync_bool +ldap_plugin_check_for_entryCSN(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error) +{ + LDAPMessage *res = NULL; + LDAPMessage *all_entries = NULL; + char *filter = NULL; + char *base = NULL; + struct berval **ber = NULL; + + + osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, ctx, sinkenv, error); + + + if (ctx == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ctx = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + if (sinkenv == NULL) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: sinkenv = NULL. Returning.\n", __FILE__, __LINE__); + goto error; + } + + + + + + filter = (char *)g_malloc0(4096); + if (filter) { + memset(filter, 0, 4096); + } else { + ldap_plugin_printf("%s:%i: ERROR: g_malloc0 problem. Exiting.", __FILE__, __LINE__); + exit(1); + } + + snprintf(filter, 4095, "(entryCSN=*)"); + + + base = (char *)g_malloc0(4096); + if (base) { + memset(base, 0, 4096); + } else { + ldap_plugin_printf("%s:%i: ERROR: g_malloc0 problem. Exiting.", __FILE__, __LINE__); + exit(1); + } + + snprintf(base, 4095, "%s", sinkenv->searchbase); + +#ifdef DEBUG_configuration + ldap_plugin_printf("\n\n%s:%i: base = \"%s\", filter = \"%s\"", __FILE__, __LINE__, base, filter); +#endif + + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, base, filter, LDAP_SCOPE_BASE, OPERATIONAL_ATTRIBUTES, sinkenv, FALSE, &all_entries, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + } + + goto error; + } + + + if (all_entries == NULL) { + int result_code = 0; + + if (ldap_get_option(sinkenv->ld, LDAP_OPT_RESULT_CODE, &result_code) != LDAP_OPT_SUCCESS) { + osync_trace(TRACE_ERROR, "%s:%i: ERROR: ldap_get_option() has failed. result_code could not be filled in.\n", __FILE__, __LINE__); + } else { + if (result_code != LDAP_SUCCESS) { + char *error_msg = ldap_plugin_report_ldap_error(sinkenv, __FILE__, __LINE__, result_code); + if (error_msg) { + ldap_plugin_printf("%s:%i: ERROR: %s", __FILE__, __LINE__, error_msg); + g_free(error_msg); + } + } + } + + osync_trace(TRACE_INTERNAL, "\n\n%s:%i: Setting sinkenv->has_entryCSN to FALSE.", __FILE__, __LINE__); + + sinkenv->has_entryCSN = FALSE; + } else { + int count = ldap_count_entries(sinkenv->ld, all_entries); + +#ifdef DEBUG_configuration + ldap_plugin_printf("%s:%i: There are %i subentries.", __FILE__, __LINE__, count); +#endif + + if (count > 0) { + osync_trace(TRACE_INTERNAL, "\n\n%s:%i: Setting sinkenv->has_entryCSN to TRUE.", __FILE__, __LINE__); + sinkenv->has_entryCSN = TRUE; + } else { + osync_trace(TRACE_INTERNAL, "\n\n%s:%i: Setting sinkenv->has_entryCSN to FALSE.", __FILE__, __LINE__); + sinkenv->has_entryCSN = FALSE; + } + } + + + + + + + + + + + + if (base) + g_free(base); + + if (filter) + g_free(filter); + + if (all_entries) { + ldap_msgfree(all_entries); + all_entries = NULL; + } osync_trace(TRACE_EXIT, "%s", __func__); @@ -1186,6 +1399,17 @@ if (res) ldap_msgfree(res); + if (base) + g_free(base); + + if (filter) + g_free(filter); + + if (all_entries) { + ldap_msgfree(all_entries); + all_entries = NULL; + } + if (!osync_error_is_set(error)) osync_error_set(error, OSYNC_ERROR_GENERIC, "Unknown reason.\n"); @@ -1196,6 +1420,8 @@ + + /** * @brief A wrapper function to the ldap_search_ext_s() library call. * @@ -1255,6 +1481,10 @@ userattributes[0] = "objectClasses"; break; + case FEDORADSmodifyTimestamp: + userattributes[0] = "modifyTimestamp"; + break; + default: userattributes[0] = LDAP_ALL_USER_ATTRIBUTES; }; @@ -1873,7 +2103,15 @@ char *subentry_filter = (char *)"(objectClass=*)"; LDAPMessage *possible_subentries = NULL; - if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, subentry_filter, LDAP_SCOPE_CHILDREN, USER_ATTRIBUTES, sinkenv, FALSE, &possible_subentries, error)) { + + /* ns-slapd does not understand LDAP_SCOPE_CHILDREN. + * LDAP_SCOPE_ONELEVEL is NOT correct, because it does not cover + * special elements, like AlarmDisplay, RecurrenceRule, TimezoneComponent, + * TimezoneRule etc. + * So we MUST use LDAP_SCOPE_SUBTREE. This forces us to skip the + * base element (continue in a for-loop). + */ + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, subentry_filter, /* LDAP_SCOPE_CHILDREN */ LDAP_SCOPE_SUBTREE, USER_ATTRIBUTES, sinkenv, FALSE, &possible_subentries, error)) { if (!osync_error_is_set(error)) { osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); } @@ -1897,6 +2135,22 @@ for ( ; subentry_result ; subentry_result = ldap_next_entry(sinkenv->ld, subentry_result)) { // keyattribute for subentries: "ou" char *keyattr = (char *)"ou"; + + +#ifdef DEBUG_ldapdata_from_server + fprintf(stderr, "\n\n\n%s:%i: dn: \"%s\"\n\n\n", __FILE__, __LINE__, ldap_get_dn(sinkenv->ld, subentry_result)); +#endif + + // We skip the base element, because with respect to ns-slapd + // we have used LDAP_SCOPE_SUBTREE rather than LDAP_SCOPE_CHILDREN. + if (!strcmp(entry->dn, ldap_get_dn(sinkenv->ld, subentry_result))) { +#ifdef DEBUG_ldapdata_from_server + fprintf(stderr, "\n\n\n****************\n%s:%i: dn == base!!!\n****************\n\n\n", __FILE__, __LINE__); +#endif + + continue; + } + ldap_entry *subentry = ldap_plugin_create_ldap_entry_from_LDAPMessage(ctx, sinkenv, subentry_result, keyattr, error); if (subentry == NULL) { @@ -2346,8 +2600,9 @@ /** * @brief This function calculates the hash value of one LDAP entry on the - * LDAP server. The hash is based on the entryCSN attribute of each entry - * in the DIT. Helper function for ldap_plugin_get_hash_from_ldap_server(). + * LDAP server. The hash is based on either the entryCSN attribute + * or the modifyTimestamp attribute of each entry in the DIT. + * Helper function for ldap_plugin_get_hash_from_ldap_server(). * * @param ctx The libopensync context. * @param sinkenv Object type specific environment. @@ -2395,7 +2650,14 @@ // ------------------------------------------- for ( ; one_entry; one_entry = ldap_next_entry(sinkenv->ld, one_entry)) { - struct berval **one_csn = ldap_get_values_len(sinkenv->ld, one_entry, "entryCSN"); + struct berval **one_csn = NULL; + + + if (sinkenv->has_entryCSN) { + one_csn = ldap_get_values_len(sinkenv->ld, one_entry, "entryCSN"); + } else { + one_csn = ldap_get_values_len(sinkenv->ld, one_entry, "modifyTimestamp"); + } if (!one_csn) { @@ -2412,7 +2674,11 @@ if (one_csn[0]->bv_val) { #ifdef DEBUG_fastsync - ldap_plugin_printf("%s:%i: entryCSN = \"%s\"", __FILE__, __LINE__, one_csn[0]->bv_val); + if (sinkenv->has_entryCSN) { + ldap_plugin_printf("%s:%i: entryCSN = \"%s\"", __FILE__, __LINE__, one_csn[0]->bv_val); + } else { + ldap_plugin_printf("%s:%i: modifyTimestamp = \"%s\"", __FILE__, __LINE__, one_csn[0]->bv_val); + } #endif if (actual_hash == NULL) { @@ -2460,8 +2726,8 @@ /** * @brief This functions calculates the hash value of an LDAP entry including - * any subentries in the DIT for any uid, based on the entryCSN - * attribute of each entry/subentry. + * any subentries in the DIT for any uid, based on either the entryCSN + * attribute or the modifyTimestamp attribute of each entry/subentry. * * @param ctx The libopensync context. * @param sinkenv Object type specific environment @@ -2506,7 +2772,11 @@ - snprintf(filter, 4095, "(entryCSN=*)"); + if (sinkenv->has_entryCSN) { + snprintf(filter, 4095, "(entryCSN=*)"); + } else { + snprintf(filter, 4095, "(modifyTimestamp=*)"); + } base = (char *)g_malloc0(4096); if (base) { @@ -2524,16 +2794,26 @@ #endif // -------------------------------------------------- - // Get entryCSN for the base entry itself plus all related subentries - if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, base, filter, LDAP_SCOPE_SUBTREE, OPERATIONAL_ATTRIBUTES, sinkenv, FALSE, &all_entries, error)) { - if (!osync_error_is_set(error)) { - osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + // Get entryCSN or modifyTimesatmp for the base entry itself + // plus all related subentries + if (sinkenv->has_entryCSN) { + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, base, filter, LDAP_SCOPE_SUBTREE, OPERATIONAL_ATTRIBUTES, sinkenv, FALSE, &all_entries, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + } + + goto error; } + } else { + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, base, filter, LDAP_SCOPE_SUBTREE, FEDORADSmodifyTimestamp, sinkenv, FALSE, &all_entries, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + } - goto error; + goto error; + } } - if (all_entries == NULL) { ldap_plugin_dump_ldap_error_message(sinkenv, base, filter, "ldap_plugin_call_ldap_search()"); osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: WARNING: all_entries = NULL. No hash available for this uid.\n", __FILE__, __LINE__); @@ -4141,12 +4421,13 @@ /** - * @brief Queries the LDAP server about the entryCSN attribute of - * one particular LDAP entry, and adds it to an already existing - * hash string, thus gradually building the final hash value. + * @brief Queries the LDAP server about either the entryCSN attribute + * or the modifyTimestamp attribute of one particular LDAP entry, + * and adds it to an already existing hash string, thus gradually + * building the final hash value. * * @param ctx The libopensync context. - * @param hash The entryCSN based hash value of an LDAP entry. + * @param hash The entryCSN/modifyTimestamp based hash value of an LDAP entry. * @param sinkenv The object type specific environment. * @param entry The LDAP entry. * @param error The libopensync error pointer. @@ -4196,23 +4477,47 @@ ldap_plugin_printf("%s:%i: \nentry->dn = \"%s\"", __FILE__, __LINE__, entry->dn); #endif - - if (sinkenv->searchfilter) { - filter = g_strdup_printf("(&(entryCSN=*)%s)", sinkenv->searchfilter); + + if (sinkenv->has_entryCSN) { + if (sinkenv->searchfilter) { + filter = g_strdup_printf("(&(entryCSN=*)%s)", sinkenv->searchfilter); + } else { + filter = g_strdup_printf("(entryCSN=*)"); + } + + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, filter, LDAP_SCOPE_SUB, OPERATIONAL_ATTRIBUTES, sinkenv, FALSE, &messages, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + } + + goto error; + } + } else { - filter = (char *)"(entryCSN=*)"; - } + if (sinkenv->searchfilter) { + filter = g_strdup_printf("(&(modifyTimestamp=*)%s)", sinkenv->searchfilter); + } else { + filter = g_strdup_printf("(modifyTimestamp=*)"); + } - if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, "(entryCSN=*)", LDAP_SCOPE_SUB, OPERATIONAL_ATTRIBUTES, sinkenv, FALSE, &messages, error)) { - if (!osync_error_is_set(error)) { - osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, filter, LDAP_SCOPE_SUB, FEDORADSmodifyTimestamp, sinkenv, FALSE, &messages, error)) { + if (!osync_error_is_set(error)) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: ERROR: ldap_plugin_call_ldap_search() has failed.\n", __FILE__, __LINE__); + } + + goto error; } - goto error; } + + + + if (filter) { + g_free(filter); + filter = NULL; + } - g_free(filter); if (messages == NULL) { int result_code = 0; @@ -4230,13 +4535,26 @@ } else { LDAPMessage *msg = ldap_first_entry(sinkenv->ld, messages); for ( ; msg ; msg = ldap_next_entry(sinkenv->ld, msg)) { - struct berval **id_values = ldap_get_values_len(sinkenv->ld, msg, "entryCSN"); + struct berval **id_values = NULL; char *ldap_attribute = NULL; BerElement *berptr = NULL; + if (sinkenv->has_entryCSN) { + id_values = ldap_get_values_len(sinkenv->ld, msg, "entryCSN"); + } else { + id_values = ldap_get_values_len(sinkenv->ld, msg, "modifyTimestamp"); + } + + + if (!id_values) { - ldap_plugin_printf("%s:%i: WARNING: Entry DN=%s has no attribute 'entryCSN' which is used here as key attribute.", __FILE__, __LINE__, ldap_get_dn(sinkenv->ld, msg)); + if (sinkenv->has_entryCSN) { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: WARNING: Entry dn: %s has no attribute 'entryCSN', although here it is expected as key attribute.", __FILE__, __LINE__, ldap_get_dn(sinkenv->ld, msg)); + } else { + osync_error_set(error, OSYNC_ERROR_GENERIC, "%s:%i: WARNING: Entry dn: %s has no attribute 'modifyTimestamp', although here it is expected as key attribute.", __FILE__, __LINE__, ldap_get_dn(sinkenv->ld, msg)); + } + goto error; } else { @@ -4252,7 +4570,16 @@ ldap_attribute = ldap_next_attribute(sinkenv->ld, msg, berptr) ) { - if (!strcmp(ldap_attribute, "entryCSN")) { + const char *identifying_str = NULL; + + + if (sinkenv->has_entryCSN) { + identifying_str = "entryCSN"; + } else { + identifying_str = "modifyTimestamp"; + } + + if (!strcmp(ldap_attribute, identifying_str)) { struct berval **ret = NULL; ret = ldap_get_values_len(sinkenv->ld, msg, ldap_attribute); @@ -4315,7 +4642,10 @@ break; - } // if (!strcmp(ldap_attribute, "entryCSN")) + } // if (!strcmp(ldap_attribute, identifying_str)) + + + if (ldap_attribute) { ldap_memfree(ldap_attribute); Modified: plugins/ldap-sync/src/ldap_plugin.c ============================================================================== --- plugins/ldap-sync/src/ldap_plugin.c Mon Jul 27 22:38:57 2009 (r5707) +++ plugins/ldap-sync/src/ldap_plugin.c Mon Jul 27 22:39:10 2009 (r5708) @@ -583,7 +583,7 @@ * @returns TRUE on successful parsing, FALSE on any error. */ -osync_bool ldap_plugin_parse_config(OSyncPluginInfo *info, sink_environment *sinkenv, const char *objtype, OSyncPluginConfig * config, OSyncError **error) +osync_bool ldap_plugin_parse_config(OSyncPluginInfo *info, sink_environment *sinkenv, const char *objtype, OSyncPluginConfig *config, OSyncError **error) { osync_bool retval = FALSE; OSyncPluginConnection *conn = NULL; @@ -3306,7 +3306,7 @@ // we should call ldap_add_ext_s(), instead. // So we must check for the existence of this LDAP entry in order to // decide which libldap call to make. - if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, "", LDAP_SCOPE_BASE, USER_ATTRIBUTES, sinkenv, FALSE, &res2, error)) { + if (!ldap_plugin_call_ldap_search(ctx, sinkenv->ld, entry->dn, "(objectclass=*)", LDAP_SCOPE_BASE, USER_ATTRIBUTES, sinkenv, FALSE, &res2, error)) { int result_code = 0; Modified: plugins/ldap-sync/src/ldap_plugin.h ============================================================================== --- plugins/ldap-sync/src/ldap_plugin.h Mon Jul 27 22:38:57 2009 (r5707) +++ plugins/ldap-sync/src/ldap_plugin.h Mon Jul 27 22:39:10 2009 (r5708) @@ -181,7 +181,11 @@ #define OPERATIONAL_ATTRIBUTES 2 ///< This refers to ldap_plugin_call_ldap_search() ///< and LDAP_ALL_OPERATIONAL_ATTRIBUTES in ldap.h #define OBJECTCLASSES 3 ///< This refers to ldap_plugin_call_ldap_search() - +#define FEDORADSmodifyTimestamp 4 ///< ns-slapd does not clearly make + ///< an appropriate distinction between + ///< LDAP_ALL_USER_ATTRIBUTES and + ///< LDAP_ALL_OPERATIONAL_ATTRIBUTES. + ///< This is sloppy. @@ -362,6 +366,11 @@ ///< the "mozillaAbPersonAlpha" objectclass. osync_bool inetorg_support; ///< Whether or not the LDAP server supports ///< the "inetOrgPerson" objectclass + osync_bool has_entryCSN; ///< Whether or not the LDAP server offers + ///< the operational attribute "entryCSN". + ///< slapd from openldap does, whereas + ///< ns-slapd from the fedora directory server + ///< does not, as it seems. LDAP *ld; ///< Handle for the connection to the LDAP server. ///< (NULL, if no connection is present) } sink_environment; @@ -419,6 +428,7 @@ char *ldap_plugin_build_actual_hash(OSyncContext *ctx, sink_environment *sinkenv, const char *base, const char *filter, LDAPMessage *all_entries, OSyncError **error); osync_bool ldap_plugin_call_ldap_search(OSyncContext *ctx, const LDAP *ldap_handle, const char *searchbase, const char *filter, const int scope, const int kind_of_attributes, const sink_environment *sinkenv, osync_bool ignore_no_such_object, LDAPMessage **results, OSyncError **error); osync_bool ldap_plugin_check_contact_format_support(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); +osync_bool ldap_plugin_check_for_entryCSN(OSyncContext *ctx, sink_environment *sinkenv, OSyncError **error); osync_bool ldap_plugin_check_for_keyattribute(sink_environment *sinkenv, ldap_entry *entry, osync_bool check_key_attribute, OSyncError **error); osync_bool ldap_plugin_check_ldap_schema_support(OSyncContext *ctx, sink_environment *sinkenv, const char *ldap_schema, osync_bool *result, OSyncError **error); osync_bool ldap_plugin_check_modify_on_attr_vals (OSyncContext *ctx, struct berval **oldvals, struct berval **newvals, osync_bool *modification, OSyncError **error); |