Search in sources :

Example 1 with ModificationType

use of com.unboundid.ldap.sdk.ModificationType in project ldapsdk by pingidentity.

the class LDIFReader method parseModifications.

/**
 * Parses the data available through the provided iterator into an array of
 * modifications suitable for use in a modify change record.
 *
 * @param  dn                     The DN of the entry being parsed.
 * @param  trailingSpaceBehavior  The behavior that should be exhibited when
 *                                encountering attribute values which are not
 *                                base64-encoded but contain trailing spaces.
 * @param  ldifLines              The lines that comprise the LDIF
 *                                representation of the full record being
 *                                parsed.
 * @param  iterator               The iterator to use to access the
 *                                modification data.
 * @param  relativeBasePath       The base path that will be prepended to
 *                                relative paths in order to obtain an
 *                                absolute path.
 * @param  firstLineNumber        The line number for the start of the record.
 * @param  schema                 The schema to use in processing.
 *
 * @return  An array containing the modifications that were read.
 *
 * @throws  LDIFException  If the provided LDIF data cannot be decoded as a
 *                         set of modifications.
 */
@NotNull()
private static Modification[] parseModifications(@NotNull final String dn, @NotNull final TrailingSpaceBehavior trailingSpaceBehavior, @NotNull final ArrayList<StringBuilder> ldifLines, @NotNull final Iterator<StringBuilder> iterator, @NotNull final String relativeBasePath, final long firstLineNumber, @Nullable final Schema schema) throws LDIFException {
    final ArrayList<Modification> modList = new ArrayList<>(ldifLines.size());
    while (iterator.hasNext()) {
        // The first line must start with "add:", "delete:", "replace:", or
        // "increment:" followed by an attribute name.
        StringBuilder line = iterator.next();
        handleTrailingSpaces(line, dn, firstLineNumber, trailingSpaceBehavior);
        int colonPos = line.indexOf(":");
        if (colonPos < 0) {
            throw new LDIFException(ERR_READ_MOD_CR_NO_MODTYPE.get(firstLineNumber), firstLineNumber, true, ldifLines, null);
        }
        final ModificationType modType;
        final String modTypeStr = StaticUtils.toLowerCase(line.substring(0, colonPos));
        if (modTypeStr.equals("add")) {
            modType = ModificationType.ADD;
        } else if (modTypeStr.equals("delete")) {
            modType = ModificationType.DELETE;
        } else if (modTypeStr.equals("replace")) {
            modType = ModificationType.REPLACE;
        } else if (modTypeStr.equals("increment")) {
            modType = ModificationType.INCREMENT;
        } else {
            throw new LDIFException(ERR_READ_MOD_CR_INVALID_MODTYPE.get(modTypeStr, firstLineNumber), firstLineNumber, true, ldifLines, null);
        }
        String attributeName;
        int length = line.length();
        if (length == (colonPos + 1)) {
            // acceptable.
            throw new LDIFException(ERR_READ_MOD_CR_MODTYPE_NO_ATTR.get(firstLineNumber), firstLineNumber, true, ldifLines, null);
        } else if (line.charAt(colonPos + 1) == ':') {
            // Skip over any spaces leading up to the value, and then the rest of
            // the string is the base64-encoded attribute name.
            int pos = colonPos + 2;
            while ((pos < length) && (line.charAt(pos) == ' ')) {
                pos++;
            }
            try {
                final byte[] dnBytes = Base64.decode(line.substring(pos));
                attributeName = StaticUtils.toUTF8String(dnBytes);
            } catch (final ParseException pe) {
                Debug.debugException(pe);
                throw new LDIFException(ERR_READ_MOD_CR_MODTYPE_CANNOT_BASE64_DECODE_ATTR.get(firstLineNumber, pe.getMessage()), firstLineNumber, true, ldifLines, pe);
            } catch (final Exception e) {
                Debug.debugException(e);
                throw new LDIFException(ERR_READ_MOD_CR_MODTYPE_CANNOT_BASE64_DECODE_ATTR.get(firstLineNumber, e), firstLineNumber, true, ldifLines, e);
            }
        } else {
            // Skip over any spaces leading up to the value, and then the rest of
            // the string is the attribute name.
            int pos = colonPos + 1;
            while ((pos < length) && (line.charAt(pos) == ' ')) {
                pos++;
            }
            attributeName = line.substring(pos);
        }
        if (attributeName.isEmpty()) {
            throw new LDIFException(ERR_READ_MOD_CR_MODTYPE_NO_ATTR.get(firstLineNumber), firstLineNumber, true, ldifLines, null);
        }
        // The next zero or more lines may be the set of attribute values.  Keep
        // reading until we reach the end of the iterator or until we find a line
        // with just a "-".
        final ArrayList<ASN1OctetString> valueList = new ArrayList<>(ldifLines.size());
        while (iterator.hasNext()) {
            line = iterator.next();
            handleTrailingSpaces(line, dn, firstLineNumber, trailingSpaceBehavior);
            if (line.toString().equals("-")) {
                break;
            }
            colonPos = line.indexOf(":");
            if (colonPos < 0) {
                throw new LDIFException(ERR_READ_NO_ATTR_COLON.get(firstLineNumber), firstLineNumber, true, ldifLines, null);
            } else if (!line.substring(0, colonPos).equalsIgnoreCase(attributeName)) {
                // There are a couple of cases in which this might be acceptable:
                // - If the two names are logically equivalent, but have an alternate
                // name (or OID) for the target attribute type, or if there are
                // attribute options and the options are just in a different order.
                // - If this is the first value for the target attribute and the
                // alternate name includes a "binary" option that the original
                // attribute name did not have.  In this case, all subsequent values
                // will also be required to have the binary option.
                final String alternateName = line.substring(0, colonPos);
                // Check to see if the base names are equivalent.
                boolean baseNameEquivalent = false;
                final String expectedBaseName = Attribute.getBaseName(attributeName);
                final String alternateBaseName = Attribute.getBaseName(alternateName);
                if (alternateBaseName.equalsIgnoreCase(expectedBaseName)) {
                    baseNameEquivalent = true;
                } else {
                    if (schema != null) {
                        final AttributeTypeDefinition expectedAT = schema.getAttributeType(expectedBaseName);
                        final AttributeTypeDefinition alternateAT = schema.getAttributeType(alternateBaseName);
                        if ((expectedAT != null) && (alternateAT != null) && expectedAT.equals(alternateAT)) {
                            baseNameEquivalent = true;
                        }
                    }
                }
                // Check to see if the attribute options are equivalent.
                final Set<String> expectedOptions = Attribute.getOptions(attributeName);
                final Set<String> lowerExpectedOptions = new HashSet<>(StaticUtils.computeMapCapacity(expectedOptions.size()));
                for (final String s : expectedOptions) {
                    lowerExpectedOptions.add(StaticUtils.toLowerCase(s));
                }
                final Set<String> alternateOptions = Attribute.getOptions(alternateName);
                final Set<String> lowerAlternateOptions = new HashSet<>(StaticUtils.computeMapCapacity(alternateOptions.size()));
                for (final String s : alternateOptions) {
                    lowerAlternateOptions.add(StaticUtils.toLowerCase(s));
                }
                final boolean optionsEquivalent = lowerAlternateOptions.equals(lowerExpectedOptions);
                if (baseNameEquivalent && optionsEquivalent) {
                // This is fine.  The two attribute descriptions are logically
                // equivalent.  We'll continue using the attribute description that
                // was provided first.
                } else if (valueList.isEmpty() && baseNameEquivalent && lowerAlternateOptions.remove("binary") && lowerAlternateOptions.equals(lowerExpectedOptions)) {
                    // This means that the provided value is the first value for the
                    // attribute, and that the only significant difference is that the
                    // provided attribute description included an unexpected "binary"
                    // option.  We'll accept this, but will require any additional
                    // values for this modification to also include the binary option,
                    // and we'll use the binary option in the attribute that is
                    // eventually created.
                    attributeName = alternateName;
                } else {
                    // of options are incompatible.  This is not acceptable.
                    throw new LDIFException(ERR_READ_MOD_CR_ATTR_MISMATCH.get(firstLineNumber, line.substring(0, colonPos), attributeName), firstLineNumber, true, ldifLines, null);
                }
            }
            length = line.length();
            final ASN1OctetString value;
            if (length == (colonPos + 1)) {
                // The colon was the last character on the line.  This is fine.
                value = new ASN1OctetString();
            } else if (line.charAt(colonPos + 1) == ':') {
                // Skip over any spaces leading up to the value, and then the rest of
                // the string is the base64-encoded value.  This is unusual and
                // unnecessary, but is nevertheless acceptable.
                int pos = colonPos + 2;
                while ((pos < length) && (line.charAt(pos) == ' ')) {
                    pos++;
                }
                try {
                    value = new ASN1OctetString(Base64.decode(line.substring(pos)));
                } catch (final ParseException pe) {
                    Debug.debugException(pe);
                    throw new LDIFException(ERR_READ_CANNOT_BASE64_DECODE_ATTR.get(attributeName, firstLineNumber, pe.getMessage()), firstLineNumber, true, ldifLines, pe);
                } catch (final Exception e) {
                    Debug.debugException(e);
                    throw new LDIFException(ERR_READ_CANNOT_BASE64_DECODE_ATTR.get(firstLineNumber, e), firstLineNumber, true, ldifLines, e);
                }
            } else if (line.charAt(colonPos + 1) == '<') {
                // Skip over any spaces leading up to the value, and then the rest of
                // the string is a URL that indicates where to get the real content.
                // At the present time, we'll only support the file URLs.
                int pos = colonPos + 2;
                while ((pos < length) && (line.charAt(pos) == ' ')) {
                    pos++;
                }
                final String urlString = line.substring(pos);
                try {
                    final byte[] urlBytes = retrieveURLBytes(urlString, relativeBasePath, firstLineNumber);
                    value = new ASN1OctetString(urlBytes);
                } catch (final Exception e) {
                    Debug.debugException(e);
                    throw new LDIFException(ERR_READ_URL_EXCEPTION.get(attributeName, urlString, firstLineNumber, e), firstLineNumber, true, ldifLines, e);
                }
            } else {
                // Skip over any spaces leading up to the value, and then the rest of
                // the string is the value.
                int pos = colonPos + 1;
                while ((pos < length) && (line.charAt(pos) == ' ')) {
                    pos++;
                }
                value = new ASN1OctetString(line.substring(pos));
            }
            valueList.add(value);
        }
        final ASN1OctetString[] values = new ASN1OctetString[valueList.size()];
        valueList.toArray(values);
        // value.
        if ((modType.intValue() == ModificationType.ADD.intValue()) && (values.length == 0)) {
            throw new LDIFException(ERR_READ_MOD_CR_NO_ADD_VALUES.get(attributeName, firstLineNumber), firstLineNumber, true, ldifLines, null);
        }
        // value.
        if ((modType.intValue() == ModificationType.INCREMENT.intValue()) && (values.length != 1)) {
            throw new LDIFException(ERR_READ_MOD_CR_INVALID_INCR_VALUE_COUNT.get(firstLineNumber, attributeName), firstLineNumber, true, ldifLines, null);
        }
        modList.add(new Modification(modType, attributeName, values));
    }
    final Modification[] mods = new Modification[modList.size()];
    modList.toArray(mods);
    return mods;
}
Also used : ASN1OctetString(com.unboundid.asn1.ASN1OctetString) Modification(com.unboundid.ldap.sdk.Modification) HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) ASN1OctetString(com.unboundid.asn1.ASN1OctetString) ParseException(java.text.ParseException) LDAPException(com.unboundid.ldap.sdk.LDAPException) IOException(java.io.IOException) AttributeTypeDefinition(com.unboundid.ldap.sdk.schema.AttributeTypeDefinition) ModificationType(com.unboundid.ldap.sdk.ModificationType) ParseException(java.text.ParseException) NotNull(com.unboundid.util.NotNull)

