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.i18n.I18n;
034    import org.apache.directory.shared.ldap.message.ResultCodeEnum;
035    import org.apache.directory.shared.ldap.name.DN;
036    import org.apache.directory.shared.ldap.util.LdapURL;
037    import org.apache.directory.shared.ldap.util.StringTools;
038    
039    
040    /**
041     * A ldapObject to store the LdapResult
042     * 
043     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044     * @version $Rev: 921600 $, $Date: 2010-03-10 23:37:30 +0100 (Wed, 10 Mar 2010) $, 
045     */
046    public class LdapResultCodec extends AbstractAsn1Object
047    {
048        // ~ Instance fields
049        // ----------------------------------------------------------------------------
050    
051        /**
052         * The result code. The different values are : 
053         * 
054         * success                                  (0), 
055         * operationsError                          (1), 
056         * protocolError                            (2), 
057         * timeLimitExceeded                        (3), 
058         * sizeLimitExceeded                        (4),
059         * compareFalse                             (5), 
060         * compareTrue                              (6), 
061         * authMethodNotSupported                   (7),
062         * strongAuthRequired                       (8), 
063         *                                          -- 9 reserved -- 
064         * referral                                 (10), 
065         * adminLimitExceeded                       (11), 
066         * unavailableCriticalExtension             (12), 
067         * confidentialityRequired                  (13), 
068         * saslBindInProgress                       (14),
069         * noSuchAttribute                          (16), 
070         * undefinedAttributeType                   (17), 
071         * inappropriateMatching                    (18), 
072         * constraintViolation                      (19), 
073         * attributeOrValueExists                   (20),
074         * invalidAttributeSyntax                   (21), 
075         *                                          -- 22-31 unused -- 
076         * noSuchObject                             (32),
077         * aliasProblem                             (33), 
078         * invalidDNSyntax                          (34), 
079         *                                          -- 35 reserved for undefined isLeaf -- 
080         * aliasDereferencingProblem                (36), 
081         *                                          -- 37-47 unused --
082         * inappropriateAuthentication              (48), 
083         * invalidCredentials                       (49),
084         * insufficientAccessRights                 (50), 
085         * busy                                     (51), 
086         * unavailable                              (52),
087         * unwillingToPerform                       (53), 
088         * loopDetect                               (54), 
089         *                                          -- 55-63 unused --
090         * namingViolation                          (64), 
091         * objectClassViolation                     (65), 
092         * notAllowedOnNonLeaf                      (66), 
093         * notAllowedOnRDN                          (67), 
094         * entryAlreadyExists                       (68),
095         * objectClassModsProhibited                (69), 
096         *                                          -- 70 reserved for CLDAP --
097         * affectsMultipleDSAs                      (71), -- new 
098         *                                          -- 72-79 unused -- 
099         * other                                    (80),
100         * ...
101         * }
102         */
103        private ResultCodeEnum resultCode;
104    
105        /** The DN that is matched by the Bind */
106        private DN matchedDN;
107    
108        /** Temporary storage of the byte[] representing the matchedDN */
109        private byte[] matchedDNBytes;
110    
111        /** The error message */
112        private String errorMessage;
113        
114        /** Temporary storage for message bytes */
115        private byte[] errorMessageBytes;
116    
117        /** The referrals, if any. This is an optional element */
118        private List<LdapURL> referrals;
119    
120        /** The inner size of the referrals sequence */
121        private int referralsLength;
122    
123    
124        // ~ Constructors
125        // -------------------------------------------------------------------------------
126    
127        /**
128         * Creates a new LdapResult object.
129         */
130        public LdapResultCodec()
131        {
132            super();
133        }
134    
135    
136        // ~ Methods
137        // ------------------------------------------------------------------------------------
138    
139        /**
140         * Initialize the referrals list
141         */
142        public void initReferrals()
143        {
144            referrals = new ArrayList<LdapURL>();
145        }
146        
147        /**
148         * Get the error message
149         * 
150         * @return Returns the errorMessage.
151         */
152        public String getErrorMessage()
153        {
154            return errorMessage;
155        }
156    
157    
158        /**
159         * Set the error message
160         * 
161         * @param errorMessage The errorMessage to set.
162         */
163        public void setErrorMessage( String errorMessage )
164        {
165            this.errorMessage = errorMessage;
166        }
167    
168    
169        /**
170         * Get the matched DN
171         * 
172         * @return Returns the matchedDN.
173         */
174        public String getMatchedDN()
175        {
176            return ( ( matchedDN == null ) ? "" : matchedDN.getNormName() );
177        }
178    
179    
180        /**
181         * Set the Matched DN
182         * 
183         * @param matchedDN The matchedDN to set.
184         */
185        public void setMatchedDN( DN matchedDN )
186        {
187            this.matchedDN = matchedDN;
188        }
189    
190    
191        /**
192         * Get the referrals
193         * 
194         * @return Returns the referrals.
195         */
196        public List<LdapURL> getReferrals()
197        {
198            return referrals;
199        }
200    
201    
202        /**
203         * Add a referral
204         * 
205         * @param referral The referral to add.
206         */
207        public void addReferral( LdapURL referral )
208        {
209            referrals.add( referral );
210        }
211    
212    
213        /**
214         * Get the result code
215         * 
216         * @return Returns the resultCode.
217         */
218        public ResultCodeEnum getResultCode()
219        {
220            return resultCode;
221        }
222    
223    
224        /**
225         * Set the result code
226         * 
227         * @param resultCode The resultCode to set.
228         */
229        public void setResultCode( ResultCodeEnum resultCode )
230        {
231            this.resultCode = resultCode;
232        }
233    
234    
235        /**
236         * Compute the LdapResult length 
237         * 
238         * LdapResult : 
239         * 0x0A 01 resultCode (0..80)
240         *   0x04 L1 matchedDN (L1 = Length(matchedDN)) 
241         *   0x04 L2 errorMessage (L2 = Length(errorMessage)) 
242         *   [0x83 L3] referrals 
243         *     | 
244         *     +--> 0x04 L4 referral 
245         *     +--> 0x04 L5 referral 
246         *     +--> ... 
247         *     +--> 0x04 Li referral 
248         *     +--> ... 
249         *     +--> 0x04 Ln referral 
250         *     
251         * L1 = Length(matchedDN) 
252         * L2 = Length(errorMessage) 
253         * L3 = n*Length(0x04) + sum(Length(L4) .. Length(Ln)) + sum(L4..Ln) 
254         * L4..n = Length(0x04) + Length(Li) + Li 
255         * Length(LdapResult) = Length(0x0x0A) +
256         *      Length(0x01) + 1 + Length(0x04) + Length(L1) + L1 + Length(0x04) +
257         *      Length(L2) + L2 + Length(0x83) + Length(L3) + L3
258         */
259        public int computeLength()
260        {
261            int ldapResultLength = 0;
262    
263            // The result code : always 3 bytes
264            ldapResultLength = 1 + 1 + 1;
265    
266            // The matchedDN length
267            if ( matchedDN == null )
268            {
269                ldapResultLength += 1 + 1;
270            }
271            else
272            {
273                matchedDNBytes = StringTools.getBytesUtf8( StringTools.trimLeft( matchedDN.getName() ) );
274                ldapResultLength += 1 + TLV.getNbBytes( matchedDNBytes.length ) + matchedDNBytes.length;
275            }
276    
277            // The errorMessage length
278            errorMessageBytes = StringTools.getBytesUtf8( errorMessage ); 
279            ldapResultLength += 1 + TLV.getNbBytes( errorMessageBytes.length ) + errorMessageBytes.length;
280    
281            if ( ( referrals != null ) && ( referrals.size() != 0 ) )
282            {
283                referralsLength = 0;
284    
285                // Each referral
286                for ( LdapURL referral:referrals )
287                {
288                    referralsLength += 1 + TLV.getNbBytes( referral.getNbBytes() ) + referral.getNbBytes();
289                }
290    
291                // The referrals
292                ldapResultLength += 1 + TLV.getNbBytes( referralsLength ) + referralsLength;
293            }
294    
295            return ldapResultLength;
296        }
297    
298    
299        /**
300         * Encode the LdapResult message to a PDU.
301         * 
302         * @param buffer The buffer where to put the PDU
303         * @return The PDU.
304         */
305        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
306        {
307            if ( buffer == null )
308            {
309                throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
310            }
311    
312            try
313            {
314                // The result code
315                buffer.put( UniversalTag.ENUMERATED_TAG );
316                buffer.put( ( byte ) 1 );
317                buffer.put( ( byte ) resultCode.getValue() );
318            }
319            catch ( BufferOverflowException boe )
320            {
321                throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
322            }
323    
324            // The matchedDN
325            Value.encode( buffer, matchedDNBytes );
326    
327            // The error message
328            Value.encode( buffer, errorMessageBytes );
329    
330            // The referrals, if any
331            if ( ( referrals != null ) && ( referrals.size() != 0 ) )
332            {
333                // Encode the referrals sequence
334                // The referrals length MUST have been computed before !
335                buffer.put( ( byte ) LdapConstants.LDAP_RESULT_REFERRAL_SEQUENCE_TAG );
336                buffer.put( TLV.getBytes( referralsLength ) );
337    
338                // Each referral
339                for ( LdapURL referral:referrals )
340                {
341                    // Encode the current referral
342                    Value.encode( buffer, referral.getBytesReference() );
343                }
344            }
345    
346            return buffer;
347        }
348    
349    
350        /**
351         * Get a String representation of a LdapResult
352         * 
353         * @return A LdapResult String
354         */
355        public String toString()
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    }