/* * Copyright (C) 2006 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Authors: * Mark McLoughlin */ /* * Compile with: * gcc -g -Wall -lldap test-ldap-psearch-notifications.c -o test-ldap-psearch-notifications */ #include #include #include #include #include /* * See: * http://www.watersprings.org/pub/id/draft-smith-psearch-ldap-01.txt * http://www.watersprings.org/pub/id/draft-ietf-ldapext-c-api-psearch-00.txt * http://www.watersprings.org/pub/id/draft-ietf-ldapext-ldap-c-api-05.txt */ #define _LDAP_CONTROL_PERSISTENTSEARCH "2.16.840.1.113730.3.4.3" #define _LDAP_CONTROL_ENTRYCHANGE "2.16.840.1.113730.3.4.7" #define _LDAP_CHANGETYPE_ADD 1 #define _LDAP_CHANGETYPE_DELETE 2 #define _LDAP_CHANGETYPE_MODIFY 4 #define _LDAP_CHANGETYPE_MODDN 8 #define _LDAP_CHANGETYPE_ANY (1|2|4|8) static inline int is_ascii_string (char *blob, int len) { int i; if (len <= 0) return 0; for (i = 0; i < len; i++) if (!isascii (blob[i])) return 0; return 1; } static const char * change_type_to_str (int change_type) { switch (change_type) { case _LDAP_CHANGETYPE_ADD: return "add"; case _LDAP_CHANGETYPE_DELETE: return "delete"; case _LDAP_CHANGETYPE_MODIFY: return "modify"; case _LDAP_CHANGETYPE_MODDN: return "moddn"; default: break; } return "unknown"; } static LDAPControl * create_psearch_control (int change_types, int changes_only, int return_ecs) { LDAPControl *control; BerElement *element; ber_int_t ber_change_types; ber_int_t ber_changes_only; ber_int_t ber_return_ecs; if ((element = ber_alloc_t (LBER_USE_DER)) == NULL) return NULL; ber_change_types = change_types; ber_changes_only = changes_only; ber_return_ecs = return_ecs; if (ber_printf (element, "{ibb}", ber_change_types, ber_changes_only, ber_return_ecs) < 0) { ber_free (element, 1); return NULL; } control = NULL; if (ldap_create_control (_LDAP_CONTROL_PERSISTENTSEARCH, element, 1, &control) != LDAP_SUCCESS) { ber_free (element, 1); return NULL; } ber_free (element, 1); return control; } static int parse_entrychange_controls (LDAP *cnx, LDAPControl **controls, int *change_type_ret, int *change_num_ret, char **previous_dn_ret) { int i; ber_int_t change_type; ber_int_t change_num; char *previous_dn; if (change_type_ret != NULL) *change_type_ret = 0; if (change_num_ret != NULL) change_num_ret = 0; if (previous_dn_ret != NULL) *previous_dn_ret = NULL; change_type = 0; change_num = 0; previous_dn = NULL; for (i = 0; controls[i] != NULL; i++) { LDAPControl *control = controls[i]; BerElement *element; ber_len_t len; if (strcmp (control->ldctl_oid, _LDAP_CONTROL_ENTRYCHANGE) != 0) continue; element = ber_init (&control->ldctl_value); if (element == NULL) return LDAP_NO_MEMORY; if (ber_scanf (element, "{e", &change_type) == LBER_ERROR) { ber_free (element, 1); return LDAP_DECODING_ERROR; } previous_dn = NULL; switch (change_type) { case _LDAP_CHANGETYPE_ADD: break; case _LDAP_CHANGETYPE_DELETE: break; case _LDAP_CHANGETYPE_MODIFY: break; case _LDAP_CHANGETYPE_MODDN: if (ber_scanf (element, "a", &previous_dn) == LBER_ERROR) { ber_free (element, 1); return LDAP_DECODING_ERROR; } break; default: break; } if (ber_peek_tag (element, &len) == LBER_INTEGER) { if (ber_scanf (element, "i", &change_num) == LBER_ERROR) { ber_free (element, 1); return LDAP_DECODING_ERROR; } } } if (change_type_ret != NULL) *change_type_ret = change_type; if (change_num_ret != NULL) *change_num_ret = change_num; if (previous_dn_ret != NULL) *previous_dn_ret = previous_dn; return LDAP_SUCCESS; } int main (int argc, char **argv) { const char *hostname; const char *base; const char *filter; int protocol; int scope; LDAP *cnx; LDAPControl *controls[2]; LDAPMessage *entries; struct timeval timeout; int msgid; int ret; hostname = "localhost"; protocol = LDAP_VERSION3; base = "dc=watercooler"; filter = "(objectClass=*)"; scope = LDAP_SCOPE_SUBTREE; if ((ret = ldap_create (&cnx)) != LDAP_SUCCESS) { fprintf (stderr, "Failed to connect to directory server: %s\n", ldap_err2string (ret)); return 1; } if ((ret = ldap_set_option (cnx, LDAP_OPT_HOST_NAME, hostname)) != LDAP_SUCCESS) { fprintf (stderr, "Error setting hostname to '%s': %s\n", hostname, ldap_err2string (ret)); ldap_unbind_ext (cnx, NULL, NULL); return 1; } if ((ret = ldap_set_option (cnx, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) { fprintf (stderr, "Error setting protocol version to '%d': %s\n", protocol, ldap_err2string (ret)); ldap_unbind_ext (cnx, NULL, NULL); return 1; } #if 0 { const char *bind_dn; const char *bind_passwd; struct berval bind_cred; bind_dn = "cn=Directory Manager"; bind_passwd = "foo"; bind_cred.bv_val = (char *) bind_passwd; bind_cred.bv_len = strlen (bind_passwd); if ((ret = ldap_sasl_bind_s (cnx, bind_dn, LDAP_SASL_SIMPLE, &bind_cred, NULL, NULL, NULL)) != LDAP_SUCCESS) { fprintf (stderr, "Error binding as '%s': %s\n", bind_dn, ldap_err2string (ret)); ldap_unbind_ext (cnx, NULL, NULL); return 1; } } #endif controls[0] = create_psearch_control (_LDAP_CHANGETYPE_ANY, 1, 1); controls[1] = NULL; msgid = 0; ret = ldap_search_ext (cnx, base, scope, filter, NULL, /* attrs */ 0, /* attrsonly */ controls, NULL, /* clientctrls */ NULL, /* timeout */ LDAP_NO_LIMIT, &msgid); if (ret != LDAP_SUCCESS) { fprintf (stderr, "Error querying with base='%s' and filter='%s': %s\n", base, filter, ldap_err2string (ret)); ldap_control_free (controls[0]); ldap_unbind_ext (cnx, NULL, NULL); return 1; } ldap_control_free (controls[0]); timeout.tv_sec = 1; timeout.tv_usec = 0; entries = NULL; while ((ret = ldap_result (cnx, msgid, LDAP_MSG_RECEIVED, &timeout, &entries)) >= 0) { LDAPMessage *entry; LDAPControl **ec_controls; if (ret == 0) { printf ("Timed out\n"); continue; } entry = ldap_first_entry (cnx, entries); while (entry != NULL) { BerElement *berptr; const char *attr; printf ("dn: %s\n", ldap_get_dn (cnx, entry)); berptr = NULL; attr = ldap_first_attribute (cnx, entry, &berptr); while (attr != NULL) { struct berval **values; int i; values = ldap_get_values_len (cnx, entry, attr); for (i = 0; values[i] != NULL; i++) if (is_ascii_string (values[i]->bv_val, values[i]->bv_len)) printf ("%s: %s\n", attr, values[i]->bv_val); ldap_value_free_len (values); attr = ldap_next_attribute (cnx, entry, berptr); } if (berptr != NULL) ber_free (berptr, 0); ec_controls = NULL; if ((ret = ldap_get_entry_controls (cnx, entry, &ec_controls)) != LDAP_SUCCESS) { fprintf (stderr, "Error getting entry change notification controls from message: %s\n", ldap_err2string (ret)); ldap_msgfree (entries); ldap_unbind_ext (cnx, NULL, NULL); return 1; } if (ec_controls != NULL) { int ch_type; int ch_num; char *prev_dn; ch_type = ch_num = 0; prev_dn = NULL; if ((ret = parse_entrychange_controls (cnx, ec_controls, &ch_type, &ch_num, &prev_dn)) != LDAP_SUCCESS) { fprintf (stderr, "Error parsing entry change notification controls: %s\n", ldap_err2string (ret)); ldap_controls_free (ec_controls); ldap_msgfree (entries); ldap_unbind_ext (cnx, NULL, NULL); return 1; } printf ("\n"); printf ("Entry change notification info: change type = '%s', change num = '%d', previous DN = '%s'\n", change_type_to_str (ch_type), ch_num, prev_dn != NULL ? prev_dn : "(null)"); ldap_controls_free (ec_controls); } printf ("\n\n"); entry = ldap_next_entry (cnx, entry); } ldap_msgfree (entries); } if (ret < 0) { int err; ret = ldap_parse_result (cnx, entries, &err, NULL, NULL, NULL, NULL, 0); fprintf (stderr, "Error fetching result from query: %s\n", ldap_err2string (err)); ldap_msgfree (entries); ldap_unbind_ext (cnx, NULL, NULL); return 1; } ldap_unbind_ext (cnx, NULL, NULL); return 0; }