blob: 7725342d383ec7de45bf988770856c9a3546fa13 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.api.dsmlv2;
import java.util.Arrays;
import java.util.Collection;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import org.apache.directory.api.asn1.util.Asn1Buffer;
import org.apache.directory.api.dsmlv2.actions.ReadSoapHeader;
import org.apache.directory.api.dsmlv2.request.BatchRequestDsml;
import org.apache.directory.api.dsmlv2.request.BatchRequestDsml.Processing;
import org.apache.directory.api.dsmlv2.request.BatchRequestDsml.ResponseOrder;
import org.apache.directory.api.i18n.I18n;
import org.apache.directory.api.ldap.codec.api.LdapApiService;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.ldif.LdifUtils;
import org.apache.directory.api.ldap.model.message.Control;
import org.apache.directory.api.util.Base64;
import org.apache.directory.api.util.Strings;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.dom4j.io.DocumentResult;
import org.dom4j.io.DocumentSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/**
* This class is a Helper class for the DSML Parser
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public final class ParserUtils
{
/** W3C XML Schema URI. */
public static final String XML_SCHEMA_URI = "http://www.w3.org/2001/XMLSchema";
/** W3C XML Schema Instance URI. */
public static final String XML_SCHEMA_INSTANCE_URI = "http://www.w3.org/2001/XMLSchema-instance";
/** Base-64 identifier. */
public static final String BASE64BINARY = "base64Binary";
/** XSI namespace prefix. */
public static final String XSI = "xsi";
/** XSD namespace prefix. */
public static final String XSD = "xsd";
/** The DSML namespace */
public static final Namespace DSML_NAMESPACE = new Namespace( null, "urn:oasis:names:tc:DSML:2:0:core" );
/** The XSD namespace */
public static final Namespace XSD_NAMESPACE = new Namespace( XSD, XML_SCHEMA_URI );
/** The XSI namespace */
public static final Namespace XSI_NAMESPACE = new Namespace( XSI, XML_SCHEMA_INSTANCE_URI );
/** A logger for this class */
private static final Logger LOG = LoggerFactory.getLogger( ParserUtils.class );
/**
* GrammarAction that reads the SOAP header data
*/
public static final GrammarAction READ_SOAP_HEADER = new ReadSoapHeader();
private ParserUtils()
{
}
/**
* Returns the value of the attribute 'type' of the "XMLSchema-instance' namespace if it exists
*
* @param xpp the XPP parser to use
* @return the value of the attribute 'type' of the "XMLSchema-instance' namespace if it exists
*/
public static String getXsiTypeAttributeValue( XmlPullParser xpp )
{
String type = null;
int nbAttributes = xpp.getAttributeCount();
for ( int i = 0; i < nbAttributes; i++ )
{
// Checking if the attribute 'type' from XML Schema Instance namespace is used.
if ( "type".equals( xpp.getAttributeName( i ) )
&& xpp.getNamespace( xpp.getAttributePrefix( i ) ).equals( XML_SCHEMA_INSTANCE_URI ) )
{
type = xpp.getAttributeValue( i );
break;
}
}
return type;
}
/**
* Tells is the given value is a Base64 binary value
*
* @param parser the XPP parser to use
* @param attrValue the attribute value
* @return true if the value of the current tag is Base64BinaryEncoded, false if not
*/
public static boolean isBase64BinaryValue( XmlPullParser parser, String attrValue )
{
if ( attrValue == null )
{
return false;
}
// We are looking for something that should look like that: "aNameSpace:base64Binary"
// We split the String. The first element should be the namespace prefix and the second "base64Binary"
String[] splitedString = attrValue.split( ":" );
return ( splitedString.length == 2 ) && ( XML_SCHEMA_URI.equals( parser.getNamespace( splitedString[0] ) ) )
&& ( BASE64BINARY.equals( splitedString[1] ) );
}
/**
* Indicates if the value needs to be encoded as Base64
*
* @param value the value to check
* @return true if the value needs to be encoded as Base64
*/
public static boolean needsBase64Encoding( Object value )
{
if ( value instanceof Value )
{
return false;
}
else if ( value instanceof byte[] )
{
return true;
}
else if ( value instanceof String )
{
return !LdifUtils.isLDIFSafe( ( String ) value );
}
return true;
}
/**
* Encodes the value as a Base64 String
*
* @param value the value to encode
* @return the value encoded as a Base64 String
*/
public static String base64Encode( Object value )
{
if ( value instanceof byte[] )
{
return new String( Base64.encode( ( byte[] ) value ) );
}
else if ( value instanceof String )
{
return new String( Base64.encode( Strings.getBytesUtf8( ( String ) value ) ) );
}
return "";
}
/**
* Parses and verify the parsed value of the requestID
*
* @param attributeValue the value of the attribute
* @param xpp the XmlPullParser
* @return the int value of the resquestID
* @throws XmlPullParserException if RequestID isn't an Integer and if requestID is below 0
*/
public static int parseAndVerifyRequestID( String attributeValue, XmlPullParser xpp ) throws XmlPullParserException
{
try
{
int requestID = Integer.parseInt( attributeValue );
if ( requestID < 0 )
{
throw new XmlPullParserException( I18n.err( I18n.ERR_03016_BELOW_0_REQUEST_ID, requestID ), xpp, null );
}
return requestID;
}
catch ( NumberFormatException nfe )
{
throw new XmlPullParserException( I18n.err( I18n.ERR_03012_REQUEST_ID_NOT_INTEGER ), xpp, nfe );
}
}
/**
* Adds Controls to the given Element.
*
* @param codec The LDAP Service to use
* @param element the element to add the Controls to
* @param controls a List of Controls
* @param isRequest A flag set to <tt>true</tt> if teh LDapMessage is a request
*/
public static void addControls( LdapApiService codec, Element element, Collection<Control> controls, boolean isRequest )
{
if ( controls != null )
{
for ( Control control : controls )
{
Element controlElement = element.addElement( "control" );
if ( control.getOid() != null )
{
controlElement.addAttribute( "type", control.getOid() );
}
if ( control.isCritical() )
{
controlElement.addAttribute( "criticality", "true" );
}
Asn1Buffer asn1Buffer = new Asn1Buffer();
if ( isRequest )
{
codec.getRequestControlFactories().get( control.getOid() ).encodeValue( asn1Buffer, control );
}
else
{
codec.getResponseControlFactories().get( control.getOid() ).encodeValue( asn1Buffer, control );
}
byte[] value = asn1Buffer.getBytes().array();
if ( value != null )
{
if ( ParserUtils.needsBase64Encoding( value ) )
{
element.getDocument().getRootElement().add( XSD_NAMESPACE );
element.getDocument().getRootElement().add( XSI_NAMESPACE );
Element valueElement = controlElement.addElement( "controlValue" ).addText(
ParserUtils.base64Encode( value ) );
valueElement.addAttribute( new QName( "type", XSI_NAMESPACE ), ParserUtils.XSD + ":"
+ ParserUtils.BASE64BINARY );
}
else
{
controlElement.addElement( "controlValue" ).setText( Arrays.toString( value ) );
}
}
}
}
}
/**
* Indicates if a request ID is needed.
*
* @param container the associated container
* @return true if a request ID is needed (ie Processing=Parallel and ResponseOrder=Unordered)
* @throws XmlPullParserException if the batch request has not been parsed yet
*/
public static boolean isRequestIdNeeded( Dsmlv2Container container ) throws XmlPullParserException
{
BatchRequestDsml batchRequest = container.getBatchRequest();
if ( batchRequest == null )
{
throw new XmlPullParserException( I18n.err( I18n.ERR_03003_UNABLE_TO_FIND_BATCH_REQUEST ), container.getParser(), null );
}
return ( batchRequest.getProcessing() == Processing.PARALLEL ) && ( batchRequest.getResponseOrder() == ResponseOrder.UNORDERED );
}
/**
* XML Pretty Printer XSLT Transformation
*
* @param document the Dom4j Document
* @return the transformed document
*/
public static Document styleDocument( Document document )
{
// load the transformer using JAXP
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = null;
try
{
factory.setFeature( javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE );
try
{
factory.setAttribute( javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD, "" );
factory.setAttribute( javax.xml.XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "" );
}
catch ( IllegalArgumentException ex )
{
// ignore
}
transformer = factory.newTransformer( new StreamSource( ParserUtils.class
.getResourceAsStream( "/org/apache/directory/shared/dsmlv2/DSMLv2.xslt" ) ) );
}
catch ( TransformerConfigurationException e1 )
{
if ( LOG.isWarnEnabled() )
{
LOG.warn( I18n.msg( I18n.MSG_3000_FAILED_TO_CREATE_XSLT_TRANSFORMER ), e1 );
}
// return original document
return document;
}
// now lets style the given document
DocumentSource source = new DocumentSource( document );
DocumentResult result = new DocumentResult();
try
{
transformer.transform( source, result );
}
catch ( TransformerException e )
{
// return original document
return document;
}
// return the transformed document
return result.getDocument();
}
}