Index: clients/tools/ldapsearch.c =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/clients/tools/ldapsearch.c,v retrieving revision 1.134 diff -u -r1.134 ldapsearch.c --- clients/tools/ldapsearch.c 21 Nov 2002 21:13:07 -0000 1.134 +++ clients/tools/ldapsearch.c 21 Nov 2002 23:26:09 -0000 @@ -34,6 +34,7 @@ #include "lutil_ldap.h" #include "ldap_defaults.h" #include "ldap_log.h" +#include "ldap_pvt.h" static char *def_tmpdir; static char *def_urlpre; @@ -62,6 +63,9 @@ #ifdef LDAP_CONTROL_SUBENTRIES " [!]subentries[=true|false] (subentries)\n" #endif +#ifdef LDAP_CONTROL_DUP_ENT +" [!]dupent=attr[,attr[...]]\n" +#endif /* LDAP_CONTROL_DUP_ENT */ " -F prefix URL prefix for files (default: %s)\n" " -l limit time limit (in seconds) for search\n" " -L print responses in LDIFv1 format\n" @@ -226,6 +230,10 @@ int num = 0, searchControlCrit = 0; #endif /* LDAP_CONTROL_PAGEDRESULTS */ +#ifdef LDAP_CONTROL_DUP_ENT + int dupentCtrl = 0; + char **dupent_attrs = NULL; +#endif /* LDAP_CONTROL_DUP_ENT */ infile = NULL; debug = verbose = not = vals2tmp = referrals = @@ -332,7 +340,6 @@ vrFilter = cvalue; version = LDAP_VERSION3; - break; #ifdef LDAP_CONTROL_PAGEDRESULTS } else if ( strcasecmp( control, "pr" ) == 0 ) { @@ -350,7 +357,6 @@ } searchControlSize = (ber_int_t)pageSize; searchControlCrit = crit; - break; #endif /* LDAP_CONTROL_PAGEDRESULTS */ #ifdef LDAP_CONTROL_SUBENTRIES @@ -371,11 +377,24 @@ if( crit ) subentries *= -1; #endif +#ifdef LDAP_CONTROL_DUP_ENT + } else if ( strcasecmp( control, "dupent" ) == 0 ) { + /* Duplicate Entry Control */ + if (dupentCtrl != 0) { + fprintf( stderr, "Dupent previously specified"); + return EXIT_FAILURE; + } + + dupent_attrs = ldap_str2charray( cvalue, "," ); + dupentCtrl++; +#endif /* LDAP_CONTROL_DUP_ENT */ + } else { fprintf( stderr, "Invalid control name: %s\n", control ); usage(prog); return EXIT_FAILURE; } + break; case 'f': /* input file */ if( infile != NULL ) { fprintf( stderr, "%s: -f previously specified\n", prog ); @@ -506,6 +525,7 @@ } if( ldaphost != NULL ) { fprintf( stderr, "%s: -h previously specified\n", prog ); + fprintf( stderr, "previous host value=%s\n", ldaphost ); return EXIT_FAILURE; } ldaphost = strdup( optarg ); @@ -1024,19 +1044,23 @@ } } -#ifdef LDAP_CONTROL_PAGEDRESULTS getNextPage: - if ( manageDSAit || noop || valuesReturnFilter || pageSize ) { - int critical = 0; -#else /* !LDAP_CONTROL_PAGEDRESULTS */ - if ( manageDSAit || noop || valuesReturnFilter ) { + if ( manageDSAit || noop || valuesReturnFilter +#ifdef LDAP_CONTROL_PAGEDRESULTS + || pageSize #endif /* !LDAP_CONTROL_PAGEDRESULTS */ +#ifdef LDAP_CONTROL_DUP_ENT + || dupentCtrl +#endif /* LDAP_CONTROL_DUP_ENT */ + ) { int err; int i=0; - LDAPControl c1,c2,c3,c4,c5; - LDAPControl *ctrls[6]; + LDAPControl c1,c2,c3,c4,c5,c6; + LDAPControl *ctrls[7]; + int critical = 0, controls = 0; if ( manageDSAit ) { + controls++; ctrls[i++]=&c1; ctrls[i] = NULL; @@ -1044,12 +1068,11 @@ c1.ldctl_value.bv_val = NULL; c1.ldctl_value.bv_len = 0; c1.ldctl_iscritical = manageDSAit > 1; -#ifdef LDAP_CONTROL_PAGEDRESULTS - if ( c1.ldctl_iscritical ) critical = 1; -#endif /* LDAP_CONTROL_PAGEDRESULTS */ + if ( c1.ldctl_iscritical ) critical++; } if ( noop ) { + controls++; ctrls[i++] = &c2; ctrls[i] = NULL; @@ -1057,30 +1080,29 @@ c2.ldctl_value.bv_val = NULL; c2.ldctl_value.bv_len = 0; c2.ldctl_iscritical = noop > 1; -#ifdef LDAP_CONTROL_PAGEDRESULTS - if ( c2.ldctl_iscritical ) critical = 1; -#endif /* LDAP_CONTROL_PAGEDRESULTS */ + if ( c2.ldctl_iscritical ) critical++; } #ifdef LDAP_CONTROL_SUBENTRIES if ( subentries ) { + controls++; ctrls[i++]=&c3; ctrls[i] = NULL; c3.ldctl_oid = LDAP_CONTROL_SUBENTRIES; c3.ldctl_iscritical = subentries < 1; -#ifdef LDAP_CONTROL_PAGEDRESULTS - if ( c3.ldctl_iscritical ) critical = 1; -#endif /* LDAP_CONTROL_PAGEDRESULTS */ + if ( c3.ldctl_iscritical ) critical++; - if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { return EXIT_FAILURE; } - err = ber_printf( ber, "{b}", abs(subentries) == 1 ? 0 : 1 ); - if ( err == LBER_ERROR ) { + err = ber_printf( ber, "{b}", + abs(subentries) == 1 ? 0 : 1 ); + if ( err == LBER_ERROR ) { ber_free( ber, 1 ); - fprintf( stderr, "Subentries control encoding error!\n" ); + fprintf( stderr, "Subentries control " + "encoding error!\n" ); return EXIT_FAILURE; } @@ -1093,22 +1115,23 @@ #endif if ( valuesReturnFilter ) { + controls++; ctrls[i++]=&c4; ctrls[i] = NULL; c4.ldctl_oid = LDAP_CONTROL_VALUESRETURNFILTER; c4.ldctl_iscritical = valuesReturnFilter > 1; -#ifdef LDAP_CONTROL_PAGEDRESULTS - if ( c4.ldctl_iscritical ) critical = 1; -#endif /* LDAP_CONTROL_PAGEDRESULTS */ + if ( c4.ldctl_iscritical ) critical++; - if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { + if (( ber = ber_alloc_t(LBER_USE_DER)) == NULL ) { return EXIT_FAILURE; } - if ( ( err = ldap_put_vrFilter( ber, vrFilter ) ) == -1 ) { + err = ldap_put_vrFilter( ber, vrFilter ); + if ( err == -1 ) { ber_free( ber, 1 ); - fprintf( stderr, "Bad ValuesReturnFilter: %s\n", vrFilter ); + fprintf( stderr, "Bad ValuesReturnFilter: " + "%s\n", vrFilter ); return EXIT_FAILURE; } @@ -1129,17 +1152,40 @@ if ( ber_flatten( pageber, &bvalptr ) == LBER_ERROR) { return EXIT_FAILURE; } - + + controls++; ctrls[i++]=&c5; ctrls[i] = NULL; c5.ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; c5.ldctl_value = ( *bvalptr ); c5.ldctl_iscritical = searchControlCrit; - if ( c5.ldctl_iscritical ) critical = 1; + if ( c5.ldctl_iscritical ) critical++; } #endif /* LDAP_CONTROL_PAGEDRESULTS */ +#ifdef LDAP_CONTROL_DUP_ENT + if ( dupentCtrl ) { + controls++; + ctrls[i++]=&c6; + ctrls[i] = NULL; + c6.ldctl_oid = LDAP_CONTROL_DUPENT_REQUEST; + c6.ldctl_value.bv_val = NULL; + c6.ldctl_value.bv_len = 0; + c6.ldctl_iscritical = dupentCtrl > 1; + if ( c6.ldctl_iscritical ) critical++; + + err = ldap_create_dupent_request( ld, dupent_attrs, + 1, 0, &c6 ); + if ( err != LDAP_SUCCESS ) { + fprintf(stderr, "Could not set " + "Duplicate Entry: " + "rc = %d\n", err); + exit( EXIT_FAILURE ); + } + } +#endif /* LDAP_CONTROL_DUP_ENT */ + err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls ); ber_bvfree(bvalp); @@ -1149,25 +1195,17 @@ ber_bvfree( bvalptr ); #endif /* LDAP_CONTROL_PAGEDRESULTS */ -#ifdef LDAP_CONTROL_PAGEDRESULTS - if( err != LDAP_OPT_SUCCESS ) { - if ( critical ) { - fprintf( stderr, "Could not set controls\n"); + if ( err != LDAP_OPT_SUCCESS ) { + if ( !critical ) { + fprintf( stderr, "Could not set %d " + "control(s)\n", controls ); return EXIT_FAILURE; } else { - fprintf( stderr, "Could not set critical controls\n" ); + fprintf( stderr, "Could not set %d control(s) " + "(%d critical)\n", + controls, critical ); } } -#else /* !LDAP_CONTROL_PAGEDRESULTS */ - if( err != LDAP_OPT_SUCCESS ) { - fprintf( stderr, "Could not set %scontrols\n", - (c1.ldctl_iscritical || c2.ldctl_iscritical) - ? "critical " : "" ); - if( c1.ldctl_iscritical && c2.ldctl_iscritical ) { - return EXIT_FAILURE; - } - } -#endif /* !LDAP_CONTROL_PAGEDRESULTS */ } if ( verbose ) { @@ -1883,6 +1921,7 @@ LDAPControl **ctrls ) { int i; + for(i=0; ctrls[i] != NULL; i++ ) { /* control: OID criticality base64value */ struct berval *b64 = NULL; Index: include/ldap.h =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/include/ldap.h,v retrieving revision 1.187 diff -u -r1.187 ldap.h --- include/ldap.h 21 Nov 2002 01:16:54 -0000 1.187 +++ include/ldap.h 21 Nov 2002 23:26:10 -0000 @@ -193,12 +193,13 @@ #define LDAP_CONTROL_SUBENTRIES "1.3.6.1.4.1.4203.1.10.1" #define LDAP_CONTROL_NOOP "1.3.6.1.4.1.4203.1.10.2" -#if 0 -#define LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1" -#define LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2" -#define LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3" -#define LDAP_CONTROL_DUPENT LDAP_CONTROL_DUPENT_REQUEST -#endif +#define LDAP_CONTROL_DUP_ENT +#ifdef LDAP_CONTROL_DUP_ENT +#define LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1" +#define LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.2" +#define LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.3" +#define LDAP_CONTROL_DUPENT LDAP_CONTROL_DUPENT_REQUEST +#endif /* LDAP_CONTROL_DUP_ENT */ #define LDAP_CONTROL_PAGEDRESULTS "1.2.840.113556.1.4.319" @@ -608,6 +609,24 @@ LDAP_F( void ) ldap_controls_free LDAP_P(( LDAPControl **ctrls )); + +/* + * in dupent_ctrl.c: + */ +LDAP_F( int ) +ldap_create_dupent_request LDAP_P(( + LDAP *ld, + char **attrs, + int partialAppAllowed, + int isCritical, + LDAPControl *ctrlp )); + +LDAP_F( int ) +ldap_parse_dupent_response LDAP_P(( + LDAPControl **ctrls, + unsigned long *returnCode, + char *errorMessage, + char *attribute )); /* * in dnssrv.c: Index: libraries/libldap/Makefile.in =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/libraries/libldap/Makefile.in,v retrieving revision 1.59 diff -u -r1.59 Makefile.in --- libraries/libldap/Makefile.in 8 Aug 2002 01:01:18 -0000 1.59 +++ libraries/libldap/Makefile.in 21 Nov 2002 23:26:10 -0000 @@ -11,7 +11,7 @@ SRCS = bind.c open.c result.c error.c compare.c search.c \ controls.c messages.c references.c extended.c cyrus.c \ modify.c add.c modrdn.c delete.c abandon.c cache.c \ - sasl.c sbind.c kbind.c unbind.c \ + sasl.c sbind.c kbind.c unbind.c dupent_ctrl.c \ filter.c free.c sort.c \ getdn.c getentry.c getattr.c getvalues.c addentry.c \ request.c os-ip.c url.c sortctrl.c vlvctrl.c \ @@ -20,7 +20,7 @@ OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \ controls.lo messages.lo references.lo extended.lo cyrus.lo \ modify.lo add.lo modrdn.lo delete.lo abandon.lo cache.lo \ - sasl.lo sbind.lo kbind.lo unbind.lo \ + sasl.lo sbind.lo kbind.lo unbind.lo dupent_ctrl.lo \ filter.lo free.lo sort.lo \ getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \ request.lo os-ip.lo url.lo sortctrl.lo vlvctrl.lo \ Index: libraries/libldap/controls.c =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/libraries/libldap/controls.c,v retrieving revision 1.38 diff -u -r1.38 controls.c --- libraries/libldap/controls.c 15 Oct 2002 21:22:21 -0000 1.38 +++ libraries/libldap/controls.c 21 Nov 2002 23:26:10 -0000 @@ -472,3 +472,227 @@ return LDAP_SUCCESS; } + +#if 0 +/* --------------------------------------------------------------------------- + ldap_create_dupent_request + + Create and encode the server-side dupent request control. + + AttributeNameList (IN) Points to a AttributeName structure, containing a + description of each of the attrs to be used. + The description consists of an attribute list and + partialApplicationAllowed boolean flag + + isCritical (IN) 0 - Indicates the control is not critical to the operation. + non-zero - The control is critical to the operation. + + ctrlp (IN/OUT) Returns a pointer to the LDAPControl created. + + Ber encoding + + LDAPDupentReg ::= SEQUENCE { + attributeList AttributeDescriptionList, + partialApplicationAllowed BOOLEAN DEFAULT TRUE } + + ---------------------------------------------------------------------------*/ + +int ldap_create_dupent_request ( + LDAP *ld, + char **attrs, + int partialAppAllowed, + int isCritical, + LDAPControl *ctrlp ) +{ + int i, rc; + BerElement *ber; + struct berval *bvalp = NULL; + ber_tag_t tag; + + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ARGS, + "ldap_create_dupent_request: attrs=%s,\tpartialAppAllowed=%d," + "\tisCritical=%d\n", *attrs,partialAppAllowed,isCritical); +#endif + + if ( (ld == NULL) || (attrs == NULL) || (ctrlp == NULL) ) { + return(LDAP_PARAM_ERROR); + } + + if ((ber = ldap_alloc_ber_with_options ( ld )) == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ( ld->ld_errno ); + } + + + /* + * start DuplicateEntryRequest sequence and + * start AttributeDescriptionList sequence + */ + tag = ber_printf(ber, "{{v}" , attrs); + if (tag == LBER_ERROR) goto exit; + + /* Add PartialApplicationAllowed flag if not allowed */ + if ( ! partialAppAllowed) { + tag = ber_printf(ber, "tb", partialAppAllowed ); + + if( tag == LBER_ERROR ) goto exit; + } + + /* end of DuplicateEntryRequest sequence */ + tag = ber_printf(ber, "N}"); + if( tag == LBER_ERROR ) goto exit; + + if ( ber_flatten ( ber, &bvalp ) == LBER_ERROR ) goto exit; + + ctrlp->ldctl_value = (*bvalp); + + rc = ldap_create_control( LDAP_CONTROL_DUPENT_REQUEST, + ber, isCritical, &ctrlp); + + ber_free(ber, 1); + + return(rc); + +exit: + ber_free(ber, 1); + rc = LDAP_ENCODING_ERROR; + return(rc); +} + + +/* --------------------------------------------------------------------------- + ldap_parse_dupent_response_done + + Decode the server-side dupent control return information. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrls (IN) The address of a NULL-terminated array of LDAPControl + structures, typically obtained by a call to + ldap_parse_result(). + + returnCode (OUT) This result parameter is filled in with the dupent control + result code. This parameter MUST not be NULL. + + errorMessage (OUT) This is an optional human readable LDAPString + + attribute (OUT) If an error occured the server may return a string + indicating the first attribute in the list + that was in error. If a string is returned, the memory + should be freed with ldap_memfree. If this parameter is + NULL, no string is returned. + + + Ber encoding for dupent control + + DuplicateEntryResponseDone ::= SEQUENCE { + resultCode ENUMERATED { + success (0), -- results are dupent + timeLimitExceeded (3), -- time limit reached before + -- processed + sizeLimitExceeded (4), -- size limit reached as a + -- result of this control + adminLimitExceeded (11), -- result set too large + -- for the server to handle + unwillingToPerform (53) -- server cannot process control + }, + errorMessage [0] LDAPString OPTIONAL, + attribute [1] AttributeDescription OPTIONAL } + ---------------------------------------------------------------------------*/ + +int +ldap_parse_dupent_response( + LDAP *ld, + LDAPControl **ctrls, + unsigned long *returnCode, + char **errorMessage, + char **attribute ) +{ + BerElement *ber; + LDAPControl *pControl; + int i; + ber_tag_t tag, berTag; + ber_len_t berLen; + + if (ld == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if (ctrls == NULL) { + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return(ld->ld_errno); + } + + if (errorMessage) { + *errorMessage = NULL; + } + + if (attribute) { + *attribute = NULL; + } + + /* Search the list of control responses for a dupent control. */ + for (i=0; ctrls[i]; i++) { + pControl = ctrls[i]; + if (!strcmp(LDAP_CONTROL_DUPENT_RESPONSE, pControl->ldctl_oid)) + goto foundDupentControl; + } + + /* No dupent control was found. */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return(ld->ld_errno); + +foundDupentControl: + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&pControl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + /* Extract the result code from the control. */ + tag = ber_scanf(ber, "{e" /*}*/, returnCode); + + if( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* If the resultCode in non-success and the errorMessage field is present + extract the errorMessage text. */ + if ( returnCode ) + { + tag = ber_scanf(ber, "ta", &berTag, errorMessage); + + if (tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + } + + /* If the resultCode in non-success and the attribute field is present + extract the attribute name which caused the error. */ + if ( returnCode ) + { + tag = ber_scanf(ber, "ta", &berTag, attribute); + + if (tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + } + + ber_free(ber,1); + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} +#endif Index: libraries/libldap_r/Makefile.in =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/libraries/libldap_r/Makefile.in,v retrieving revision 1.58 diff -u -r1.58 Makefile.in --- libraries/libldap_r/Makefile.in 8 Aug 2002 01:01:18 -0000 1.58 +++ libraries/libldap_r/Makefile.in 21 Nov 2002 23:26:10 -0000 @@ -14,7 +14,7 @@ controls.c messages.c references.c extended.c cyrus.c \ modify.c add.c modrdn.c delete.c abandon.c cache.c \ sasl.c sbind.c kbind.c unbind.c \ - filter.c free.c sort.c \ + dupent_ctrl.c filter.c free.c sort.c \ getdn.c getentry.c getattr.c getvalues.c addentry.c \ request.c os-ip.c url.c sortctrl.c vlvctrl.c \ init.c options.c print.c string.c util-int.c schema.c \ @@ -29,7 +29,7 @@ controls.lo messages.lo references.lo extended.lo cyrus.lo \ modify.lo add.lo modrdn.lo delete.lo abandon.lo cache.lo \ sasl.lo sbind.lo kbind.lo unbind.lo \ - filter.lo free.lo sort.lo \ + dupent_ctrl.lo filter.lo free.lo sort.lo \ getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \ request.lo os-ip.lo url.lo sortctrl.lo vlvctrl.lo \ init.lo options.lo print.lo string.lo util-int.lo schema.lo \ Index: servers/slapd/controls.c =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/controls.c,v retrieving revision 1.45 diff -u -r1.45 controls.c --- servers/slapd/controls.c 21 Nov 2002 21:37:06 -0000 1.45 +++ servers/slapd/controls.c 21 Nov 2002 23:26:10 -0000 @@ -49,6 +49,9 @@ static SLAP_CTRL_PARSE_FN parseNoOp; static SLAP_CTRL_PARSE_FN parsePagedResults; static SLAP_CTRL_PARSE_FN parseValuesReturnFilter; +#ifdef LDAP_CONTROL_DUP_ENT +static SLAP_CTRL_PARSE_FN parseDupEnt; +#endif /* LDAP_CONTROL_DUP_ENT */ #ifdef LDAP_CLIENT_UPDATE static SLAP_CTRL_PARSE_FN parseClientUpdate; @@ -91,6 +94,11 @@ SLAP_CTRL_SEARCH, NULL, parseClientUpdate }, #endif /* LDAP_CLIENT_UPDATE */ +#ifdef LDAP_CONTROL_DUP_ENT + { LDAP_CONTROL_DUPENT, + SLAP_CTRL_SEARCH, NULL, + parseDupEnt }, +#endif /* LDAP_CONTROL_DUP_ENT */ { NULL } }; @@ -733,4 +741,386 @@ return LDAP_SUCCESS; } -#endif /* LDAP_CLIENT_UPDATE */ +#endif + +#ifdef LDAP_CONTROL_DUP_ENT +/*************************************************************************** + * parseDupEnt - Parse the DuplicateEntryRequest from the LDAPControl value + * + * DuplicateEntryRequest ::= SEQUENCE { + * AttributeDescriptionList, -- from [RFC2251] + * PartialApplicationAllowed BOOLEAN DEFAULT TRUE } + * + * AttributeDescriptionListn ::= SEQUENCE OF + * AttributeDescription + * + * AttributeDescription ::= LDAPString + * + * Based on the above, the BER for the request should be as follows: + * + * {{v}bN} + */ +static int parseDupEnt ( + Connection *conn, + Operation *op, + LDAPControl *ctrl, + const char **text ) +{ + BerElement *ber; + AttributeName *an = NULL; + char * attrs = NULL; + ber_len_t off, i; + ber_tag_t tag; + ber_len_t len; + struct berval **vals; + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ENTRY, "\nparseDupEnt:", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "parseDupEnt:\n", 0, 0, 0 ); +#endif + + if ( op->o_dupent != SLAP_NO_CONTROL ) { + *text = "dupent control specified multiple times"; + return LDAP_PROTOCOL_ERROR; + } + + op->o_dupent_ctrl.all_attrs = SLAP_UNDEFINED_MODE; + op->o_dupent_ctrl.control_done = SLAP_UNDEFINED_MODE; + + ber = ber_init( &ctrl->ldctl_value ); + if( ber == NULL ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, ERR, + "parseDupEnt: ber_init failed\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "parseDupEnt: ber_init failed\n", + 0, 0, 0 ); +#endif + *text = "dupent decoding error"; + return LDAP_PROTOCOL_ERROR; + } + + tag = ber_scanf( ber, "{" /*}*/ ); + + if( tag != LBER_ERROR ) { + tag = ber_peek_tag( ber, &len ); + } + + if(ber_scanf(ber,"{V}", &vals) == LBER_ERROR){ +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ERR, + "parseDupEnt: ber_scanf failed\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "parseDupEnt: ber_scanf failed\n", + 0, 0, 0 ); +#endif + *text = "dupent decoding error"; + return LDAP_PROTOCOL_ERROR; + } + + /************************************************************ + * TODO: Check for special case of * in place of attributes. + ************************************************************/ + for ( i=0; vals[i] != NULL; i++ ) { +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, DETAIL1, "parseDupEnt: attr_value = %s\n", + vals[i]->bv_val, 0, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "parseDupEnt: attr_value = %s\n", + vals[i]->bv_val, 0, 0 ); +#endif + } + + op->o_dupent_ctrl.attrList = vals[0]->bv_val; + + /* + * If the client did not provide attr list, or used * in place of + * the attr list, then set the o_dupent_ctrl.all_attrs flag + */ + if (i == 0) + op->o_dupent_ctrl.all_attrs = LDAP_COMPARE_TRUE; + + tag = ber_scanf( ber, "b" ); + if( tag == LBER_BOOLEAN ) { /* client must have provided PartialAppl flag */ + tag = ber_get_boolean ( ber, &op->o_dupent_ctrl.partialAppAllowed ); + } + + op->o_dupent = ctrl->ldctl_iscritical + ? SLAP_CRITICAL_CONTROL + : SLAP_NONCRITICAL_CONTROL; + +#ifdef NEW_LOGGING + LDAP_LOG ( OPERATION, DETAIL1, "dupent ctrl: %s - partialAppAllowed = %x\n", + ctrl->ldctl_iscritical ? "critical" : "non critical", + op->o_dupent_ctrl.partialAppAllowed, 0 ); +#else + Debug( LDAP_DEBUG_TRACE, "parseDupEnt: partialAppAllowed = %d\n", + op->o_dupent_ctrl.partialAppAllowed, 0, 0 ); +#endif + + return LDAP_SUCCESS; +} + +/* + * process_dupent_ctrl + * + * The following example illustrates what this function is trying to + * accomplish. If the search request contains the attribute telephoneNumber + * and the search returns the following entry with duplicate telephoneNumber + * attribute values: + * + * dn: cn=User1, dc=example, dc=net + * telephoneNumber: 555-0123 + * telephoneNumber: 555-4567 + * + * it will be transformed into: + * + * dn: cn=user1, dc=example, dc=net + * telephoneNumber: 555-0123 + * control: 2.16.840.1.113719.1.27.101.2 + * + * dn: cn=user1, dc=example, dc=net + * telephoneNumber: 555-4567 + * control: 2.16.840.1.113719.1.27.101.2 + * + * In order to accomplish the above, we need to walk thru list of + * attributes in the entry. If an attribute matches any in the control list, + * the count the number of values in that attribute. If the number is + * greater than 1, then do the following for each attribute value: + * a. create a new entry + * b. populate new entry with the same DN and one of the attribute values + * c. add DuplicateEntrySearchResult control to the entry + * d. send out the new entry + * f. remove this attribute value from the old entry + * + * Special Case: If the o_dupent flag is set and the control attribute list + * is empty or contains a "*", the control will be applied to all attrs in + * in the entry. See section 5.1.1, last paragraph. + * + * TODO: Section 5.1.2 PartialApplicationAllowed semantics + */ + +int +process_dupent_ctrl( + Backend *be, + Connection *conn, + Operation *op, + Entry *e, + AttributeName *attrs, + ber_int_t attrsonly, + LDAPControl **ctrlp +) +{ + Entry *dupent_e; + LDAPControl ctrl; + Attribute *t, *t2, *t3; + Attribute *tmp_attr = NULL; + struct berval **bvals; + AttributeName *ans = NULL; + AttributeName *an = NULL; + long int i, j, j2, j3, k, rc = LDAP_SUCCESS; + +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ENTRY, "process_dupent_ctrl: entry\n", 0, 0, 0 ); +#else + Debug( LDAP_DEBUG_ANY, "=> process_dupent_ctrl\n", 0, 0, 0 ); +#endif + + ans = str2anlist( ans, op->o_dupent_ctrl.attrList, "," ); + + dupent_e = ch_malloc( sizeof(Entry) ); + + /* + * Count number of controls allready filed in. + */ + for ( i=0; ctrlp[i] != NULL; i++); + + ctrl.ldctl_oid = NULL; + ctrl.ldctl_iscritical = 0; + ctrl.ldctl_value.bv_val = NULL; + ctrl.ldctl_value.bv_len = 0; + + ctrlp[i] = &ctrl; + ctrlp[++i] = NULL; + + + /* walk thru all attrs in the entry */ + for ( t = e->e_attrs, i=0; t != NULL; t = t->a_next, i++ ) { + /* walk thru all attrs in the dupent attr list */ + for (j = 0, an = ans; an->an_name.bv_val != NULL; j++, an++) { + /* + * Check for matching attribute or attribute subtype names. + * TODO: + * Section 5.1.1 says: There is a special case where either no + * attributes are specified or an attribute description value + * of "*" is specified. In this case, all attributes are used. + * Needs to provide support for this special case + */ + if( strcmp( t->a_desc->ad_cname.bv_val, an->an_name.bv_val ) == 0 || + is_ad_subtype ( t->a_desc, an->an_desc ) ) { + + /* count number of values for this attribute */ + for ( j2 = 0; t->a_vals[j2].bv_val != NULL; j2++ ) ; + + if ( j2 > 1 ) { + /* We have at least one duplicate entry, set the + * control_done flag so we could send the + * response_done control with the final result. + */ + if( op->o_dupent_ctrl.control_done == SLAP_UNDEFINED_MODE) + op->o_dupent_ctrl.control_done = LDAP_COMPARE_TRUE; + + /* + * construct a separate Entry for each attribute value, + * attach a LDAP_CONTROL_DUPENT_ENTRY control to it and + * send it out, except for the last attribute. + */ + ctrl.ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY; + + rc = create_dupent_response ( &ctrl, + LDAP_CONTROL_DUPENT_ENTRY, + LDAP_SUCCESS, "", t->a_desc->ad_cname.bv_val ); + + for(j3 = 0; j3a_vals[j3].bv_val != NULL; j3++) { +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, DETAIL1, + "dupent_ctrl: matched attr <%s> value <%s>\n", + t->a_desc->ad_cname.bv_val, + t->a_vals[j3].bv_val, 0 ); +#else + Debug( LDAP_DEBUG_ANY, + "dupent_ctrl: matched attr <%s> value <%s>\n", + t->a_desc->ad_cname.bv_val, + t->a_vals[j3].bv_val, 0 ); +#endif + tmp_attr = ch_malloc( sizeof(Attribute) ); + bvals = ch_calloc( 2, sizeof(struct berval)); + bvals[1] = NULL; + + tmp_attr->a_vals = bvals; + ber_dupbv( tmp_attr->a_vals, &t->a_vals[j3] ); + tmp_attr->a_desc = t->a_desc; + tmp_attr->a_next = NULL; + tmp_attr->a_flags = 0; + + ber_dupbv( &dupent_e->e_name, &e->e_name ); + ber_dupbv( &dupent_e->e_nname, &e->e_nname ); + ber_dupbv( &dupent_e->e_bv, &e->e_bv ); + dupent_e->e_attrs = tmp_attr; + + send_search_entry( be, conn, op, dupent_e, + attrs, attrsonly, ctrlp ); + } + } + break; + } + } + } + /* walk thru all remaining attrs in the entry */ + ber_dupbv( &dupent_e->e_name, &e->e_name ); + ber_dupbv( &dupent_e->e_nname, &e->e_nname ); + ber_dupbv( &dupent_e->e_bv, &e->e_bv ); + dupent_e->e_attrs = NULL; + k = 0; + for ( t = e->e_attrs, i=0; t != NULL; t = t->a_next, i++ ) { + if( t->a_flags == 0) + attr_merge_one (dupent_e, t->a_desc, t->a_vals ); + } + /* + * If there are any attributes left in the original entry, then send + * it out without any controls. + */ + + if( ctrl.ldctl_oid != NULL ) + send_search_entry( be, conn, op, dupent_e, attrs, attrsonly, ctrlp ); + else + send_search_entry( be, conn, op, dupent_e, attrs, attrsonly, NULL ); + +done: + if( tmp_attr ) + ch_free( tmp_attr ); + + if( dupent_e ) + ch_free( dupent_e ); + + if( ans ) + ch_free( ans ); + + return rc; +} + +/* + * DuplicateEntryResponseDone ::= SEQUENCE { + * resultCode, -- From [RFC2251] + * errorMessage [0] LDAPString OPTIONAL, + * attribute [1] AttributeDescription OPTIONAL } + * + */ +int +create_dupent_response ( + LDAPControl *ctrl, + char *controlType, + int resultCode, + char *errorMessage, + char *attribute ) +{ + char berbuf[256]; + BerElement *ber = (BerElement *)berbuf; + struct berval *bvalp = NULL; + int rc = LDAP_SUCCESS; + +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, ARGS, + "create_dupent_response: controlType=%s,resultCode=%d,attribute=%s\n", + controlType, resultCode, attribute ); +#else + Debug( LDAP_DEBUG_ANY, + "create_dupent_response: controlType=%s,resultCode=%d,attribute=%s\n", + controlType, resultCode, attribute ); +#endif + + ber_init_w_nullc( ber, LBER_USE_DER ); + + if( strcmp( controlType, LDAP_CONTROL_DUPENT_ENTRY ) == 0 ) { + /* + * Control Value not required. Just construct empty control + */ + goto finish; + } + + /* encode starting sequence of sequence */ + if( ber_printf( ber, "t{", LDAP_TAG_CONTROLS ) == -1) { + rc = LDAP_ENCODING_ERROR; + return rc; + } + + if ( ber_printf( ber, "i" , resultCode ) == -1) { + rc = LDAP_ENCODING_ERROR; + return rc; + } + + if ( ber_printf( ber, "s" , errorMessage ) == -1) { + rc = LDAP_ENCODING_ERROR; + return rc; + } + + if ( ber_printf( ber, "sN}" , attribute ) == -1) { + rc = LDAP_ENCODING_ERROR; + return rc; + } + +finish: + if ( ber_flatten ( ber, &bvalp ) == LBER_ERROR ) { + rc = LBER_ERROR; + return rc; + } + + ctrl->ldctl_value = (*bvalp); + + rc = ldap_create_control ( controlType, ber, 1, &ctrl ); + + return rc; +} +#endif /* LDAP_CONTROL_DUP_ENT */ + Index: servers/slapd/proto-slap.h =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/proto-slap.h,v retrieving revision 1.378 diff -u -r1.378 proto-slap.h --- servers/slapd/proto-slap.h 10 Nov 2002 18:48:36 -0000 1.378 +++ servers/slapd/proto-slap.h 21 Nov 2002 23:26:11 -0000 @@ -1142,5 +1142,24 @@ LDAP_END_DECL +/* + * dupentCtrl + */ +LDAP_SLAPD_F (int) process_dupent_ctrl( + Backend *be, + Connection *conn, + Operation *op, + Entry *entry, + AttributeName *a, + ber_int_t attrsonly, + LDAPControl **ctrl ); + +LDAP_SLAPD_F (int) create_dupent_response( + LDAPControl *ctrl, + char *controlType, + int resultCode, + char *errorMessage, + char *attribute ); + #endif /* PROTO_SLAP_H */ Index: servers/slapd/search.c =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/search.c,v retrieving revision 1.93 diff -u -r1.93 search.c --- servers/slapd/search.c 25 Oct 2002 15:51:31 -0000 1.93 +++ servers/slapd/search.c 21 Nov 2002 23:26:11 -0000 @@ -256,9 +256,32 @@ rc = test_filter( NULL, conn, op, entry, filter ); - if( rc == LDAP_COMPARE_TRUE ) { + if ( rc == LDAP_COMPARE_TRUE ) { +#ifdef LDAP_CONTROL_DUP_ENT +#ifdef NEW_LOGGING + LDAP_LOG( OPERATION, DETAIL1, + "do_search: conn %ul op->o_dupent = %d\n", + conn->c_connid, op->o_dupent, 0 ); +#else + Debug( LDAP_DEBUG_ANY, "do_search: conn: %ul op->o_dupent = %d\n", + conn->c_connid, op->o_dupent, 0 ); +#endif + if ( op->o_dupent != SLAP_NO_CONTROL ) { + LDAPControl *ctrls[2]; + ctrls[0] = NULL; + ctrls[1] = NULL; + + rc = process_dupent_ctrl( be, conn, + op, entry, + an, attrsonly, ctrls ); + } else { + send_search_entry( NULL, conn, op, + entry, an, attrsonly, NULL ); + } +#else /* !LDAP_CONTROL_DUP_ENT */ send_search_entry( NULL, conn, op, entry, an, attrsonly, NULL ); +#endif /* !LDAP_CONTROL_DUP_ENT */ } entry_free( entry ); Index: servers/slapd/slap.h =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/slap.h,v retrieving revision 1.375 diff -u -r1.375 slap.h --- servers/slapd/slap.h 21 Nov 2002 19:49:02 -0000 1.375 +++ servers/slapd/slap.h 21 Nov 2002 23:26:11 -0000 @@ -1619,6 +1619,17 @@ }; #endif /* LDAP_CLIENT_UPDATE */ +#ifdef LDAP_CONTROL_DUP_ENT +/* + * Duplicate Entry Control + */ +typedef struct slap_dup_ent_ctrl { + char *attrList; + int partialAppAllowed; + int all_attrs; + int control_done; +} DuplicateEntryRequest; +#endif /* LDAP_CONTROL_DUP_ENT */ /* * represents an operation pending from an ldap client @@ -1651,6 +1662,10 @@ char o_pagedresults; ber_int_t o_pagedresults_size; PagedResultsState o_pagedresults_state; +#ifdef LDAP_CONTROL_DUP_ENT + char o_dupent; + DuplicateEntryRequest o_dupent_ctrl; +#endif /* LDAP_CONTROL_DUP_ENT */ #ifdef LDAP_CLIENT_UPDATE char o_clientupdate; Index: servers/slapd/back-bdb/init.c =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/back-bdb/init.c,v retrieving revision 1.102 diff -u -r1.102 init.c --- servers/slapd/back-bdb/init.c 21 Nov 2002 01:16:54 -0000 1.102 +++ servers/slapd/back-bdb/init.c 21 Nov 2002 23:26:12 -0000 @@ -510,6 +510,9 @@ #ifdef LDAP_CONTROL_PAGEDRESULTS LDAP_CONTROL_PAGEDRESULTS, #endif /* LDAP_CONTROL_PAGEDRESULTS */ +#ifdef LDAP_CONTROL_DUP_ENT + LDAP_CONTROL_DUPENT, +#endif /* LDAP_CONTROL_DUP_ENT */ NULL }; Index: servers/slapd/back-bdb/search.c =================================================================== RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/back-bdb/search.c,v retrieving revision 1.89 diff -u -r1.89 search.c --- servers/slapd/back-bdb/search.c 21 Nov 2002 19:49:03 -0000 1.89 +++ servers/slapd/back-bdb/search.c 21 Nov 2002 23:26:12 -0000 @@ -666,13 +666,14 @@ } else #endif { + LDAPControl *ctrls[3]; + ctrls[0] = NULL; #ifdef LDAP_CLIENT_UPDATE if ( op->o_clientupdate_type & SLAP_LCUP_SYNC ) { Attribute* a; int ret; int res; const char *text = NULL; - LDAPControl *ctrls[2]; struct berval *bv; BerElement *ber = ber_alloc_t( LBER_USE_DER ); @@ -771,8 +772,17 @@ ber_dupbv( &ctrls[0]->ldctl_value, bv ); +#ifdef LDAP_CONTROL_DUP_ENT + if( op->o_dupent != SLAP_NO_CONTROL ) + result = process_dupent_ctrl ( be, conn, op, e, + attrs, attrsonly, ctrls ); + else + result = send_search_entry( be, conn, op, + e, attrs, attrsonly, ctrls); +#else /* !LDAP_CONTROL_DUP_ENT */ result = send_search_entry( be, conn, op, e, attrs, attrsonly, ctrls); +#endif /* !LDAP_CONTROL_DUP_ENT */ ch_free( ctrls[0]->ldctl_value.bv_val ); ch_free( ctrls[0] ); @@ -781,8 +791,17 @@ } else #endif /* LDAP_CLIENT_UPDATE */ { +#ifdef LDAP_CONTROL_DUP_ENT + if( op->o_dupent != SLAP_NO_CONTROL ) + result = process_dupent_ctrl ( be, conn, op, e, + attrs, attrsonly, ctrls ); + else + result = send_search_entry( be, conn, op, + e, attrs, attrsonly, NULL); +#else /* !LDAP_CONTROL_DUP_ENT */ result = send_search_entry( be, conn, op, e, attrs, attrsonly, NULL); +#endif /* !LDAP_CONTROL_DUP_ENT */ } } @@ -887,13 +906,52 @@ ch_free( ctrls[0] ); ber_free( ber, 1 ); ber_bvfree( bv ); - } else + } else { #endif /* LDAP_CLIENT_UPDATE */ - { + /* + * FIXME: this conditional compilation + * is getting too nested; before this + * code is released we should remove + * conditional complation where things + * look reliable + */ +#ifdef LDAP_CONTROL_DUP_ENT + /* + * If dupent_ctrl was specified and duplicates were found, then + * construct and send a dupent_response control with the search result. + */ + if( op->o_dupent != SLAP_NO_CONTROL && + op->o_dupent_ctrl.control_done == LDAP_COMPARE_TRUE ) { + LDAPControl *ctrls[2]; + LDAPControl ctrl; + + ctrls[0] = &ctrl; + + ctrl.ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE; + ctrl.ldctl_iscritical = 0; + ctrl.ldctl_value.bv_val = NULL; + ctrl.ldctl_value.bv_len = 0; + rc = create_dupent_response ( &ctrl, LDAP_CONTROL_DUPENT_RESPONSE, + LDAP_SUCCESS, "", op->o_dupent_ctrl.attrList ); + + if ( !rc ) { + send_search_result( conn, op, + v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL, + NULL, NULL, v2refs, ctrls, nentries ); + } + } else { +#endif /* LDAP_CONTROL_DUP_ENT */ send_search_result( conn, op, v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL, NULL, NULL, v2refs, NULL, nentries ); +#ifdef LDAP_CONTROL_DUP_ENT + } +#endif /* LDAP_CONTROL_DUP_ENT */ + +#ifdef LDAP_CLIENT_UPDATE } +#endif /* LDAP_CLIENT_UPDATE */ + rc = 0;