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.codec;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    import java.util.ArrayList;
026    import java.util.List;
027    
028    import org.apache.directory.shared.asn1.AbstractAsn1Object;
029    import org.apache.directory.shared.asn1.ber.tlv.TLV;
030    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
031    import org.apache.directory.shared.asn1.ber.tlv.Value;
032    import org.apache.directory.shared.asn1.codec.EncoderException;
033    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
034    import org.apache.directory.shared.ldap.name.LdapDN;
035    import org.apache.directory.shared.ldap.util.LdapURL;
036    import org.apache.directory.shared.ldap.util.StringTools;
037    
038    
039    /**
040     * A ldapObject to store the LdapResult
041     * 
042     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
043     * @version $Rev: 764131 $, $Date: 2009-04-11 03:03:00 +0200 (Sam, 11 avr 2009) $, 
044     */
045    public class LdapResultCodec extends AbstractAsn1Object
046    {
047        // ~ Instance fields
048        // ----------------------------------------------------------------------------
049    
050        /**
051         * The result code. The different values are : 
052         * 
053         * success                                  (0), 
054         * operationsError                          (1), 
055         * protocolError                            (2), 
056         * timeLimitExceeded                        (3), 
057         * sizeLimitExceeded                        (4),
058         * compareFalse                             (5), 
059         * compareTrue                              (6), 
060         * authMethodNotSupported                   (7),
061         * strongAuthRequired                       (8), 
062         *                                          -- 9 reserved -- 
063         * referral                                 (10), 
064         * adminLimitExceeded                       (11), 
065         * unavailableCriticalExtension             (12), 
066         * confidentialityRequired                  (13), 
067         * saslBindInProgress                       (14),
068         * noSuchAttribute                          (16), 
069         * undefinedAttributeType                   (17), 
070         * inappropriateMatching                    (18), 
071         * constraintViolation                      (19), 
072         * attributeOrValueExists                   (20),
073         * invalidAttributeSyntax                   (21), 
074         *                                          -- 22-31 unused -- 
075         * noSuchObject                             (32),
076         * aliasProblem                             (33), 
077         * invalidDNSyntax                          (34), 
078         *                                          -- 35 reserved for undefined isLeaf -- 
079         * aliasDereferencingProblem                (36), 
080         *                                          -- 37-47 unused --
081         * inappropriateAuthentication              (48), 
082         * invalidCredentials                       (49),
083         * insufficientAccessRights                 (50), 
084         * busy                                     (51), 
085         * unavailable                              (52),
086         * unwillingToPerform                       (53), 
087         * loopDetect                               (54), 
088         *                                          -- 55-63 unused --
089         * namingViolation                          (64), 
090         * objectClassViolation                     (65), 
091         * notAllowedOnNonLeaf                      (66), 
092         * notAllowedOnRDN                          (67), 
093         * entryAlreadyExists                       (68),
094         * objectClassModsProhibited                (69), 
095         *                                          -- 70 reserved for CLDAP --
096         * affectsMultipleDSAs                      (71), -- new 
097         *                                          -- 72-79 unused -- 
098         * other                                    (80),
099         * ...
100         * }
101         */
102        private ResultCodeEnum resultCode;
103    
104        /** The DN that is matched by the Bind */
105        private LdapDN matchedDN;
106    
107        /** Temporary storage of the byte[] representing the matchedDN */
108        private byte[] matchedDNBytes;
109    
110        /** The error message */
111        private String errorMessage;
112        
113        /** Temporary storage for message bytes */
114        private byte[] errorMessageBytes;
115    
116        /** The referrals, if any. This is an optional element */
117        private List<LdapURL> referrals;
118    
119        /** The inner size of the referrals sequence */
120        private int referralsLength;
121    
122    
123        // ~ Constructors
124        // -------------------------------------------------------------------------------
125    
126        /**
127         * Creates a new LdapResult object.
128         */
129        public LdapResultCodec()
130        {
131            super();
132        }
133    
134    
135        // ~ Methods
136        // ------------------------------------------------------------------------------------
137    
138        /**
139         * Initialize the referrals list
140         */
141        public void initReferrals()
142        {
143            referrals = new ArrayList<LdapURL>();
144        }
145        
146        /**
147         * Get the error message
148         * 
149         * @return Returns the errorMessage.
150         */
151        public String getErrorMessage()
152        {
153            return errorMessage;
154        }
155    
156    
157        /**
158         * Set the error message
159         * 
160         * @param errorMessage The errorMessage to set.
161         */
162        public void setErrorMessage( String errorMessage )
163        {
164            this.errorMessage = errorMessage;
165        }
166    
167    
168        /**
169         * Get the matched DN
170         * 
171         * @return Returns the matchedDN.
172         */
173        public String getMatchedDN()
174        {
175            return ( ( matchedDN == null ) ? "" : matchedDN.toString() );
176        }
177    
178    
179        /**
180         * Set the Matched DN
181         * 
182         * @param matchedDN The matchedDN to set.
183         */
184        public void setMatchedDN( LdapDN matchedDN )
185        {
186            this.matchedDN = matchedDN;
187        }
188    
189    
190        /**
191         * Get the referrals
192         * 
193         * @return Returns the referrals.
194         */
195        public List<LdapURL> getReferrals()
196        {
197            return referrals;
198        }
199    
200    
201        /**
202         * Add a referral
203         * 
204         * @param referral The referral to add.
205         */
206        public void addReferral( LdapURL referral )
207        {
208            referrals.add( referral );
209        }
210    
211    
212        /**
213         * Get the result code
214         * 
215         * @return Returns the resultCode.
216         */
217        public ResultCodeEnum getResultCode()
218        {
219            return resultCode;
220        }
221    
222    
223        /**
224         * Set the result code
225         * 
226         * @param resultCode The resultCode to set.
227         */
228        public void setResultCode( ResultCodeEnum resultCode )
229        {
230            this.resultCode = resultCode;
231        }
232    
233    
234        /**
235         * Compute the LdapResult length 
236         * 
237         * LdapResult : 
238         * 0x0A 01 resultCode (0..80)
239         *   0x04 L1 matchedDN (L1 = Length(matchedDN)) 
240         *   0x04 L2 errorMessage (L2 = Length(errorMessage)) 
241         *   [0x83 L3] referrals 
242         *     | 
243         *     +--> 0x04 L4 referral 
244         *     +--> 0x04 L5 referral 
245         *     +--> ... 
246         *     +--> 0x04 Li referral 
247         *     +--> ... 
248         *     +--> 0x04 Ln referral 
249         *     
250         * L1 = Length(matchedDN) 
251         * L2 = Length(errorMessage) 
252         * L3 = n*Length(0x04) + sum(Length(L4) .. Length(Ln)) + sum(L4..Ln) 
253         * L4..n = Length(0x04) + Length(Li) + Li 
254         * Length(LdapResult) = Length(0x0x0A) +
255         *      Length(0x01) + 1 + Length(0x04) + Length(L1) + L1 + Length(0x04) +
256         *      Length(L2) + L2 + Length(0x83) + Length(L3) + L3
257         */
258        public int computeLength()
259        {
260            int ldapResultLength = 0;
261    
262            // The result code : always 3 bytes
263            ldapResultLength = 1 + 1 + 1;
264    
265            // The matchedDN length
266            if ( matchedDN == null )
267            {
268                ldapResultLength += 1 + 1;
269            }
270            else
271            {
272                matchedDNBytes = StringTools.getBytesUtf8( StringTools.trimLeft( matchedDN.getUpName() ) );
273                ldapResultLength += 1 + TLV.getNbBytes( matchedDNBytes.length ) + matchedDNBytes.length;
274            }
275    
276            // The errorMessage length
277            errorMessageBytes = StringTools.getBytesUtf8( errorMessage ); 
278            ldapResultLength += 1 + TLV.getNbBytes( errorMessageBytes.length ) + errorMessageBytes.length;
279    
280            if ( ( referrals != null ) && ( referrals.size() != 0 ) )
281            {
282                referralsLength = 0;
283    
284                // Each referral
285                for ( LdapURL referral:referrals )
286                {
287                    referralsLength += 1 + TLV.getNbBytes( referral.getNbBytes() ) + referral.getNbBytes();
288                }
289    
290                // The referrals
291                ldapResultLength += 1 + TLV.getNbBytes( referralsLength ) + referralsLength;
292            }
293    
294            return ldapResultLength;
295        }
296    
297    
298        /**
299         * Encode the LdapResult message to a PDU.
300         * 
301         * @param buffer The buffer where to put the PDU
302         * @return The PDU.
303         */
304        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
305        {
306            if ( buffer == null )
307            {
308                throw new EncoderException( "Cannot put a PDU in a null buffer !" );
309            }
310    
311            try
312            {
313                // The result code
314                buffer.put( UniversalTag.ENUMERATED_TAG );
315                buffer.put( ( byte ) 1 );
316                buffer.put( ( byte ) resultCode.getValue() );
317            }
318            catch ( BufferOverflowException boe )
319            {
320                throw new EncoderException( "The PDU buffer size is too small !" );
321            }
322    
323            // The matchedDN
324            Value.encode( buffer, matchedDNBytes );
325    
326            // The error message
327            Value.encode( buffer, errorMessageBytes );
328    
329            // The referrals, if any
330            if ( ( referrals != null ) && ( referrals.size() != 0 ) )
331            {
332                // Encode the referrals sequence
333                // The referrals length MUST have been computed before !
334                buffer.put( ( byte ) LdapConstants.LDAP_RESULT_REFERRAL_SEQUENCE_TAG );
335                buffer.put( TLV.getBytes( referralsLength ) );
336    
337                // Each referral
338                for ( LdapURL referral:referrals )
339                {
340                    // Encode the current referral
341                    Value.encode( buffer, referral.getBytesReference() );
342                }
343            }
344    
345            return buffer;
346        }
347    
348    
349        /**
350         * Get a String representation of a LdapResult
351         * 
352         * @return A LdapResult String
353         */
354        public String toString()
355        {
356    
357            StringBuffer sb = new StringBuffer();
358    
359            sb.append( "        Ldap Result\n" );
360            sb.append( "            Result code : (" ).append( resultCode ).append( ')' );
361    
362            switch ( resultCode )
363            {
364    
365                case SUCCESS:
366                    sb.append( " success\n" );
367                    break;
368    
369                case OPERATIONS_ERROR:
370                    sb.append( " operationsError\n" );
371                    break;
372    
373                case PROTOCOL_ERROR:
374                    sb.append( " protocolError\n" );
375                    break;
376    
377                case TIME_LIMIT_EXCEEDED:
378                    sb.append( " timeLimitExceeded\n" );
379                    break;
380    
381                case SIZE_LIMIT_EXCEEDED:
382                    sb.append( " sizeLimitExceeded\n" );
383                    break;
384    
385                case COMPARE_FALSE:
386                    sb.append( " compareFalse\n" );
387                    break;
388    
389                case COMPARE_TRUE:
390                    sb.append( " compareTrue\n" );
391                    break;
392    
393                case AUTH_METHOD_NOT_SUPPORTED:
394                    sb.append( " authMethodNotSupported\n" );
395                    break;
396    
397                case STRONG_AUTH_REQUIRED:
398                    sb.append( " strongAuthRequired\n" );
399                    break;
400    
401                case REFERRAL:
402                    sb.append( " referral -- new\n" );
403                    break;
404    
405                case ADMIN_LIMIT_EXCEEDED:
406                    sb.append( " adminLimitExceeded -- new\n" );
407                    break;
408    
409                case UNAVAILABLE_CRITICAL_EXTENSION:
410                    sb.append( " unavailableCriticalExtension -- new\n" );
411                    break;
412    
413                case CONFIDENTIALITY_REQUIRED:
414                    sb.append( " confidentialityRequired -- new\n" );
415                    break;
416    
417                case SASL_BIND_IN_PROGRESS:
418                    sb.append( " saslBindInProgress -- new\n" );
419                    break;
420    
421                case NO_SUCH_ATTRIBUTE:
422                    sb.append( " noSuchAttribute\n" );
423                    break;
424    
425                case UNDEFINED_ATTRIBUTE_TYPE:
426                    sb.append( " undefinedAttributeType\n" );
427                    break;
428    
429                case INAPPROPRIATE_MATCHING:
430                    sb.append( " inappropriateMatching\n" );
431                    break;
432    
433                case CONSTRAINT_VIOLATION:
434                    sb.append( " constraintViolation\n" );
435                    break;
436    
437                case ATTRIBUTE_OR_VALUE_EXISTS:
438                    sb.append( " attributeOrValueExists\n" );
439                    break;
440    
441                case INVALID_ATTRIBUTE_SYNTAX:
442                    sb.append( " invalidAttributeSyntax\n" );
443                    break;
444    
445                case NO_SUCH_OBJECT:
446                    sb.append( " noSuchObject\n" );
447                    break;
448    
449                case ALIAS_PROBLEM:
450                    sb.append( " aliasProblem\n" );
451                    break;
452    
453                case INVALID_DN_SYNTAX:
454                    sb.append( " invalidDNSyntax\n" );
455                    break;
456    
457                case ALIAS_DEREFERENCING_PROBLEM:
458                    sb.append( " aliasDereferencingProblem\n" );
459                    break;
460    
461                case INAPPROPRIATE_AUTHENTICATION:
462                    sb.append( " inappropriateAuthentication\n" );
463                    break;
464    
465                case INVALID_CREDENTIALS:
466                    sb.append( " invalidCredentials\n" );
467                    break;
468    
469                case INSUFFICIENT_ACCESS_RIGHTS:
470                    sb.append( " insufficientAccessRights\n" );
471                    break;
472    
473                case BUSY:
474                    sb.append( " busy\n" );
475                    break;
476    
477                case UNAVAILABLE:
478                    sb.append( " unavailable\n" );
479                    break;
480    
481                case UNWILLING_TO_PERFORM:
482                    sb.append( " unwillingToPerform\n" );
483                    break;
484    
485                case LOOP_DETECT:
486                    sb.append( " loopDetect\n" );
487                    break;
488    
489                case NAMING_VIOLATION:
490                    sb.append( " namingViolation\n" );
491                    break;
492    
493                case OBJECT_CLASS_VIOLATION:
494                    sb.append( " objectClassViolation\n" );
495                    break;
496    
497                case NOT_ALLOWED_ON_NON_LEAF:
498                    sb.append( " notAllowedOnNonLeaf\n" );
499                    break;
500    
501                case NOT_ALLOWED_ON_RDN:
502                    sb.append( " notAllowedOnRDN\n" );
503                    break;
504    
505                case ENTRY_ALREADY_EXISTS:
506                    sb.append( " entryAlreadyExists\n" );
507                    break;
508    
509                case OBJECT_CLASS_MODS_PROHIBITED:
510                    sb.append( " objectClassModsProhibited\n" );
511                    break;
512    
513                case AFFECTS_MULTIPLE_DSAS:
514                    sb.append( " affectsMultipleDSAs -- new\n" );
515                    break;
516    
517                case OTHER:
518                    sb.append( " other\n" );
519                    break;
520    
521                default:
522                    switch ( resultCode.getResultCode() )
523                    {
524                        case 9:
525                            sb.append( " -- 9 reserved --\n" );
526                            break;
527        
528                        case 22:
529                        case 23:
530                        case 24:
531                        case 25:
532                        case 26:
533                        case 27:
534                        case 28:
535                        case 29:
536                        case 30:
537                        case 31:
538                            sb.append( " -- 22-31 unused --\n" );
539                            break;
540                            
541                        case 35 :
542                            sb.append( " -- 35 reserved for undefined isLeaf --\n" );
543                            break;
544                            
545                        case 37:
546                        case 38:
547                        case 39:
548                        case 40:
549                        case 41:
550                        case 42:
551                        case 43:
552                        case 44:
553                        case 45:
554                        case 46:
555                        case 47:
556                            sb.append( " -- 37-47 unused --\n" );
557                            break;
558        
559                        case 55:
560                        case 56:
561                        case 57:
562                        case 58:
563                        case 59:
564                        case 60:
565                        case 61:
566                        case 62:
567                        case 63:
568                            sb.append( " -- 55-63 unused --\n" );
569                            break;
570        
571                        case 70:
572                            sb.append( " -- 70 reserved for CLDAP --\n" );
573                            break;
574                            
575                        case 72:
576                        case 73:
577                        case 74:
578                        case 75:
579                        case 76:
580                        case 77:
581                        case 78:
582                        case 79:
583                            sb.append( " -- 72-79 unused --\n" );
584                            break;
585        
586                        case 81:
587                        case 82:
588                        case 83:
589                        case 84:
590                        case 85:
591                        case 86:
592                        case 87:
593                        case 88:
594                        case 89:
595                        case 90:
596                            sb.append( " -- 81-90 reserved for APIs --" );
597                            break;
598                            
599                        default :
600                            sb.append( "Unknown error code : " ).append( resultCode );
601                            break;
602                    }
603                
604                break;
605            }
606    
607            sb.append( "            Matched DN : '" ).append( matchedDN == null ? "": matchedDN.toString() ).append( "'\n" );
608            sb.append( "            Error message : '" ).append( errorMessage == null ? "" : errorMessage ).append( "'\n" );
609    
610            
611            if ( ( referrals != null ) && ( referrals.size() != 0 ) )
612            {
613                sb.append( "            Referrals :\n" );
614                int i = 0;
615    
616                for ( LdapURL referral:referrals )
617                {
618    
619                    sb.append( "                Referral[" ).
620                        append( i++ ).
621                        append( "] :" ).
622                        append( referral ).
623                        append( '\n' );
624                }
625            }
626    
627            return sb.toString();
628        }
629    }