001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 * 
010 *    https://www.apache.org/licenses/LICENSE-2.0
011 * 
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 * 
019 */
020package org.apache.directory.api.ldap.codec.protocol.mina;
021
022
023import java.nio.ByteBuffer;
024import java.util.ArrayList;
025import java.util.List;
026
027import org.apache.directory.api.asn1.DecoderException;
028import org.apache.directory.api.asn1.ber.Asn1Decoder;
029import org.apache.directory.api.asn1.ber.tlv.TLVStateEnum;
030import org.apache.directory.api.i18n.I18n;
031import org.apache.directory.api.ldap.codec.api.LdapDecoder;
032import org.apache.directory.api.ldap.codec.api.LdapMessageContainer;
033import org.apache.directory.api.ldap.codec.api.ResponseCarryingException;
034import org.apache.directory.api.ldap.model.constants.Loggers;
035import org.apache.directory.api.ldap.model.exception.ResponseCarryingMessageException;
036import org.apache.directory.api.ldap.model.message.AbstractMessage;
037import org.apache.directory.api.ldap.model.message.Message;
038import org.apache.directory.api.util.Strings;
039import org.apache.mina.core.buffer.IoBuffer;
040import org.apache.mina.core.session.IoSession;
041import org.apache.mina.filter.codec.ProtocolDecoder;
042import org.apache.mina.filter.codec.ProtocolDecoderOutput;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046
047/**
048 * A LDAP message decoder. It is based on api-ldap decoder.
049 *
050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051 */
052public class LdapProtocolDecoder implements ProtocolDecoder
053{
054    /** The logger */
055    private static final Logger CODEC_LOG = LoggerFactory.getLogger( Loggers.CODEC_LOG.getName() );
056
057    /**
058     * Creates a new instance of LdapProtocolEncoder.
059     */
060    public LdapProtocolDecoder()
061    {
062    }
063
064
065    /**
066     * {@inheritDoc}
067     */
068    @Override
069    public void decode( IoSession session, IoBuffer in, ProtocolDecoderOutput out ) throws Exception
070    {
071        @SuppressWarnings("unchecked")
072        LdapMessageContainer<AbstractMessage> messageContainer =
073            ( LdapMessageContainer<AbstractMessage> )
074            session.getAttribute( LdapDecoder.MESSAGE_CONTAINER_ATTR );
075
076        if ( session.containsAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR ) )
077        {
078            int maxPDUSize = ( Integer ) session.getAttribute( LdapDecoder.MAX_PDU_SIZE_ATTR );
079
080            messageContainer.setMaxPDUSize( maxPDUSize );
081        }
082
083        List<Message> decodedMessages = new ArrayList<>();
084        ByteBuffer buf = in.buf();
085
086        decode( buf, messageContainer, decodedMessages );
087
088        for ( Message message : decodedMessages )
089        {
090            out.write( message );
091        }
092    }
093
094
095    /**
096     * Decode an incoming buffer into LDAP messages. The result can be 0, 1 or many
097     * LDAP messages, which will be stored into the array the caller has created.
098     * 
099     * @param buffer The incoming byte buffer
100     * @param messageContainer The LdapMessageContainer which will be used to store the
101     * message being decoded. If the message is not fully decoded, the ucrrent state
102     * is stored into this container
103     * @param decodedMessages The list of decoded messages
104     * @throws DecoderException If the decoding failed
105     */
106    private void decode( ByteBuffer buffer, LdapMessageContainer<AbstractMessage> messageContainer,
107        List<Message> decodedMessages ) throws DecoderException
108    {
109        buffer.mark();
110
111        while ( buffer.hasRemaining() )
112        {
113            try
114            {
115                if ( CODEC_LOG.isDebugEnabled() )
116                {
117                    CODEC_LOG.debug( I18n.msg( I18n.MSG_14000_DECODING_PDU ) );
118
119                    int size = buffer.limit();
120                    int position = buffer.position();
121                    int pduLength = size - position;
122
123                    byte[] array = new byte[pduLength];
124
125                    System.arraycopy( buffer.array(), position, array, 0, pduLength );
126
127                    if ( array.length == 0 )
128                    {
129                        CODEC_LOG.debug( I18n.msg( I18n.MSG_14001_NULL_BUFFER ) );
130                    }
131                    else
132                    {
133                        CODEC_LOG.debug( Strings.dumpBytes( array ) );
134                    }
135                }
136
137                Asn1Decoder.decode( buffer, messageContainer );
138
139                if ( messageContainer.getState() == TLVStateEnum.PDU_DECODED )
140                {
141                    if ( CODEC_LOG.isDebugEnabled() )
142                    {
143                        CODEC_LOG.debug( I18n.msg( I18n.MSG_14002_DECODED_LDAP_MESSAGE, messageContainer.getMessage() ) );
144                    }
145
146                    Message message = messageContainer.getMessage();
147
148                    decodedMessages.add( message );
149
150                    messageContainer.clean();
151                }
152            }
153            catch ( ResponseCarryingException rce )
154            {
155                buffer.clear();
156                messageContainer.clean();
157                
158                // Transform the DecoderException message to a MessageException
159                ResponseCarryingMessageException rcme = new ResponseCarryingMessageException( rce.getMessage(), rce );
160                rcme.setResponse( rce.getResponse() );
161
162                throw rcme;
163            }
164            catch ( DecoderException de )
165            {
166                buffer.clear();
167                messageContainer.clean();
168
169                // TODO : This is certainly not the way we should handle such an exception !
170                throw new ResponseCarryingException( de.getMessage(), de );
171            }
172        }
173    }
174
175
176    /**
177     * {@inheritDoc}
178     */
179    @Override
180    public void finishDecode( IoSession session, ProtocolDecoderOutput out ) throws Exception
181    {
182        // Nothing to do
183    }
184
185
186    /**
187     * {@inheritDoc}
188     */
189    @Override
190    public void dispose( IoSession session ) throws Exception
191    {
192        // Nothing to do
193    }
194}