Example 2 with ModificationType

use of com.unboundid.ldap.sdk.ModificationType in project ldapsdk by pingidentity.

the class JNDIConverter method convertModification.

/**
 * Converts the provided JNDI modification item to an LDAP SDK modification.
 *
 * @param  m  The JNDI modification item to be converted.
 *
 * @return  The LDAP SDK modification that corresponds to the provided JNDI
 *          modification item.
 *
 * @throws  NamingException  If a problem is encountered during the conversion
 *                           process.
 */
@Nullable()
public static Modification convertModification(@Nullable final ModificationItem m) throws NamingException {
    if (m == null) {
        return null;
    }
    final ModificationType modType;
    switch(m.getModificationOp()) {
        case DirContext.ADD_ATTRIBUTE:
            modType = ModificationType.ADD;
            break;
        case DirContext.REMOVE_ATTRIBUTE:
            modType = ModificationType.DELETE;
            break;
        case DirContext.REPLACE_ATTRIBUTE:
            modType = ModificationType.REPLACE;
            break;
        default:
            throw new NamingException("Unsupported modification type " + m);
    }
    final Attribute a = convertAttribute(m.getAttribute());
    return new Modification(modType, a.getName(), a.getRawValues());
}
Also used : Modification(com.unboundid.ldap.sdk.Modification) ModificationType(com.unboundid.ldap.sdk.ModificationType) Attribute(com.unboundid.ldap.sdk.Attribute) BasicAttribute(javax.naming.directory.BasicAttribute) NamingException(javax.naming.NamingException) Nullable(com.unboundid.util.Nullable)

