]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#8983 Add draft Persistent Search
authorHoward Chu <hyc@openldap.org>
Mon, 25 Feb 2019 11:53:55 +0000 (11:53 +0000)
committerHoward Chu <hyc@openldap.org>
Mon, 25 Feb 2019 15:19:33 +0000 (15:19 +0000)
clients/tools/common.c
clients/tools/ldapsearch.c
include/ldap.h
libraries/libldap/Makefile.in
libraries/libldap/psearchctrl.c [new file with mode: 0644]
libraries/libldap_r/Makefile.in

index 4c1c4d774e63429c0967f7133677523877807ef2..672bd07bb0586c9787f4b601aaad140610093ef2 100644 (file)
@@ -139,6 +139,7 @@ typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl );
 static int print_preread( LDAP *ld, LDAPControl *ctrl );
 static int print_postread( LDAP *ld, LDAPControl *ctrl );
 static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
+static int print_psearch( LDAP *ld, LDAPControl *ctrl );
 #ifdef LDAP_CONTROL_AUTHZID_RESPONSE
 static int print_authzid( LDAP *ld, LDAPControl *ctrl );
 #endif
@@ -167,6 +168,7 @@ static struct tool_ctrls_t {
        { LDAP_CONTROL_PRE_READ,                        TOOL_ALL,       print_preread },
        { LDAP_CONTROL_POST_READ,                       TOOL_ALL,       print_postread },
        { LDAP_CONTROL_PAGEDRESULTS,                    TOOL_SEARCH,    print_paged_results },
+       { LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE,                     TOOL_SEARCH,    print_psearch },
 #ifdef LDAP_CONTROL_AUTHZID_RESPONSE
        /* this is generally deprecated in favor of LDAP WhoAmI? operation, hence only supported as a VC inner control */
        { LDAP_CONTROL_AUTHZID_RESPONSE,                TOOL_VC,        print_authzid },
@@ -2161,6 +2163,60 @@ print_paged_results( LDAP *ld, LDAPControl *ctrl )
        return 0;
 }
 
