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     *    http://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     */
020    package org.apache.directory.shared.ldap.name;
021    
022    
023    import javax.naming.InvalidNameException;
024    
025    import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
026    import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
027    import org.apache.directory.shared.ldap.util.DNUtils;
028    import org.apache.directory.shared.ldap.util.Position;
029    import org.apache.directory.shared.ldap.util.StringTools;
030    
031    
032    /**
033     * This class parse the name-component part or the following BNF grammar (as of
034     * RFC2253, par. 3, and RFC1779, fig. 1) : <br> - &lt;name-component&gt; ::=
035     * &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt;
036     * &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; <br> -
037     * &lt;attributeTypeAndValues&gt; ::= &lt;spaces&gt; '+' &lt;spaces&gt;
038     * &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt;
039     * &lt;attributeValue&gt; &lt;attributeTypeAndValues&gt; | e <br> -
040     * &lt;attributeType&gt; ::= [a-zA-Z] &lt;keychars&gt; | &lt;oidPrefix&gt; [0-9]
041     * &lt;digits&gt; &lt;oids&gt; | [0-9] &lt;digits&gt; &lt;oids&gt; <br> -
042     * &lt;keychars&gt; ::= [a-zA-Z] &lt;keychars&gt; | [0-9] &lt;keychars&gt; | '-'
043     * &lt;keychars&gt; | e <br> - &lt;oidPrefix&gt; ::= 'OID.' | 'oid.' | e <br> -
044     * &lt;oids&gt; ::= '.' [0-9] &lt;digits&gt; &lt;oids&gt; | e <br> -
045     * &lt;attributeValue&gt; ::= &lt;pairs-or-strings&gt; | '#' &lt;hexstring&gt;
046     * |'"' &lt;quotechar-or-pairs&gt; '"' <br> - &lt;pairs-or-strings&gt; ::= '\'
047     * &lt;pairchar&gt; &lt;pairs-or-strings&gt; | &lt;stringchar&gt;
048     * &lt;pairs-or-strings&gt; | e <br> - &lt;quotechar-or-pairs&gt; ::=
049     * &lt;quotechar&gt; &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt;
050     * &lt;quotechar-or-pairs&gt; | e <br> - &lt;pairchar&gt; ::= ',' | '=' | '+' |
051     * '&lt;' | '&gt;' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br> -
052     * &lt;hexstring&gt; ::= [0-9a-fA-F] [0-9a-fA-F] &lt;hexpairs&gt; <br> -
053     * &lt;hexpairs&gt; ::= [0-9a-fA-F] [0-9a-fA-F] &lt;hexpairs&gt; | e <br> -
054     * &lt;digits&gt; ::= [0-9] &lt;digits&gt; | e <br> - &lt;stringchar&gt; ::=
055     * [0x00-0xFF] - [,=+&lt;&gt;#;\"\n\r] <br> - &lt;quotechar&gt; ::= [0x00-0xFF] -
056     * [\"] <br> - &lt;separator&gt; ::= ',' | ';' <br> - &lt;spaces&gt; ::= ' '
057     * &lt;spaces&gt; | e <br>
058     * <br>
059     * A RDN is a part of a DN. It can be composed of many types, as in the RDN
060     * following RDN :<br>
061     * ou=value + cn=other value<br>
062     * <br>
063     * In this case, we have to store an 'ou' and a 'cn' in the RDN.<br>
064     * <br>
065     * The types are case insensitive. <br>
066     * Spaces before and after types and values are not stored.<br>
067     * Spaces before and after '+' are not stored.<br>
068     * <br>
069     * Thus, we can consider that the following RDNs are equals :<br>
070     * <br>
071     * 'ou=test 1'<br> ' ou=test 1'<br>
072     * 'ou =test 1'<br>
073     * 'ou= test 1'<br>
074     * 'ou=test 1 '<br> ' ou = test 1 '<br>
075     * <br>
076     * So are the following :<br>
077     * <br>
078     * 'ou=test 1+cn=test 2'<br>
079     * 'ou = test 1 + cn = test 2'<br> ' ou =test 1+ cn =test 2 ' <br>
080     * 'cn = test 2 +ou = test 1'<br>
081     * <br>
082     * but the following are not equal :<br>
083     * 'ou=test 1' <br>
084     * 'ou=test 1'<br>
085     * because we have more than one spaces inside the value.<br>
086     * <br>
087     *
088     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
089     * @version $Rev: 798550 $, $Date: 2009-07-28 16:54:01 +0200 (Mar, 28 jul 2009) $
090     */
091    public class RdnParser
092    {
093        /**
094         * Parse this rule : <br>
095         * <p>
096         * &lt;oidValue&gt; ::= [0-9] &lt;digits&gt; &lt;oids&gt;
097         * </p>
098         *
099         * @param bytes The bytes array to parse
100         * @param pos The current position in the byte buffer
101         * @return The new position in the vyte array, or PARSING_ERROR if the rule
102         *         does not apply to the byte array
103         */
104        private static String parseOidValue( byte[] bytes, Position pos )
105        {
106            pos.start += pos.length;
107            pos.end = pos.start;
108    
109            // <attributType> ::= [0-9] <digits> <oids>
110            if ( !StringTools.isDigit( bytes, pos.start ) )
111            {
112                // Nope... An error
113                return null;
114            }
115            else
116            {
117                // Let's process an oid
118                pos.end++;
119    
120                while ( StringTools.isDigit( bytes, pos.end ) )
121                {
122                    pos.end++;
123                }
124    
125                // <oids> ::= '.' [0-9] <digits> <oids> | e
126                if ( !StringTools.isCharASCII( bytes, pos.end, '.' ) )
127                {
128                    return null;
129                }
130                else
131                {
132                    do
133                    {
134                        pos.end++;
135    
136                        if ( !StringTools.isDigit( bytes, pos.end ) )
137                        {
138                            return null;
139                        }
140                        else
141                        {
142                            pos.end++;
143    
144                            while ( StringTools.isDigit( bytes, pos.end ) )
145                            {
146                                pos.end++;
147                            }
148                        }
149    
150                    }
151                    while ( StringTools.isCharASCII( bytes, pos.end, '.' ) );
152    
153                    return StringTools.utf8ToString( bytes, pos.start - pos.length, pos.end - pos.start + pos.length );
154                }
155            }
156        }
157    
158        
159        /**
160         * Validate this rule : <br>
161         * <p>
162         * &lt;oidValue&gt; ::= [0-9] &lt;digits&gt; &lt;oids&gt;
163         * </p>
164         *
165         * @param bytes The byte array to parse
166         * @param pos The current position in the byte buffer
167         * @return <code>true</code> if this is a valid OID
168         */
169        private static boolean isValidOidValue( byte[] bytes, Position pos )
170        {
171            pos.start += pos.length;
172            pos.end = pos.start;
173    
174            // <attributType> ::= [0-9] <digits> <oids>
175            if ( !StringTools.isDigit( bytes, pos.start ) )
176            {
177                // Nope... An error
178                return false;
179            }
180            else
181            {
182                // Let's process an oid
183                pos.end++;
184    
185                while ( StringTools.isDigit( bytes, pos.end ) )
186                {
187                    pos.end++;
188                }
189    
190                // <oids> ::= '.' [0-9] <digits> <oids> | e
191                if ( !StringTools.isCharASCII( bytes, pos.end, '.' ) )
192                {
193                    return false;
194                }
195                else
196                {
197                    do
198                    {
199                        pos.end++;
200    
201                        if ( !StringTools.isDigit( bytes, pos.end ) )
202                        {
203                            return false;
204                        }
205                        else
206                        {
207                            pos.end++;
208    
209                            while ( StringTools.isDigit( bytes, pos.end ) )
210                            {
211                                pos.end++;
212                            }
213                        }
214    
215                    }
216                    while ( StringTools.isCharASCII( bytes, pos.end, '.' ) );
217    
218                    return true;
219                }
220            }
221        }
222    
223    
224        /**
225         * Parse this rule : <br>
226         * <p>
227         * &lt;oidPrefix&gt; ::= 'OID.' | 'oid.' | e
228         * </p>
229         *
230         * @param bytes The buffer to parse
231         * @param pos The current position in the byte array
232         * @return The new position in the byte array, or PARSING_ERROR if the rule
233         *         does not apply to the byte array
234         */
235        private static int parseOidPrefix( byte[] bytes, Position pos )
236        {
237            if ( StringTools.isICharASCII( bytes, pos.start, 'O' ) &&
238                 StringTools.isICharASCII( bytes, pos.start + 1, 'I' ) && 
239                 StringTools.isICharASCII( bytes, pos.start + 2, 'D' ) &&
240                 StringTools.isICharASCII( bytes, pos.start + 3, '.' ) )
241            {
242                pos.end += DNUtils.OID_LOWER.length();
243                return DNUtils.PARSING_OK;
244            }
245            else
246            {
247                return DNUtils.PARSING_ERROR;
248            }
249        }
250    
251    
252        /**
253         * Parse this rule : <br>
254         * <p>
255         * &lt;attributType&gt; ::= [a-zA-Z] &lt;keychars&gt; | &lt;oidPrefix&gt;
256         * [0-9] &lt;digits&gt; &lt;oids&gt; | [0-9] &lt;digits&gt; &lt;oids&gt;
257         * </p>
258         * The string *MUST* be an ASCII string, not an unicode string.
259         *
260         * @param bytes The byte array to parse
261         * @param pos The current position in the byte array
262         * @return The new position in the byte array, or PARSING_ERROR if the rule
263         *         does not apply to the byte array
264         */
265        private static String parseAttributeType( byte[] bytes, Position pos )
266        {
267            // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits>
268            // <oids> | [0-9] <digits> <oids>
269            if ( StringTools.isAlphaASCII( bytes, pos.start ) )
270            {
271                // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9]
272                // <digits> <oids>
273    
274                // We have got an Alpha char, it may be the begining of an OID ?
275                if ( parseOidPrefix( bytes, pos ) != DNUtils.PARSING_ERROR )
276                {
277                    pos.length = 4;
278    
279                    return parseOidValue( bytes, pos );
280                }
281                else
282                {
283                    // It's not an oid, it's a String (ASCII)
284                    // <attributType> ::= [a-zA-Z] <keychars>
285                    // <keychars> ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-'
286                    // <keychar> | e
287                    pos.end++;
288    
289                    while ( StringTools.isAlphaDigitMinus( bytes, pos.end ) )
290                    {
291                        pos.end++;
292                    }
293    
294                    return StringTools.utf8ToString( bytes, pos.start, pos.end - pos.start );
295                }
296            }
297            else
298            {
299                // An oid
300                // <attributType> ::= [0-9] <digits> <oids>
301                return parseOidValue( bytes, pos );
302            }
303        }
304    
305    
306        /**
307         * Parse this rule : <br>
308         * <p>
309         * &lt;attributType&gt; ::= [a-zA-Z] &lt;keychars&gt; | &lt;oidPrefix&gt;
310         * [0-9] &lt;digits&gt; &lt;oids&gt; | [0-9] &lt;digits&gt; &lt;oids&gt;
311         * </p>
312         * The string *MUST* be an ASCII string, not an unicode string.
313         *
314         * @param bytes The byte array to parse
315         * @param pos The current position in the byte array
316         * @return The new position in the byte array, or PARSING_ERROR if the rule
317         *         does not apply to the byte array
318         */
319        private static boolean isValidAttributeType( byte[] bytes, Position pos )
320        {
321            // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits>
322            // <oids> | [0-9] <digits> <oids>
323            if ( StringTools.isAlphaASCII( bytes, pos.start ) )
324            {
325                // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9]
326                // <digits> <oids>
327    
328                // We have got an Alpha char, it may be the begining of an OID ?
329                if ( parseOidPrefix( bytes, pos ) != DNUtils.PARSING_ERROR )
330                {
331                    pos.length = 4;
332    
333                    return isValidOidValue( bytes, pos );
334                }
335                else
336                {
337                    // It's not an oid, it's a String (ASCII)
338                    // <attributType> ::= [a-zA-Z] <keychars>
339                    // <keychars> ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-'
340                    // <keychar> | e
341                    pos.end++;
342    
343                    while ( StringTools.isAlphaDigitMinus( bytes, pos.end ) )
344                    {
345                        pos.end++;
346                    }
347    
348                    return true;
349                }
350            }
351            else
352            {
353                // An oid
354                // <attributType> ::= [0-9] <digits> <oids>
355                return isValidOidValue( bytes, pos );
356            }
357        }
358    
359    
360        /**
361         * Parse this rule : <br>
362         * <p>
363         * &lt;attributeValue&gt; ::= &lt;pairs-or-strings&gt; | '#'
364         *     &lt;hexstring&gt; |'"' &lt;quotechar-or-pairs&gt; '"' <br>
365         * &lt;pairs-or-strings&gt; ::= '\' &lt;pairchar&gt;
366         * &lt;pairs-or-strings&gt; | &lt;stringchar&gt; &lt;pairs-or-strings&gt; | |
367         * e <br>
368         * &lt;quotechar-or-pairs&gt; ::= &lt;quotechar&gt;
369         * &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt;
370         * &lt;quotechar-or-pairs&gt; | e <br>
371         * </p>
372         *
373         * @param bytes The byte array to parse
374         * @param pos The current position in the byte array
375         * @return The new position in the byte array, or PARSING_ERROR if the rule
376         *         does not apply to the byte array
377         */
378        private static Object parseAttributeValue( byte[] bytes, Position pos )
379        {
380            if ( pos.start == bytes.length )
381            {
382                // This is an empty value
383                return "";
384            }
385            
386            //StringBuffer sb = new StringBuffer();
387            byte c = bytes[pos.start];
388            byte[] buffer = new byte[bytes.length];
389            int currPos = 0;
390    
391            if ( c == '#' )
392            {
393                pos.start++;
394                int nbHex = 0;
395                int currentPos = pos.start;
396    
397                // First, we will count the number of hexPairs
398                while ( DNUtils.parseHexPair( bytes, currentPos ) >= 0 )
399                {
400                    nbHex++;
401                    currentPos += DNUtils.TWO_CHARS;
402                }
403    
404                byte[] hexValue = new byte[nbHex];
405    
406                // Now, convert the value
407                // <attributeValue> ::= '#' <hexstring>
408                if ( DNUtils.parseHexString( bytes, hexValue, pos ) == DNUtils.PARSING_ERROR )
409                {
410                    return null;
411                }
412    
413                pos.start--;
414                StringTools.trimRight( bytes, pos );
415                pos.length = pos.end - pos.start;
416    
417                return hexValue;
418            }
419            else if ( c == '"' )
420            {
421                pos.start++;
422                pos.length = 0;
423                pos.end = pos.start;
424                int nbBytes = 0;
425                int length = 0;
426    
427                // <attributeValue> ::= '"' <quotechar-or-pair> '"'
428                // <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\'
429                //                                                  <pairchar> <quotechar-or-pairs> | e
430                while ( true )
431                {
432                    if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
433                    {
434                        pos.end++;
435                        int nbChars = 0;
436    
437                        if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) == DNUtils.PARSING_ERROR )
438                        {
439                            return null;
440                        }
441                        else
442                        {
443                            if ( nbChars == 1 )
444                            {
445                                buffer[currPos++] = bytes[pos.end];
446                            }
447                            else
448                            {
449                                byte b = StringTools.getHexValue( bytes[pos.end], bytes[pos.end + 1] );
450    
451                                buffer[currPos++] = b;
452                            }
453                            
454                            pos.end += nbChars;
455                            length += nbChars;
456                        }
457                    }
458                    else if ( ( nbBytes = DNUtils.isQuoteChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
459                    {
460                        for ( int i = 0; i < nbBytes; i++ )
461                        {
462                            buffer[currPos++] = bytes[pos.end + i];
463                        }
464                        
465                        pos.end += nbBytes;
466                        length += nbBytes;
467                    }
468                    else
469                    {
470                        //pos.length = pos.end - pos.start;
471                        break;
472                    }
473                }
474    
475                if ( StringTools.isCharASCII( bytes, pos.end, '"' ) )
476                {
477                    pos.end++;
478                    pos.length = length + 2;
479                    return StringTools.utf8ToString( buffer, length );
480                }
481                else
482                {
483                    return null;
484                }
485            }
486            else
487            {
488                int escapedSpace = -1;
489    
490                while ( true )
491                {
492                    if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
493                    {
494                        // '\' <pairchar> <pairs-or-strings>
495                        pos.end++;
496    
497                        int nbChars = 0;
498                        
499                        if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) == DNUtils.PARSING_ERROR )
500                        {
501                            return null;
502                        }
503                        else
504                        {
505                            if ( nbChars == 1 )
506                            {
507                                buffer[currPos++] = bytes[pos.end];
508                            }
509                            else
510                            {
511                                byte b = StringTools.getHexValue( bytes[pos.end], bytes[pos.end + 1] );
512    
513                                buffer[currPos++] = b;
514                            }
515    
516                            if ( bytes[pos.end] == ' ' )
517                            {
518                                escapedSpace = currPos;
519                            }
520                            else
521                            {
522                                escapedSpace = -1;
523                            }
524    
525                            pos.end += nbChars;
526                        }
527                    }
528                    else
529                    {
530                        int nbChars = 0;
531    
532                        // <stringchar> <pairs-or-strings>
533                        if ( ( nbChars = DNUtils.isStringChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
534                        {
535                            // If the char is not a space, we have to switch off the escapedSpace flag
536                            if ( ( escapedSpace != -1 ) && ! StringTools.isCharASCII( bytes, pos.end, ' ' ) )
537                            {
538                                escapedSpace = -1;
539                            }
540                            
541                            // A special case : if we have some spaces before the
542                            // '+' character,
543                            // we MUST skip them.
544                            if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
545                            {
546                                //StringTools.trimLeft( string, pos );
547    
548                                if ( ( DNUtils.isStringChar( bytes, pos.end ) == DNUtils.PARSING_ERROR )
549                                    && ( !StringTools.isCharASCII( bytes, pos.end, '\\' ) ) )
550                                {
551                                    // Ok, we are done with the stringchar.
552                                    String result = StringTools.trimRight( 
553                                        StringTools.utf8ToString( bytes, pos.start, pos.start + pos.length ) );
554                                    
555                                    return result;
556                                }
557                                else
558                                {
559                                    for ( int i = 0; i < nbChars; i++ )
560                                    {
561                                        buffer[currPos++] = bytes[pos.end];
562                                        pos.end ++;
563                                    }
564                                }
565                            }
566                            else
567                            {
568                                for ( int i = 0; i < nbChars; i++ )
569                                {
570                                    buffer[currPos++] = bytes[pos.end];
571                                    pos.end ++;
572                                }
573                            }
574                        }
575                        else
576                        {
577                            pos.length = pos.end - pos.start;
578                            
579                            if ( escapedSpace == -1 )
580                            {
581                                String result = StringTools.trimRight( StringTools.utf8ToString( buffer, currPos ) );
582                                return result;
583                            }
584                            
585                            String result = StringTools.utf8ToString( buffer, escapedSpace );
586    
587                            return result;
588                        }
589                    }
590                }
591            }
592        }
593    
594    
595        /**
596         * Validate this rule : <br>
597         * <p>
598         * &lt;attributeValue&gt; ::= &lt;pairs-or-strings&gt; | '#'
599         *     &lt;hexstring&gt; |'"' &lt;quotechar-or-pairs&gt; '"' <br>
600         * &lt;pairs-or-strings&gt; ::= '\' &lt;pairchar&gt;
601         * &lt;pairs-or-strings&gt; | &lt;stringchar&gt; &lt;pairs-or-strings&gt; | |
602         * e <br>
603         * &lt;quotechar-or-pairs&gt; ::= &lt;quotechar&gt;
604         * &lt;quotechar-or-pairs&gt; | '\' &lt;pairchar&gt;
605         * &lt;quotechar-or-pairs&gt; | e <br>
606         * </p>
607         *
608         * @param bytes The byte array to parse
609         * @param pos The current position in the byte array
610         * @return <code>true</code> if the rule is valid
611         */
612        private static boolean isValidAttributeValue( byte[] bytes, Position pos )
613        {
614            if( bytes.length <= pos.start )
615            {
616                // no attribute value
617                return true;
618            }
619    
620            byte c = bytes[pos.start];
621    
622            if ( c == '#' )
623            {
624                pos.start++;
625                int nbHex = 0;
626                int currentPos = pos.start;
627    
628                // First, we will count the number of hexPairs
629                while ( DNUtils.parseHexPair( bytes, currentPos ) >= 0 )
630                {
631                    nbHex++;
632                    currentPos += DNUtils.TWO_CHARS;
633                }
634    
635                byte[] hexValue = new byte[nbHex];
636    
637                // Now, convert the value
638                // <attributeValue> ::= '#' <hexstring>
639                if ( DNUtils.parseHexString( bytes, hexValue, pos ) == DNUtils.PARSING_ERROR )
640                {
641                    return false;
642                }
643    
644                pos.start--;
645                StringTools.trimRight( bytes, pos );
646                pos.length = pos.end - pos.start;
647    
648                return true;
649            }
650            else if ( c == '"' )
651            {
652                pos.start++;
653                pos.length = 0;
654                pos.end = pos.start;
655                int nbBytes = 0;
656    
657                // <attributeValue> ::= '"' <quotechar-or-pair> '"'
658                // <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\'
659                //                                                  <pairchar> <quotechar-or-pairs> | e
660                while ( true )
661                {
662                    if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
663                    {
664                        pos.end++;
665                        int nbChars = 0;
666    
667                        if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
668                        {
669                            pos.end += nbChars;
670                        }
671                        else
672                        {
673                            return false;
674                        }
675                    }
676                    else if ( ( nbBytes = DNUtils.isQuoteChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
677                    {
678                        pos.end += nbBytes;
679                    }
680                    else
681                    {
682                        pos.length = pos.end - pos.start;
683                        break;
684                    }
685                }
686    
687                if ( StringTools.isCharASCII( bytes, pos.end, '"' ) )
688                {
689                    pos.end++;
690                    return true;
691                }
692                else
693                {
694                    return false;
695                }
696            }
697            else
698            {
699                while ( true )
700                {
701                    if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
702                    {
703                        // '\' <pairchar> <pairs-or-strings>
704                        pos.end++;
705    
706                        int nbChars = 0;
707                        
708                        if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) == DNUtils.PARSING_ERROR )
709                        {
710                            return false;
711                        }
712                        else
713                        {
714                            pos.end += nbChars;
715                        }
716                    }
717                    else
718                    {
719                        int nbChars = 0;
720    
721                        // <stringchar> <pairs-or-strings>
722                        if ( ( nbChars = DNUtils.isStringChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
723                        {
724                            // A special case : if we have some spaces before the
725                            // '+' character,
726                            // we MUST skip them.
727                            if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
728                            {
729                                //StringTools.trimLeft( string, pos );
730    
731                                if ( ( DNUtils.isStringChar( bytes, pos.end ) == DNUtils.PARSING_ERROR )
732                                    && ( !StringTools.isCharASCII( bytes, pos.end, '\\' ) ) )
733                                {
734                                    // Ok, we are done with the stringchar.
735                                    return true;
736                                }
737                                else
738                                {
739                                    pos.end++;
740                                }
741                            }
742                            else
743                            {
744                                pos.end += nbChars;
745                            }
746                        }
747                        else
748                        {
749                            return true;
750                        }
751                    }
752                }
753            }
754        }
755    
756    
757        /**
758         * Parse this rule : <br>
759         * <p>
760         * &lt;nameComponents&gt; ::= &lt;spaces&gt; '+' &lt;spaces&gt;
761         * &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt;
762         * &lt;attributeValue&gt; &lt;nameComponents&gt; | e
763         * </p>
764         *
765         * @param bytes The byte buffer to parse
766         * @param pos The current position in the byte buffer
767         * @param rdn The rdn to generate
768         * @return The new position in the byte buffer, or PARSING_ERROR if the rule
769         *         does not apply to the byte buffer
770         * @throws InvalidNameException If the NameComponent is invalid
771         */
772        private static int parseNameComponents( byte[] bytes, Position pos, Rdn rdn ) throws InvalidNameException
773        {
774            if ( rdn == null )
775            {
776                throw new InvalidNameException( "The RDN should not be null" );
777            }
778            
779            int newStart = 0;
780            String type = null;
781            Object value = null;
782    
783            while ( true )
784            {
785                StringTools.trimLeft( bytes, pos );
786    
787                if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
788                {
789                    pos.start++;
790                    pos.length = 0;
791                }
792                else
793                {
794                    // <attributeTypeAndValues> ::= e
795                    rdn.normalize();
796                    return DNUtils.PARSING_OK;
797                }
798    
799                StringTools.trimLeft( bytes, pos );
800    
801                if ( ( type = parseAttributeType( bytes, pos ) ) == null )
802                {
803                    return DNUtils.PARSING_ERROR;
804                }
805    
806                pos.start = pos.end;
807    
808                StringTools.trimLeft( bytes, pos );
809    
810                if ( StringTools.isCharASCII( bytes, pos.end, '=' ) )
811                {
812                    pos.start++;
813                }
814                else
815                {
816                    return DNUtils.PARSING_ERROR;
817                }
818    
819                StringTools.trimLeft( bytes, pos );
820    
821                value = parseAttributeValue( bytes, pos );
822    
823                newStart = pos.end;
824    
825                if ( value != null )
826                {
827                    if ( rdn != null )
828                    {
829                        if ( value instanceof String )
830                        {
831                            rdn.addAttributeTypeAndValue( type, type, 
832                                new ClientStringValue( (String)value ), 
833                                new ClientStringValue( (String)value ) );
834                        }
835                        else
836                        {
837                            rdn.addAttributeTypeAndValue( type, type, 
838                                new ClientBinaryValue( (byte[])value ), 
839                                new ClientBinaryValue( (byte[])value ) );
840                        }
841                    }
842                }
843    
844                pos.start = newStart;
845                pos.end = newStart;
846            }
847        }
848    
849    
850        /**
851         * Validate this rule : <br>
852         * <p>
853         * &lt;nameComponents&gt; ::= &lt;spaces&gt; '+' &lt;spaces&gt;
854         * &lt;attributeType&gt; &lt;spaces&gt; '=' &lt;spaces&gt;
855         * &lt;attributeValue&gt; &lt;nameComponents&gt; | e
856         * </p>
857         *
858         * @param bytes The byte buffer to parse
859         * @param pos The current position in the byte buffer
860         * @param isFirstRdn A flag set if the RDN is the first one
861         * @return <code>true</code> if the rule is valid
862         */
863        private static boolean isValidNameComponents( byte[] bytes, Position pos, boolean isFirstRdn )
864        {
865            int newStart = 0;
866    
867            while ( true )
868            {
869                StringTools.trimLeft( bytes, pos );
870    
871                if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
872                {
873                    pos.start++;
874                }
875                else
876                {
877                    // <attributeTypeAndValues> ::= e
878                    return true;
879                }
880    
881                StringTools.trimLeft( bytes, pos );
882    
883                if ( !isValidAttributeType( bytes, pos ) )
884                {
885                    return false;
886                }
887    
888                pos.start = pos.end;
889    
890                StringTools.trimLeft( bytes, pos );
891    
892                if ( StringTools.isCharASCII( bytes, pos.end, '=' ) )
893                {
894                    pos.start++;
895                }
896                else
897                {
898                    return false;
899                }
900    
901                StringTools.trimLeft( bytes, pos );
902    
903                if ( !isValidAttributeValue( bytes, pos ) )
904                {
905                    return false;
906                }
907    
908                newStart = pos.end;
909                pos.start = newStart;
910                pos.end = newStart;
911            }
912        }
913    
914    
915        /**
916         * Parse a NameComponent : <br>
917         * <p>
918         * &lt;name-component&gt; ::= &lt;attributeType&gt; &lt;spaces&gt; '='
919         * &lt;spaces&gt; &lt;attributeValue&gt; &lt;nameComponents&gt;
920         * </p>
921         *
922         * @param dn The String to parse
923         * @param pos The current position in the buffer
924         * @param rdn The constructed RDN
925         * @return The new position in the char array, or PARSING_ERROR if the rule
926         *         does not apply to the char array
927         * @throws InvalidNameException If the NameComponent is invalid
928         */
929        public static int parse( String dn, Position pos, Rdn rdn ) throws InvalidNameException
930        {
931            return parse( StringTools.getBytesUtf8( dn ), pos, rdn );
932        }
933    
934    
935        /**
936         * Parse a NameComponent : <br>
937         * <p>
938         * &lt;name-component&gt; ::= &lt;attributeType&gt; &lt;spaces&gt; '='
939         * &lt;spaces&gt; &lt;attributeValue&gt; &lt;nameComponents&gt;
940         * </p>
941         *
942         * @param dn The byte array to parse
943         * @param pos The current position in the buffer
944         * @param rdn The constructed RDN
945         * @return The new position in the byte array, or PARSING_ERROR if the rule
946         *         does not apply to the byte array
947         * @throws InvalidNameException If the NameComponent is invalid
948         */
949        public static int parse( byte[] dn, Position pos, Rdn rdn ) throws InvalidNameException
950        {
951            if ( rdn == null )
952            {
953                throw new InvalidNameException( "Cannot feed a null RDN structure" );
954            }
955            
956            String type = null;
957            Object value = null;
958            int start = pos.start;
959    
960            StringTools.trimLeft( dn, pos );
961    
962            pos.end = pos.start;
963            pos.length = 0;
964            
965            if ( ( type = parseAttributeType( dn, pos ) ) == null )
966            {
967                return DNUtils.PARSING_ERROR;
968            }
969    
970            pos.start = pos.end;
971    
972            StringTools.trimLeft( dn, pos );
973    
974            if ( !StringTools.isCharASCII( dn, pos.start, '=' ) )
975            {
976                return DNUtils.PARSING_ERROR;
977            }
978            else
979            {
980                pos.start++;
981            }
982    
983            StringTools.trimLeft( dn, pos );
984    
985            pos.end = pos.start;
986            
987            int start2 = pos.start;
988    
989            if ( ( value = parseAttributeValue( dn, pos ) ) == null )
990            {
991                return DNUtils.PARSING_ERROR;
992            }
993    
994            String upValue = StringTools.utf8ToString( dn, start2, pos.length );
995            
996            if ( value instanceof String )
997            {
998                rdn.addAttributeTypeAndValue( type, type, 
999                    new ClientStringValue( upValue ), 
1000                    new ClientStringValue( (String)value ) );
1001            }
1002            else
1003            {
1004                rdn.addAttributeTypeAndValue( type, type, 
1005                    new ClientStringValue( upValue ), 
1006                    new ClientBinaryValue( (byte[])value ) );
1007            }
1008            
1009            rdn.normalize();
1010    
1011            pos.start = pos.end;
1012            pos.length = 0;
1013    
1014            if ( parseNameComponents( dn, pos, rdn ) == DNUtils.PARSING_ERROR )
1015            {
1016                return DNUtils.PARSING_ERROR;
1017            }
1018            
1019            rdn.setUpName( StringTools.utf8ToString( dn, start, pos.end - start ) );
1020            pos.start = pos.end;
1021            return DNUtils.PARSING_OK;
1022        }
1023    
1024    
1025        /**
1026         * Validate a NameComponent : <br>
1027         * <p>
1028         * &lt;name-component&gt; ::= &lt;attributeType&gt; &lt;spaces&gt; '='
1029         * &lt;spaces&gt; &lt;attributeValue&gt; &lt;nameComponents&gt;
1030         * </p>
1031         *
1032         * @param dn The byte array to parse
1033         * @param pos The current position in the buffer
1034         * @param isFirstRdn a flag set if the RDN is the first for the current DN
1035         * @return <code>true</code> if the RDN is valid
1036         */
1037        public static boolean isValid( byte[] dn, Position pos, boolean isFirstRdn )
1038        {
1039            StringTools.trimLeft( dn, pos );
1040    
1041            pos.end = pos.start;
1042            pos.length = 0;
1043            
1044            if ( !isValidAttributeType( dn, pos ) )
1045            {
1046                return false;
1047            }
1048    
1049            pos.start = pos.end;
1050    
1051            StringTools.trimLeft( dn, pos );
1052    
1053            if ( !StringTools.isCharASCII( dn, pos.start, '=' ) )
1054            {
1055                return false;
1056            }
1057            else
1058            {
1059                pos.start++;
1060            }
1061    
1062            StringTools.trimLeft( dn, pos );
1063    
1064            pos.end = pos.start;
1065    
1066            if ( !isValidAttributeValue( dn, pos ) )
1067            {
1068                return false;
1069            }
1070    
1071            pos.start = pos.end;
1072            pos.length = 0;
1073    
1074            if ( !isValidNameComponents( dn, pos, isFirstRdn )  )
1075            {
1076                return false;
1077            }
1078            
1079            pos.start = pos.end;
1080            return true;
1081        }
1082    
1083    
1084        /**
1085         * Parse a NameComponent : <br>
1086         * <p>
1087         * &lt;name-component&gt; ::= &lt;attributeType&gt; &lt;spaces&gt; '='
1088         * &lt;spaces&gt; &lt;attributeValue&gt; &lt;nameComponents&gt;
1089         * </p>
1090         *
1091         * @param dn The String to parse
1092         * @param rdn The RDN to fill. Beware that if the RDN is not empty, the new
1093         *            AttributeTypeAndValue will be added.
1094         * @throws InvalidNameException If the NameComponent is invalid
1095         */
1096        public static void parse( String dn, Rdn rdn ) throws InvalidNameException
1097        {
1098            if ( parse( StringTools.getBytesUtf8( dn ), new Position(), rdn ) == DNUtils.PARSING_ERROR )
1099            {
1100                throw new InvalidNameException( "The RDN can't be parsed." );
1101            }
1102        }
1103    
1104        /**
1105         * Validate a NameComponent : <br>
1106         * <p>
1107         * &lt;name-component&gt; ::= &lt;attributeType&gt; &lt;spaces&gt; '='
1108         * &lt;spaces&gt; &lt;attributeValue&gt; &lt;nameComponents&gt;
1109         * </p>
1110         *
1111         * @param dn The string to parse
1112         * @return <code>true</code> if the RDN is valid
1113         */
1114        public static boolean isValid( String dn )
1115        {
1116            return isValid( StringTools.getBytesUtf8( dn ), new Position(), false );
1117        }
1118    }