Example 3 with ModificationType

use of com.unboundid.ldap.sdk.ModificationType in project zm-mailbox by Zimbra.

the class UBIDModificationList method modifyAttr.

@Override
public void modifyAttr(String name, String value, Entry entry, boolean containsBinaryData, boolean isBinaryTransfer) {
    ModificationType modOp = (StringUtil.isNullOrEmpty(value)) ? ModificationType.DELETE : ModificationType.REPLACE;
    if (modOp == ModificationType.DELETE) {
        // make sure it exists
        if (entry.getAttr(name, false) == null) {
            return;
        }
    }
    if (modOp == ModificationType.DELETE) {
        removeAttr(name, isBinaryTransfer);
    } else {
        String[] val = new String[] { value };
        modifyAttr(name, val, containsBinaryData, isBinaryTransfer);
    }
}
Also used : ModificationType(com.unboundid.ldap.sdk.ModificationType) ASN1OctetString(com.unboundid.asn1.ASN1OctetString)

Aggregations

ModificationType (com.unboundid.ldap.sdk.ModificationType)3 ASN1OctetString (com.unboundid.asn1.ASN1OctetString)2 Modification (com.unboundid.ldap.sdk.Modification)2 Attribute (com.unboundid.ldap.sdk.Attribute)1 LDAPException (com.unboundid.ldap.sdk.LDAPException)1 AttributeTypeDefinition (com.unboundid.ldap.sdk.schema.AttributeTypeDefinition)1 NotNull (com.unboundid.util.NotNull)1 Nullable (com.unboundid.util.Nullable)1 IOException (java.io.IOException)1 ParseException (java.text.ParseException)1 ArrayList (java.util.ArrayList)1 HashSet (java.util.HashSet)1 Set (java.util.Set)1 NamingException (javax.naming.NamingException)1 BasicAttribute (javax.naming.directory.BasicAttribute)1