+static int
+print_psearch( LDAP *ld, LDAPControl *ctrl )
+{
+       int rc;
+       int chgtype;
+       int chgpres;
+       long chgnum;
+       struct berval prevdn;
+
+       rc = ldap_parse_entrychange_control( ld, ctrl, &chgtype, &prevdn,
+               &chgpres, &chgnum );
+       if ( rc == LDAP_SUCCESS ) {
+               char buf[ BUFSIZ ];
+               char *ptr = buf;
+               int blen = sizeof(buf), len;
+               
+               switch( chgtype ) {
+               case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD:
+                       len = snprintf( ptr, blen, "add" );
+                       ptr += len;
+                       blen -= len;
+                       break;
+               case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE:
+                       len = snprintf( ptr, blen, "delete" );
+                       ptr += len;
+                       blen -= len;
+                       break;
+               case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY:
+                       len = snprintf( ptr, blen, "modify" );
+                       ptr += len;
+                       blen -= len;
+                       break;
+               case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME:
+                       len = snprintf( ptr, blen, "moddn" );
+                       ptr += len;
+                       blen -= len;
+                       if ( prevdn.bv_val != NULL ) {
+                               len = snprintf( ptr, blen, " prevdn %s", prevdn.bv_val );
+                               ptr += len;
+                               blen -= len;
+                       }
+                       break;
+               }
+               if ( chgpres ) {
+                       len = snprintf( ptr, blen, " changeNumber %ld", chgnum) ;
+                       ptr += len;
+                       blen -= len;
+               }
+
+               tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+                       ldif ? "persistentSearch: " : "persistentSearch", buf, len );
+       }
+}
+
 static int
 print_sss( LDAP *ld, LDAPControl *ctrl )
 {
index 696837ff63fe5e282dbea9de712fe8d40d731e65..e0ee59f9b4ac9f530564357c5ffa78c82186a888 100644 (file)
@@ -129,6 +129,7 @@ usage( void )
        fprintf( stderr, _("             !dontUseCopy                (Don't Use Copy)\n"));
        fprintf( stderr, _("             [!]mv=<filter>              (RFC 3876 matched values filter)\n"));
        fprintf( stderr, _("             [!]pr=<size>[/prompt|noprompt] (RFC 2696 paged results/prompt)\n"));
+       fprintf( stderr, _("             [!]ps=<changetypes>/<changesonly>/<echg> (draft persisten search)\n"));
        fprintf( stderr, _("             [!]sss=[-]<attr[:OID]>[/[-]<attr[:OID]>...]\n"));
        fprintf( stderr, _("                                         (RFC 2891 server side sorting)\n"));
        fprintf( stderr, _("             [!]subentries[=true|false]  (RFC 3672 subentries)\n"));
@@ -237,6 +238,9 @@ static int ldapsync = 0;
 static struct berval sync_cookie = { 0, NULL };
 static int sync_slimit = -1;
 
+static int psearch = 0;
+static int ps_chgtypes, ps_chgsonly, ps_echg_ctrls;
+
 /* cookie and morePagedResults moved to common.c */
 static int pagedResults = 0;
 static int pagePrompt = 1;
@@ -467,6 +471,28 @@ handle_private_option( int i )
                        pageSize = (ber_int_t) tmp;
                        pagedResults = 1 + crit;
 
+               } else if ( strcasecmp( control, "ps" ) == 0 ) {
+                       int num, tmp;
+                       /* PersistentSearch control */
+                       if ( psearch != 0 ) {
+                               fprintf( stderr,
+                                       _("PersistentSearch previously specified\n") );
+                               exit( EXIT_FAILURE );
+                       }
+                       if( cvalue != NULL ) {
+                               num = sscanf( cvalue, "%i/%d/%d", &ps_chgtypes, &ps_chgsonly, &ps_echg_ctrls );
+                               if ( num != 3 ) {
+                                       fprintf( stderr,
+                                               _("Invalid value for PersistentSearch, %s.\n"),
+                                               cvalue );
+                                       exit( EXIT_FAILURE );
+                               }
+                       } else {
+                               fprintf(stderr, _("Invalid value for PersistentSearch.\n"));
+                               exit( EXIT_FAILURE );
+                       }
+                       psearch = 1 + crit;
+
 #ifdef LDAP_CONTROL_DONTUSECOPY
                } else if ( strcasecmp( control, "dontUseCopy" ) == 0 ) {
                        if( dontUseCopy ) {
@@ -1097,6 +1123,7 @@ getNextPage:
 #endif
                || domainScope
                || pagedResults
+               || psearch
                || ldapsync
                || sss
                || subentries
@@ -1233,6 +1260,22 @@ getNextPage:
                        i++;
                }
 
+               if ( psearch ) {
+                       if ( ctrl_add() ) {
+                               tool_exit( ld, EXIT_FAILURE );
+                       }
+
+                       if ( ldap_create_persistentsearch_control_value( ld,
+                               ps_chgtypes, ps_chgsonly, ps_echg_ctrls, &c[i].ldctl_value ) )
+                       {
+                               tool_exit( ld, EXIT_FAILURE );
+                       }
+
+                       c[i].ldctl_oid = LDAP_CONTROL_PERSIST_REQUEST;
+                       c[i].ldctl_iscritical = psearch > 1;
+                       i++;
+               }
+
                if ( sss ) {
                        if ( ctrl_add() ) {
                                tool_exit( ld, EXIT_FAILURE );
index 0359a757ff757122c18f481405965e7759c14ff7..9f3351025b900b1afea2c4217770748a08e0effd 100644 (file)
@@ -2679,6 +2679,37 @@ ldap_parse_deref_control LDAP_P((
        LDAPControl     **ctrls,
        LDAPDerefRes    **drp ));
 
+/*
+ * in psearch.c
+ */
+
+LDAP_F( int )
+ldap_create_persistentsearch_control_value LDAP_P((
+       LDAP *ld,
+       int changetypes,
+       int changesonly,
+       int return_echg_ctls,
+       struct berval *value ));
+
+LDAP_F( int )
+ldap_create_persistentsearch_control LDAP_P((
+       LDAP *ld,
+       int changetypes,
+       int changesonly,
+       int return_echg_ctls,
+       int isCritical,
+       LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_entrychange_control LDAP_P((
+       LDAP *ld,
+       LDAPControl *ctrl,
+       int *chgtypep,
+       struct berval *prevdnp,
+       int *chgnumpresentp,
+       long *chgnump ));
+
+
 /*
  * high level LDIF to LDAP structure support
  */
index 9449f40c297a825a92b9b793d4ec3e723475e6da..399e85381f6d7c5c69e306814de328ac28eb028d 100644 (file)
@@ -29,7 +29,7 @@ SRCS  = bind.c open.c result.c error.c compare.c search.c \
        tls2.c tls_o.c tls_g.c tls_m.c \
        turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
        assertion.c deref.c ldifutil.c ldif.c fetch.c lbase64.c \
-       msctrl.c
+       msctrl.c psearchctrl.c
 
 OBJS   = bind.lo open.lo result.lo error.lo compare.lo search.lo \
        controls.lo messages.lo references.lo extended.lo cyrus.lo \
@@ -43,7 +43,7 @@ OBJS  = bind.lo open.lo result.lo error.lo compare.lo search.lo \
        tls2.lo tls_o.lo tls_g.lo tls_m.lo \
        turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
        assertion.lo deref.lo ldifutil.lo ldif.lo fetch.lo lbase64.lo \
-       msctrl.lo
+       msctrl.lo psearchctrl.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries
diff --git a/libraries/libldap/psearchctrl.c b/libraries/libldap/psearchctrl.c
new file mode 100644 (file)
index 0000000..6050477
--- /dev/null
@@ -0,0 +1,348 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2019 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* Based on draft-ietf-ldapext-c-api-psearch-00 */
+
+/* ---------------------------------------------------------------------------
+   ldap_create_persistentsearch_control_value
+   
+   Create and encode the value of the server-side sort control.
+   
+   ld          (IN) An LDAP session handle, as obtained from a call to
+                                       ldap_init().
+
+   changetypes (IN) A bit-sensitive field that indicates which kinds of
+                                       changes the client wants to be informed about.  Its
+                                       value should be LDAP_CHANGETYPE_ANY, or any logical-OR
+                                       combination of LDAP_CHANGETYPE_ADD,
+                                       LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
+                                       LDAP_CHANGETYPE_MODDN.  This field corresponds to the
+                                       changeType element of the BER-encoded PersistentSearch
+                                       control value itself.
+
+   changesonly (IN) A Boolean field that indicates whether the client
+                                       wishes to only receive searchResultEntry messages for
+                                       entries that have been changed. If non-zero, only
+                                       entries that result from changes are returned; other-
+                                       wise, all of the static entries that match the search
+                                       criteria are returned before the server begins change
+                                       notification.  This field corresponds to the changes-
+                                       Only element of the BER-encoded PersistentSearch con-
+                                       trol value itself.
+
+   return_echg_ctls (IN) A Boolean field that indicates whether the server
+                                       should send back an Entry Change Notification control
+                                       with each searchResultEntry that is returned due to a
+                                       change to an entry.  If non-zero, Entry Change
+                                       Notification controls are requested; if zero, they are
+                                       not.  This field corresponds to the returnECs element
+                                       of the BER-encoded PersistentSearch control value
+                                       itself.
+                          
+   value      (OUT) Contains the control value; the bv_val member of the berval structure
+                                       SHOULD be freed by calling ldap_memfree() when done.
+   
+   ---------------------------------------------------------------------------*/
+
+int
+ldap_create_persistentsearch_control_value(
+       LDAP *ld,
+       int changetypes,
+       int changesonly,
+       int return_echg_ctls,
+       struct berval *value )
+{
+       int             i;
+       BerElement      *ber = NULL;
+       ber_tag_t       tag;
+
+       assert( ld != NULL );
+       assert( LDAP_VALID( ld ) );
+
+       if ( ld == NULL ) return LDAP_PARAM_ERROR;
+       if ( value == NULL ) {
+               ld->ld_errno = LDAP_PARAM_ERROR;
+               return LDAP_PARAM_ERROR;
+       }
+       if (( changetypes & 0x0f ) != changetypes ) {
+               ld->ld_errno = LDAP_PARAM_ERROR;
+               return LDAP_PARAM_ERROR;
+       }
+
+       value->bv_val = NULL;
+       value->bv_len = 0;
+       ld->ld_errno = LDAP_SUCCESS;
+
+       ber = ldap_alloc_ber_with_options( ld );
+       if ( ber == NULL) {
+               ld->ld_errno = LDAP_NO_MEMORY;
+               return ld->ld_errno;
+       }
+
+       tag = ber_printf( ber, "{ibb}", changetypes, changesonly, return_echg_ctls );
+       if ( tag == LBER_ERROR ) {
+               goto error_return;
+       }
+
+       if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+               ld->ld_errno = LDAP_NO_MEMORY;
+       }
+
+       if ( 0 ) {
+error_return:;
+               ld->ld_errno =  LDAP_ENCODING_ERROR;
+       }
+
+       if ( ber != NULL ) {
+               ber_free( ber, 1 );
+       }
+
+       return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+   ldap_create_persistentsearch_control
+   
+   Create and encode the persistent search control.
+   
+   ld          (IN) An LDAP session handle, as obtained from a call to
+                                       ldap_init().
+
+   changetypes (IN) A bit-sensitive field that indicates which kinds of
+                                       changes the client wants to be informed about.  Its
+                                       value should be LDAP_CHANGETYPE_ANY, or any logical-OR
+                                       combination of LDAP_CHANGETYPE_ADD,
+                                       LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
+                                       LDAP_CHANGETYPE_MODDN.  This field corresponds to the
+                                       changeType element of the BER-encoded PersistentSearch
+                                       control value itself.
+
+   changesonly (IN) A Boolean field that indicates whether the client
+                                       wishes to only receive searchResultEntry messages for
+                                       entries that have been changed. If non-zero, only
+                                       entries that result from changes are returned; other-
+                                       wise, all of the static entries that match the search
+                                       criteria are returned before the server begins change
+                                       notification.  This field corresponds to the changes-
+                                       Only element of the BER-encoded PersistentSearch con-
+                                       trol value itself.
+
+   return_echg_ctls (IN) A Boolean field that indicates whether the server
+                                       should send back an Entry Change Notification control
+                                       with each searchResultEntry that is returned due to a
+                                       change to an entry.  If non-zero, Entry Change
+                                       Notification controls are requested; if zero, they are
+                                       not.  This field corresponds to the returnECs element
+                                       of the BER-encoded PersistentSearch control value
+                                       itself.
+
+   isCritical  (IN) 0 - Indicates the control is not critical to the operation.
+                                       non-zero - The control is critical to the operation.
+                                        
+   ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
+                                       SHOULD be freed by calling ldap_control_free() when done.
+   
+   ---------------------------------------------------------------------------*/
+
+int
+ldap_create_persistentsearch_control(
+       LDAP *ld,
+       int changetypes,
+       int changesonly,
+       int return_echg_ctls,
+       int isCritical,
+       LDAPControl **ctrlp )
+{
+       struct berval   value;
+
+       assert( ld != NULL );
+       assert( LDAP_VALID( ld ) );
+
+       if ( ld == NULL ) {
+               return LDAP_PARAM_ERROR;
+       }
+
+       if ( ctrlp == NULL ) {
+               ld->ld_errno = LDAP_PARAM_ERROR;
+               return ld->ld_errno;
+       }
+
+       ld->ld_errno = ldap_create_persistentsearch_control_value( ld, changetypes, changesonly, return_echg_ctls, &value );
+       if ( ld->ld_errno == LDAP_SUCCESS ) {
+               ld->ld_errno = ldap_control_create( LDAP_CONTROL_PERSIST_REQUEST,
+                       isCritical, &value, 0, ctrlp );
+               if ( ld->ld_errno != LDAP_SUCCESS ) {
+                       LDAP_FREE( value.bv_val );
+               }
+       }
+
+       return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+   ldap_parse_entrychange_control
+   
+   Decode the entry change notification control return information.
+
+   ld          (IN) An LDAP session handle, as obtained from a call to
+                                       ldap_init().
+
+   ctrl        (IN) The address of the LDAP Control Structure.
+
+   chgtypep   (OUT) This result parameter is filled in with one of the
+                                       following values to indicate the type of change that was
+                                       made that caused the entry to be returned:
+                                       LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD (1),
+                                       LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE (2),
+                                       LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY (4), or
+                                       LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME (8).
+                                       If this parameter is NULL, the change type information
+                                       is not returned. 
+
+   prevdnp    (OUT) This result parameter points to the DN the
+                                       entry had before it was renamed and/or moved by a
+                                       modifyDN operation. It is set to NULL for other types
+                                       of changes. If this parameter is NULL, the previous DN
+                                       information is not returned. The returned value is a
+                                       pointer to the contents of the control; it is not a
+                                       copy of the data.
+
+   chgnumpresentp (OUT) This result parameter is filled in with a non-zero
+                                       value if a change number was returned in the control
+                                       (the change number is optional and servers MAY choose
+                                       not to return it). If this parameter is NULL, no indication
+                                       of whether the change number was present is returned.
+
+   chgnump    (OUT) This result paramter is filled in with the change number
+                                       if one was returned in the control. If this parameter
+                                       is NULL, the change number is not returned.
+   
+   ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_entrychange_control(
+       LDAP *ld,
+       LDAPControl *ctrl,
+       int *chgtypep,
+       struct berval *prevdnp,
+       int *chgnumpresentp,
+       long *chgnump )
+{
+       BerElement *ber;
+       ber_tag_t tag, berTag;
+       ber_len_t berLen;
+       ber_int_t chgtype;
+
+       assert( ld != NULL );
+       assert( LDAP_VALID( ld ) );
+       assert( ctrl != NULL );
+
+       if (ld == NULL) {
+               return LDAP_PARAM_ERROR;
+       }
+
+       if (ctrl == NULL) {
+               ld->ld_errno =  LDAP_PARAM_ERROR;
+               return(ld->ld_errno);
+       }
+
+       if ( !ctrl->ldctl_value.bv_val ) {
+               ld->ld_errno = LDAP_DECODING_ERROR;
+               return(ld->ld_errno);
+       }
+
+       /* Create a BerElement from the berval returned in the control. */
+       ber = ber_init(&ctrl->ldctl_value);
+
+       if (ber == NULL) {
+               ld->ld_errno = LDAP_NO_MEMORY;
+               return(ld->ld_errno);
+       }
+
+       if ( prevdnp != NULL ) {
+               BER_BVZERO( prevdnp );
+       }
+       if ( chgnumpresentp != NULL )
+               *chgnumpresentp = 0;
+       if ( chgnump != NULL )
+               *chgnump = 0;
+
+       /* Extract the change type from the control. */
+       tag = ber_scanf(ber, "{e" /*}*/, &chgtype);
+
+       if( tag != LBER_ENUMERATED ) {
+               ber_free(ber, 1);
+               ld->ld_errno = LDAP_DECODING_ERROR;
+               return(ld->ld_errno);
+       }
+       if ( chgtypep != NULL )
+               *chgtypep = chgtype;
+
+       tag = ber_peek_tag( ber, &berLen );
+       if ( berLen ) {
+               if (tag == LBER_OCTETSTRING) {
+                       if (prevdnp != NULL) {
+                               tag = ber_get_stringbv( ber, prevdnp, 0 );
+                       } else {
+                               struct berval bv;
+                               tag = ber_skip_element( ber, &bv );
+                       }
+                       if ( tag == LBER_ERROR ) {
+                               ber_free(ber, 1);
+                               ld->ld_errno = LDAP_DECODING_ERROR;
+                               return(ld->ld_errno);
+                       }
+                       tag = ber_peek_tag( ber, &berLen );
+               }
+
+               if ( chgnumpresentp != NULL || chgnump != NULL ) {
+                       ber_int_t chgnum = 0;
+                       int present = 0;
+                       if (tag == LBER_INTEGER) {
+                               present = 1;
+                               tag = ber_get_int( ber, &chgnum );
+                               if ( tag == LBER_ERROR ) {
+                                       ber_free(ber, 1);
+                                       ld->ld_errno = LDAP_DECODING_ERROR;
+                                       return(ld->ld_errno);
+                               }
+                               if ( chgnumpresentp != NULL )
+                                       *chgnumpresentp = present;
+                               if ( chgnump != NULL )
+                                       *chgnump = chgnum;
+                       }
+               }
+       }
+
+       ber_free(ber,1);
+
+       ld->ld_errno = LDAP_SUCCESS;
+       return(ld->ld_errno);
+}
index a3e08962936d14a0e0ba087f75424538ee1a67c4..527bc691d04298c74422c25e4624133d76ace179 100644 (file)
@@ -31,7 +31,7 @@ XXSRCS    = apitest.c test.c \
        tls2.c tls_o.c tls_g.c tls_m.c \
        turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
        assertion.c deref.c ldifutil.c ldif.c fetch.c lbase64.c \
-       msctrl.c
+       msctrl.c psearchctrl.c
 SRCS   = threads.c rdwr.c tpool.c rq.c \
        thr_posix.c thr_thr.c thr_nt.c \
        thr_pth.c thr_stub.c thr_debug.c
@@ -50,7 +50,7 @@ OBJS  = threads.lo rdwr.lo tpool.lo  rq.lo \
        tls2.lo tls_o.lo tls_g.lo tls_m.lo \
        turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
        assertion.lo deref.lo ldifutil.lo ldif.lo fetch.lo lbase64.lo \
-       msctrl.lo
+       msctrl.lo psearchctrl.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries