summaryrefslogtreecommitdiffstats
path: root/ldap/admin/lib/dsalib_dn.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/admin/lib/dsalib_dn.c
downloadds-ldapserver7x.tar.gz
ds-ldapserver7x.tar.xz
ds-ldapserver7x.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/admin/lib/dsalib_dn.c')
-rw-r--r--ldap/admin/lib/dsalib_dn.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/ldap/admin/lib/dsalib_dn.c b/ldap/admin/lib/dsalib_dn.c
new file mode 100644
index 00000000..d7de36d9
--- /dev/null
+++ b/ldap/admin/lib/dsalib_dn.c
@@ -0,0 +1,465 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#if defined( XP_WIN32 )
+#include <windows.h>
+#else
+#include <sys/types.h>
+#endif
+#include <string.h>
+#include "dsalib.h"
+#include "portable.h"
+#include <stdlib.h>
+
+#define DNSEPARATOR(c) (c == ',' || c == ';')
+#define SEPARATOR(c) (c == ',' || c == ';' || c == '+')
+#define SPACE(c) (c == ' ' || c == '\n')
+#define NEEDSESCAPE(c) (c == '\\' || c == '"')
+#define B4TYPE 0
+#define INTYPE 1
+#define B4EQUAL 2
+#define B4VALUE 3
+#define INVALUE 4
+#define INQUOTEDVALUE 5
+#define B4SEPARATOR 6
+
+DS_EXPORT_SYMBOL char*
+dn_normalize( char *dn )
+{
+ char *d, *s;
+ int state, gotesc;
+
+ /* Debug( LDAP_DEBUG_TRACE, "=> dn_normalize \"%s\"\n", dn, 0, 0 ); */
+
+ gotesc = 0;
+ state = B4TYPE;
+ for ( d = s = dn; *s; s++ ) {
+ switch ( state ) {
+ case B4TYPE:
+ if ( ! SPACE( *s ) ) {
+ state = INTYPE;
+ *d++ = *s;
+ }
+ break;
+ case INTYPE:
+ if ( *s == '=' ) {
+ state = B4VALUE;
+ *d++ = *s;
+ } else if ( SPACE( *s ) ) {
+ state = B4EQUAL;
+ } else {
+ *d++ = *s;
+ }
+ break;
+ case B4EQUAL:
+ if ( *s == '=' ) {
+ state = B4VALUE;
+ *d++ = *s;
+ } else if ( ! SPACE( *s ) ) {
+ /* not a valid dn - but what can we do here? */
+ *d++ = *s;
+ }
+ break;
+ case B4VALUE:
+ if ( *s == '"' ) {
+ state = INQUOTEDVALUE;
+ *d++ = *s;
+ } else if ( ! SPACE( *s ) ) {
+ state = INVALUE;
+ *d++ = *s;
+ }
+ break;
+ case INVALUE:
+ if ( !gotesc && SEPARATOR( *s ) ) {
+ while ( SPACE( *(d - 1) ) )
+ d--;
+ state = B4TYPE;
+ if ( *s == '+' ) {
+ *d++ = *s;
+ } else {
+ *d++ = ',';
+ }
+ } else if ( gotesc && !NEEDSESCAPE( *s ) &&
+ !SEPARATOR( *s ) ) {
+ *--d = *s;
+ d++;
+ } else {
+ *d++ = *s;
+ }
+ break;
+ case INQUOTEDVALUE:
+ if ( !gotesc && *s == '"' ) {
+ state = B4SEPARATOR;
+ *d++ = *s;
+ } else if ( gotesc && !NEEDSESCAPE( *s ) ) {
+ *--d = *s;
+ d++;
+ } else {
+ *d++ = *s;
+ }
+ break;
+ case B4SEPARATOR:
+ if ( SEPARATOR( *s ) ) {
+ state = B4TYPE;
+ if ( *s == '+' ) {
+ *d++ = *s;
+ } else {
+ *d++ = ',';
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if ( *s == '\\' ) {
+ gotesc = 1;
+ } else {
+ gotesc = 0;
+ }
+ }
+ *d = '\0';
+
+ /* Debug( LDAP_DEBUG_TRACE, "<= dn_normalize \"%s\"\n", dn, 0, 0 ); */
+ return( dn );
+}
+
+DS_EXPORT_SYMBOL int
+dn_issuffix(
+ char *dn,
+ char *suffix
+)
+{
+ int dnlen, suffixlen;
+
+ if ( dn == NULL ) {
+ return( 0 );
+ }
+
+ suffixlen = strlen( suffix );
+ dnlen = strlen( dn );
+
+ if ( suffixlen > dnlen ) {
+ return( 0 );
+ }
+
+ return( strcasecmp( dn + dnlen - suffixlen, suffix ) == 0 );
+}
+
+DS_EXPORT_SYMBOL char*
+ds_dn_expand (char* dn)
+{
+ char* edn;
+ size_t i = 0;
+ char* s;
+ int state = B4TYPE;
+ int gotesc = 0;
+
+ if (dn == NULL) return NULL;
+ edn = strdup (dn);
+ for (s = dn; *s != '\0'; ++s, ++i) {
+ switch (state) {
+ case B4TYPE:
+ if ( ! SPACE (*s)) {
+ state = INTYPE;
+ }
+ break;
+ case INTYPE:
+ if (*s == '=') {
+ state = B4VALUE;
+ } else if (SPACE (*s)) {
+ state = B4EQUAL;
+ }
+ break;
+ case B4EQUAL:
+ if (*s == '=') {
+ state = B4VALUE;
+ }
+ break;
+ case B4VALUE:
+ if (*s == '"') {
+ state = INQUOTEDVALUE;
+ } else if ( ! SPACE (*s)) {
+ state = INVALUE;
+ }
+ break;
+ case INQUOTEDVALUE:
+ if (gotesc) {
+ if ( ! NEEDSESCAPE (*s)) {
+ --i;
+ memmove (edn+i, edn+i+1, strlen (edn+i));
+ }
+ } else {
+ if (*s == '"') {
+ state = B4SEPARATOR;
+ }
+ }
+ break;
+ case INVALUE:
+ if (gotesc) {
+ if ( ! NEEDSESCAPE (*s) && ! SEPARATOR (*s)) {
+ --i;
+ memmove (edn+i, edn+i+1, strlen (edn+i));
+ }
+ break;
+ }
+ case B4SEPARATOR:
+ if (SEPARATOR (*s)) {
+ state = B4TYPE;
+ if ( ! SPACE (s[1])) {
+ /* insert a space following edn[i] */
+ edn = (char*) realloc (edn, strlen (edn)+2);
+ ++i;
+ memmove (edn+i+1, edn+i, strlen (edn+i)+1);
+ edn[i] = ' ';
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ gotesc = (*s == '\\');
+ }
+ return edn;
+}
+
+int
+hexchar2int( char c )
+{
+ if ( '0' <= c && c <= '9' ) {
+ return( c - '0' );
+ }
+ if ( 'a' <= c && c <= 'f' ) {
+ return( c - 'a' + 10 );
+ }
+ if ( 'A' <= c && c <= 'F' ) {
+ return( c - 'A' + 10 );
+ }
+ return( -1 );
+}
+
+/*
+ * substr_dn_normalize - map a DN to a canonical form.
+ * The DN is read from *dn through *(end-1) and normalized in place.
+ * The new end is returned; that is, the canonical form is in
+ * *dn through *(the_return_value-1).
+ */
+
+/* The goals of this function are:
+ * 1. be compatible with previous implementations. Especially, enable
+ * a server running this code to find database index keys that were
+ * computed by Directory Server 3.0 with a prior version of this code.
+ * 2. Normalize in place; that is, avoid allocating memory to contain
+ * the canonical form.
+ * 3. eliminate insignificant differences; that is, any two DNs are
+ * not significantly different if and only if their canonical forms
+ * are identical (ignoring upper/lower case).
+ * 4. handle a DN in the syntax defined by RFC 2253.
+ * 5. handle a DN in the syntax defined by RFC 1779.
+ *
+ * Goals 3 through 5 are not entirely achieved by this implementation,
+ * because it can't be done without violating goal 1. Specifically,
+ * DNs like cn="a,b" and cn=a\,b are not mapped to the same canonical form,
+ * although they're not significantly different. Likewise for any pair
+ * of DNs that differ only in their choice of quoting convention.
+ * A previous version of this code changed all DNs to the most compact
+ * quoting convention, but that violated goal 1, since Directory Server
+ * 3.0 did not.
+ *
+ * Also, this implementation handles the \xx convention of RFC 2253 and
+ * consequently violates RFC 1779, according to which this type of quoting
+ * would be interpreted as a sequence of 2 numerals (not a single byte).
+ */
+
+DS_EXPORT_SYMBOL char *
+dn_normalize_convert( char *dn )
+{
+ /* \xx is changed to \c.
+ \c is changed to c, unless this would change its meaning.
+ All values that contain 2 or more separators are "enquoted";
+ all other values are not enquoted.
+ */
+ char *value, *value_separator;
+ char *d, *s;
+ char *end;
+
+ int gotesc = 0;
+ int state = B4TYPE;
+ if (NULL == dn)
+ return dn;
+
+ end = dn + strlen(dn);
+ for ( d = s = dn; s != end; s++ ) {
+ switch ( state ) {
+ case B4TYPE:
+ if ( ! SPACE( *s ) ) {
+ state = INTYPE;
+ *d++ = *s;
+ }
+ break;
+ case INTYPE:
+ if ( *s == '=' ) {
+ state = B4VALUE;
+ *d++ = *s;
+ } else if ( SPACE( *s ) ) {
+ state = B4EQUAL;
+ } else {
+ *d++ = *s;
+ }
+ break;
+ case B4EQUAL:
+ if ( *s == '=' ) {
+ state = B4VALUE;
+ *d++ = *s;
+ } else if ( ! SPACE( *s ) ) {
+ /* not a valid dn - but what can we do here? */
+ *d++ = *s;
+ }
+ break;
+ case B4VALUE:
+ if ( *s == '"' || ! SPACE( *s ) ) {
+ value_separator = NULL;
+ value = d;
+ state = ( *s == '"' ) ? INQUOTEDVALUE : INVALUE;
+ *d++ = *s;
+ }
+ break;
+ case INVALUE:
+ if ( gotesc ) {
+ if ( SEPARATOR( *s ) ) {
+ if ( value_separator ) value_separator = dn;
+ else value_separator = d;
+ } else if ( ! NEEDSESCAPE( *s ) ) {
+ --d; /* eliminate the \ */
+ }
+ } else if ( SEPARATOR( *s ) ) {
+ while ( SPACE( *(d - 1) ) )
+ d--;
+ if ( value_separator == dn ) { /* 2 or more separators */
+ /* convert to quoted value: */
+ auto char *L = NULL;
+ auto char *R;
+ for ( R = value; (R = strchr( R, '\\' )) && (R < d); L = ++R ) {
+ if ( SEPARATOR( R[1] )) {
+ if ( L == NULL ) {
+ auto const size_t len = R - value;
+ if ( len > 0 ) memmove( value+1, value, len );
+ *value = '"'; /* opening quote */
+ value = R + 1;
+ } else {
+ auto const size_t len = R - L;
+ if ( len > 0 ) {
+ memmove( value, L, len );
+ value += len;
+ }
+ --d;
+ }
+ }
+ }
+ memmove( value, L, d - L + 1 );
+ *d++ = '"'; /* closing quote */
+ }
+ state = B4TYPE;
+ *d++ = (*s == '+') ? '+' : ',';
+ break;
+ }
+ *d++ = *s;
+ break;
+ case INQUOTEDVALUE:
+ if ( gotesc ) {
+ if ( ! NEEDSESCAPE( *s ) ) {
+ --d; /* eliminate the \ */
+ }
+ } else if ( *s == '"' ) {
+ state = B4SEPARATOR;
+ if ( value_separator == dn /* 2 or more separators */
+ || SPACE( value[1] ) || SPACE( d[-1] ) ) {
+ *d++ = *s;
+ } else {
+ /* convert to non-quoted value: */
+ if ( value_separator == NULL ) { /* no separators */
+ memmove ( value, value+1, (d-value)-1 );
+ --d;
+ } else { /* 1 separator */
+ memmove ( value, value+1, (value_separator-value)-1 );
+ *(value_separator - 1) = '\\';
+ }
+ }
+ break;
+ }
+ if ( SEPARATOR( *s )) {
+ if ( value_separator ) value_separator = dn;
+ else value_separator = d;
+ }
+ *d++ = *s;
+ break;
+ case B4SEPARATOR:
+ if ( SEPARATOR( *s ) ) {
+ state = B4TYPE;
+ *d++ = (*s == '+') ? '+' : ',';
+ }
+ break;
+ default:
+/* LDAPDebug( LDAP_DEBUG_ANY,
+ "slapi_dn_normalize - unknown state %d\n", state, 0, 0 );*/
+ break;
+ }
+ if ( *s != '\\' ) {
+ gotesc = 0;
+ } else {
+ gotesc = 1;
+ if ( s+2 < end ) {
+ auto int n = hexchar2int( s[1] );
+ if ( n >= 0 ) {
+ auto int n2 = hexchar2int( s[2] );
+ if ( n2 >= 0 ) {
+ n = (n << 4) + n2;
+ if (n == 0) { /* don't change \00 */
+ *d++ = *++s;
+ *d++ = *++s;
+ gotesc = 0;
+ } else { /* change \xx to a single char */
+ ++s;
+ *(unsigned char*)(s+1) = n;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Trim trailing spaces */
+ while ( d != dn && *(d - 1) == ' ' ) d--;
+
+ *d = 0;
+
+ return( dn );
+}
+
+/* if dn contains an unescaped quote return true */
+DS_EXPORT_SYMBOL int
+ds_dn_uses_LDAPv2_quoting(const char *dn)
+{
+ const char ESC = '\\';
+ const char Q = '"';
+ int ret = 0;
+ const char *p = 0;
+
+ /* check dn for a even number (incl. 0) of ESC followed by Q */
+ if (!dn)
+ return ret;
+
+ p = strchr(dn, Q);
+ if (p)
+ {
+ int nESC = 0;
+ for (--p; (p >= dn) && (*p == ESC); --p)
+ ++nESC;
+ if (!(nESC % 2))
+ ret = 1;
+ }
+
+ return ret;
+}