Home | History | Annotate | Line # | Download | only in libldap
      1 /*	$NetBSD: psearchctrl.c,v 1.3 2025/09/05 21:16:21 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 /* ACKNOWLEDGEMENTS:
     18  * This work was developed by Howard Chu for inclusion in
     19  * OpenLDAP Software.
     20  */
     21 
     22 #include <sys/cdefs.h>
     23 __RCSID("$NetBSD: psearchctrl.c,v 1.3 2025/09/05 21:16:21 christos Exp $");
     24 
     25 #include "portable.h"
     26 
     27 #include <stdio.h>
     28 #include <ac/stdlib.h>
     29 #include <ac/string.h>
     30 #include <ac/time.h>
     31 
     32 #include "ldap-int.h"
     33 
     34 /* Based on draft-ietf-ldapext-c-api-psearch-00 */
     35 
     36 /* ---------------------------------------------------------------------------
     37    ldap_create_persistentsearch_control_value
     38 
     39    Create and encode the value of the server-side sort control.
     40 
     41    ld          (IN) An LDAP session handle, as obtained from a call to
     42 					ldap_init().
     43 
     44    changetypes (IN) A bit-sensitive field that indicates which kinds of
     45 					changes the client wants to be informed about.  Its
     46 					value should be LDAP_CHANGETYPE_ANY, or any logical-OR
     47 					combination of LDAP_CHANGETYPE_ADD,
     48 					LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
     49 					LDAP_CHANGETYPE_MODDN.  This field corresponds to the
     50 					changeType element of the BER-encoded PersistentSearch
     51 					control value itself.
     52 
     53    changesonly (IN) A Boolean field that indicates whether the client
     54 					wishes to only receive searchResultEntry messages for
     55 					entries that have been changed. If non-zero, only
     56 					entries that result from changes are returned; other-
     57 					wise, all of the static entries that match the search
     58 					criteria are returned before the server begins change
     59 					notification.  This field corresponds to the changes-
     60 					Only element of the BER-encoded PersistentSearch con-
     61 					trol value itself.
     62 
     63    return_echg_ctls (IN) A Boolean field that indicates whether the server
     64 					should send back an Entry Change Notification control
     65 					with each searchResultEntry that is returned due to a
     66 					change to an entry.  If non-zero, Entry Change
     67 					Notification controls are requested; if zero, they are
     68 					not.  This field corresponds to the returnECs element
     69 					of the BER-encoded PersistentSearch control value
     70 					itself.
     71 
     72    value      (OUT) Contains the control value; the bv_val member of the berval structure
     73 					SHOULD be freed by calling ldap_memfree() when done.
     74 
     75    ---------------------------------------------------------------------------*/
     76 
     77 int
     78 ldap_create_persistentsearch_control_value(
     79 	LDAP *ld,
     80 	int changetypes,
     81 	int changesonly,
     82 	int return_echg_ctls,
     83 	struct berval *value )
     84 {
     85 	int		i;
     86 	BerElement	*ber = NULL;
     87 	ber_tag_t	tag;
     88 
     89 	assert( ld != NULL );
     90 	assert( LDAP_VALID( ld ) );
     91 
     92 	if ( ld == NULL ) return LDAP_PARAM_ERROR;
     93 	if ( value == NULL ) {
     94 		ld->ld_errno = LDAP_PARAM_ERROR;
     95 		return LDAP_PARAM_ERROR;
     96 	}
     97 	if (( changetypes & 0x0f ) != changetypes ) {
     98 		ld->ld_errno = LDAP_PARAM_ERROR;
     99 		return LDAP_PARAM_ERROR;
    100 	}
    101 
    102 	value->bv_val = NULL;
    103 	value->bv_len = 0;
    104 	ld->ld_errno = LDAP_SUCCESS;
    105 
    106 	ber = ldap_alloc_ber_with_options( ld );
    107 	if ( ber == NULL) {
    108 		ld->ld_errno = LDAP_NO_MEMORY;
    109 		return ld->ld_errno;
    110 	}
    111 
    112 	tag = ber_printf( ber, "{ibb}", changetypes, changesonly, return_echg_ctls );
    113 	if ( tag == LBER_ERROR ) {
    114 		goto error_return;
    115 	}
    116 
    117 	if ( ber_flatten2( ber, value, 1 ) == -1 ) {
    118 		ld->ld_errno = LDAP_NO_MEMORY;
    119 	}
    120 
    121 	if ( 0 ) {
    122 error_return:;
    123 		ld->ld_errno =  LDAP_ENCODING_ERROR;
    124 	}
    125 
    126 	if ( ber != NULL ) {
    127 		ber_free( ber, 1 );
    128 	}
    129 
    130 	return ld->ld_errno;
    131 }
    132 
    133 
    134 /* ---------------------------------------------------------------------------
    135    ldap_create_persistentsearch_control
    136 
    137    Create and encode the persistent search control.
    138 
    139    ld          (IN) An LDAP session handle, as obtained from a call to
    140 					ldap_init().
    141 
    142    changetypes (IN) A bit-sensitive field that indicates which kinds of
    143 					changes the client wants to be informed about.  Its
    144 					value should be LDAP_CHANGETYPE_ANY, or any logical-OR
    145 					combination of LDAP_CHANGETYPE_ADD,
    146 					LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
    147 					LDAP_CHANGETYPE_MODDN.  This field corresponds to the
    148 					changeType element of the BER-encoded PersistentSearch
    149 					control value itself.
    150 
    151    changesonly (IN) A Boolean field that indicates whether the client
    152 					wishes to only receive searchResultEntry messages for
    153 					entries that have been changed. If non-zero, only
    154 					entries that result from changes are returned; other-
    155 					wise, all of the static entries that match the search
    156 					criteria are returned before the server begins change
    157 					notification.  This field corresponds to the changes-
    158 					Only element of the BER-encoded PersistentSearch con-
    159 					trol value itself.
    160 
    161    return_echg_ctls (IN) A Boolean field that indicates whether the server
    162 					should send back an Entry Change Notification control
    163 					with each searchResultEntry that is returned due to a
    164 					change to an entry.  If non-zero, Entry Change
    165 					Notification controls are requested; if zero, they are
    166 					not.  This field corresponds to the returnECs element
    167 					of the BER-encoded PersistentSearch control value
    168 					itself.
    169 
    170    isCritical  (IN) 0 - Indicates the control is not critical to the operation.
    171 					non-zero - The control is critical to the operation.
    172 
    173    ctrlp      (OUT) Returns a pointer to the LDAPControl created.  This control
    174 					SHOULD be freed by calling ldap_control_free() when done.
    175 
    176    ---------------------------------------------------------------------------*/
    177 
    178 int
    179 ldap_create_persistentsearch_control(
    180 	LDAP *ld,
    181 	int changetypes,
    182 	int changesonly,
    183 	int return_echg_ctls,
    184 	int isCritical,
    185 	LDAPControl **ctrlp )
    186 {
    187 	struct berval	value;
    188 
    189 	assert( ld != NULL );
    190 	assert( LDAP_VALID( ld ) );
    191 
    192 	if ( ld == NULL ) {
    193 		return LDAP_PARAM_ERROR;
    194 	}
    195 
    196 	if ( ctrlp == NULL ) {
    197 		ld->ld_errno = LDAP_PARAM_ERROR;
    198 		return ld->ld_errno;
    199 	}
    200 
    201 	ld->ld_errno = ldap_create_persistentsearch_control_value( ld, changetypes, changesonly, return_echg_ctls, &value );
    202 	if ( ld->ld_errno == LDAP_SUCCESS ) {
    203 		ld->ld_errno = ldap_control_create( LDAP_CONTROL_PERSIST_REQUEST,
    204 			isCritical, &value, 0, ctrlp );
    205 		if ( ld->ld_errno != LDAP_SUCCESS ) {
    206 			LDAP_FREE( value.bv_val );
    207 		}
    208 	}
    209 
    210 	return ld->ld_errno;
    211 }
    212 
    213 
    214 /* ---------------------------------------------------------------------------
    215    ldap_parse_entrychange_control
    216 
    217    Decode the entry change notification control return information.
    218 
    219    ld          (IN) An LDAP session handle, as obtained from a call to
    220 					ldap_init().
    221 
    222    ctrl        (IN) The address of the LDAP Control Structure.
    223 
    224    chgtypep   (OUT) This result parameter is filled in with one of the
    225 					following values to indicate the type of change that was
    226 					made that caused the entry to be returned:
    227 					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD (1),
    228 					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE (2),
    229 					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY (4), or
    230 					LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME (8).
    231 					If this parameter is NULL, the change type information
    232 					is not returned.
    233 
    234    prevdnp    (OUT) This result parameter points to the DN the
    235    					entry had before it was renamed and/or moved by a
    236 					modifyDN operation. It is set to NULL for other types
    237 					of changes. If this parameter is NULL, the previous DN
    238 					information is not returned. The returned value is a
    239 					pointer to the contents of the control; it is not a
    240 					copy of the data.
    241 
    242    chgnumpresentp (OUT) This result parameter is filled in with a non-zero
    243    					value if a change number was returned in the control
    244 					(the change number is optional and servers MAY choose
    245 					not to return it). If this parameter is NULL, no indication
    246 					of whether the change number was present is returned.
    247 
    248    chgnump    (OUT) This result parameter is filled in with the change number
    249    					if one was returned in the control. If this parameter
    250 					is NULL, the change number is not returned.
    251 
    252    ---------------------------------------------------------------------------*/
    253 
    254 int
    255 ldap_parse_entrychange_control(
    256 	LDAP *ld,
    257 	LDAPControl *ctrl,
    258 	int *chgtypep,
    259 	struct berval *prevdnp,
    260 	int *chgnumpresentp,
    261 	long *chgnump )
    262 {
    263 	BerElement *ber;
    264 	ber_tag_t tag, berTag;
    265 	ber_len_t berLen;
    266 	ber_int_t chgtype;
    267 
    268 	assert( ld != NULL );
    269 	assert( LDAP_VALID( ld ) );
    270 	assert( ctrl != NULL );
    271 
    272 	if (ld == NULL) {
    273 		return LDAP_PARAM_ERROR;
    274 	}
    275 
    276 	if (ctrl == NULL) {
    277 		ld->ld_errno =  LDAP_PARAM_ERROR;
    278 		return(ld->ld_errno);
    279 	}
    280 
    281 	if ( !ctrl->ldctl_value.bv_val ) {
    282 		ld->ld_errno = LDAP_DECODING_ERROR;
    283 		return(ld->ld_errno);
    284 	}
    285 
    286 	/* Create a BerElement from the berval returned in the control. */
    287 	ber = ber_init(&ctrl->ldctl_value);
    288 
    289 	if (ber == NULL) {
    290 		ld->ld_errno = LDAP_NO_MEMORY;
    291 		return(ld->ld_errno);
    292 	}
    293 
    294 	if ( prevdnp != NULL ) {
    295 		BER_BVZERO( prevdnp );
    296 	}
    297 	if ( chgnumpresentp != NULL )
    298 		*chgnumpresentp = 0;
    299 	if ( chgnump != NULL )
    300 		*chgnump = 0;
    301 
    302 	/* Extract the change type from the control. */
    303 	tag = ber_scanf(ber, "{e" /*}*/, &chgtype);
    304 
    305 	if( tag != LBER_ENUMERATED ) {
    306 		ber_free(ber, 1);
    307 		ld->ld_errno = LDAP_DECODING_ERROR;
    308 		return(ld->ld_errno);
    309 	}
    310 	if ( chgtypep != NULL )
    311 		*chgtypep = chgtype;
    312 
    313 	tag = ber_peek_tag( ber, &berLen );
    314 	if ( berLen ) {
    315 		if (tag == LBER_OCTETSTRING) {
    316 			if (prevdnp != NULL) {
    317 				tag = ber_get_stringbv( ber, prevdnp, 0 );
    318 			} else {
    319 				struct berval bv;
    320 				tag = ber_skip_element( ber, &bv );
    321 			}
    322 			if ( tag == LBER_ERROR ) {
    323 				ber_free(ber, 1);
    324 				ld->ld_errno = LDAP_DECODING_ERROR;
    325 				return(ld->ld_errno);
    326 			}
    327 			tag = ber_peek_tag( ber, &berLen );
    328 		}
    329 
    330 		if ( chgnumpresentp != NULL || chgnump != NULL ) {
    331 			ber_int_t chgnum = 0;
    332 			int present = 0;
    333 			if (tag == LBER_INTEGER) {
    334 				present = 1;
    335 				tag = ber_get_int( ber, &chgnum );
    336 				if ( tag == LBER_ERROR ) {
    337 					ber_free(ber, 1);
    338 					ld->ld_errno = LDAP_DECODING_ERROR;
    339 					return(ld->ld_errno);
    340 				}
    341 				if ( chgnumpresentp != NULL )
    342 					*chgnumpresentp = present;
    343 				if ( chgnump != NULL )
    344 					*chgnump = chgnum;
    345 			}
    346 		}
    347 	}
    348 
    349 	ber_free(ber,1);
    350 
    351 	ld->ld_errno = LDAP_SUCCESS;
    352 	return(ld->ld_errno);
    353 }
    354