001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.controls;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.util.ArrayList;
033    
034    import org.opends.server.protocols.asn1.ASN1Boolean;
035    import org.opends.server.protocols.asn1.ASN1Element;
036    import org.opends.server.protocols.asn1.ASN1Exception;
037    import org.opends.server.protocols.asn1.ASN1Integer;
038    import org.opends.server.protocols.asn1.ASN1OctetString;
039    import org.opends.server.protocols.asn1.ASN1Sequence;
040    import org.opends.server.protocols.ldap.LDAPResultCode;
041    import org.opends.server.types.Control;
042    import org.opends.server.types.DebugLogLevel;
043    import org.opends.server.types.LDAPException;
044    
045    import static org.opends.server.loggers.debug.DebugLogger.*;
046    import org.opends.server.loggers.debug.DebugTracer;
047    import static org.opends.messages.ProtocolMessages.*;
048    import static org.opends.server.util.ServerConstants.*;
049    import static org.opends.server.util.StaticUtils.*;
050    
051    
052    
053    /**
054     * This class implements the account usable response control.  This is a
055     * Sun-defined control with OID 1.3.6.1.4.1.42.2.27.9.5.8.  The value of this
056     * control is composed according to the following BNF:
057     * <BR>
058     * <PRE>
059     * ACCOUNT_USABLE_RESPONSE ::= CHOICE {
060     *      is_available           [0] INTEGER, -- Seconds before expiration --
061     *      is_not_available       [1] MORE_INFO }
062     *
063     * MORE_INFO ::= SEQUENCE {
064     *      inactive               [0] BOOLEAN DEFAULT FALSE,
065     *      reset                  [1] BOOLEAN DEFAULT FALSE,
066     *      expired                [2] BOOLEAN DEFAULT_FALSE,
067     *      remaining_grace        [3] INTEGER OPTIONAL,
068     *      seconds_before_unlock  [4] INTEGER OPTIONAL }
069     * </PRE>
070     */
071    public class AccountUsableResponseControl
072           extends Control
073    {
074      /**
075       * The tracer object for the debug logger.
076       */
077      private static final DebugTracer TRACER = getTracer();
078    
079    
080    
081    
082      /**
083       * The BER type to use for the seconds before expiration when the account is
084       * available.
085       */
086      public static final byte TYPE_SECONDS_BEFORE_EXPIRATION = (byte) 0x80;
087    
088    
089    
090      /**
091       * The BER type to use for the MORE_INFO sequence when the account is not
092       * available.
093       */
094      public static final byte TYPE_MORE_INFO = (byte) 0xA1;
095    
096    
097    
098      /**
099       * The BER type to use for the MORE_INFO element that indicates that the
100       * account has been inactivated.
101       */
102      public static final byte TYPE_INACTIVE = (byte) 0x80;
103    
104    
105    
106      /**
107       * The BER type to use for the MORE_INFO element that indicates that the
108       * password has been administratively reset.
109       */
110      public static final byte TYPE_RESET = (byte) 0x81;
111    
112    
113    
114      /**
115       * The BER type to use for the MORE_INFO element that indicates that the
116       * user's password is expired.
117       */
118      public static final byte TYPE_EXPIRED = (byte) 0x82;
119    
120    
121    
122      /**
123       * The BER type to use for the MORE_INFO element that provides the number of
124       * remaining grace logins.
125       */
126      public static final byte TYPE_REMAINING_GRACE_LOGINS = (byte) 0x83;
127    
128    
129    
130      /**
131       * The BER type to use for the MORE_INFO element that indicates that the
132       * password has been administratively reset.
133       */
134      public static final byte TYPE_SECONDS_BEFORE_UNLOCK = (byte) 0x84;
135    
136    
137    
138      // Indicates whether the user's account is usable.
139      private boolean isUsable;
140    
141      // Indicates whether the user's password is expired.
142      private boolean isExpired;
143    
144      // Indicates whether the user's account is inactive.
145      private boolean isInactive;
146    
147      // Indicates whether the user's account is currently locked.
148      private boolean isLocked;
149    
150      // Indicates whether the user's password has been reset and must be changed
151      // before anything else can be done.
152      private boolean isReset;
153    
154      // The number of remaining grace logins, if available.
155      private int remainingGraceLogins;
156    
157      // The length of time in seconds before the user's password expires, if
158      // available.
159      private int secondsBeforeExpiration;
160    
161      // The length of time before the user's account is unlocked, if available.
162      private int secondsBeforeUnlock;
163    
164    
165    
166      /**
167       * Creates a new account usability response control that may be used to
168       * indicate that the account is available and provide the number of seconds
169       * until expiration.  It will use the default OID and criticality.
170       *
171       * @param  secondsBeforeExpiration  The length of time in seconds until the
172       *                                  user's password expires, or -1 if the
173       *                                  user's password will not expire or the
174       *                                  expiration time is unknown.
175       */
176      public AccountUsableResponseControl(int secondsBeforeExpiration)
177      {
178        super(OID_ACCOUNT_USABLE_CONTROL, false,
179              encodeValue(secondsBeforeExpiration));
180    
181    
182        this.secondsBeforeExpiration = secondsBeforeExpiration;
183    
184        isUsable             = true;
185        isInactive           = false;
186        isReset              = false;
187        isExpired            = false;
188        remainingGraceLogins = -1;
189        isLocked             = false;
190        secondsBeforeUnlock  = 0;
191      }
192    
193    
194    
195      /**
196       * Creates a new account usability response control that may be used to
197       * indicate that the account is available and provide the number of seconds
198       * until expiration.
199       *
200       * @param  oid                      The OID for this account usability
201       *                                  response control.
202       * @param  isCritical               Indicates whether this control should be
203       *                                  marked critical.
204       * @param  secondsBeforeExpiration  The length of time in seconds until the
205       *                                  user's password expires, or -1 if the
206       *                                  user's password will not expire or the
207       *                                  expiration time is unknown.
208       */
209      public AccountUsableResponseControl(String oid, boolean isCritical,
210                                          int secondsBeforeExpiration)
211      {
212        super(oid, isCritical, encodeValue(secondsBeforeExpiration));
213    
214    
215        this.secondsBeforeExpiration = secondsBeforeExpiration;
216    
217        isUsable             = true;
218        isInactive           = false;
219        isReset              = false;
220        isExpired            = false;
221        remainingGraceLogins = -1;
222        isLocked             = false;
223        secondsBeforeUnlock  = 0;
224      }
225    
226    
227    
228      /**
229       * Creates a new account usability response control that may be used to
230       * indicate that the account is not available and provide information about
231       * the underlying reason.  It will use the default OID and criticality.
232       *
233       * @param  isInactive            Indicates whether the user's account has been
234       *                               inactivated by an administrator.
235       * @param  isReset               Indicates whether the user's password has
236       *                               been reset by an administrator.
237       * @param  isExpired             Indicates whether the user's password is
238       *                               expired.
239       * @param  remainingGraceLogins  The number of grace logins remaining.  A
240       *                               value of zero indicates that there are none
241       *                               remaining.  A value of -1 indicates that
242       *                               grace login functionality is not enabled.
243       * @param  isLocked              Indicates whether the user's account is
244       *                               currently locked out.
245       * @param  secondsBeforeUnlock   The length of time in seconds until the
246       *                               account is unlocked.  A value of -1 indicates
247       *                               that the account will not be automatically
248       *                               unlocked and must be reset by an
249       *                               administrator.
250       */
251      public AccountUsableResponseControl(boolean isInactive, boolean isReset,
252                                          boolean isExpired,
253                                          int remainingGraceLogins,
254                                          boolean isLocked, int secondsBeforeUnlock)
255      {
256        super(OID_ACCOUNT_USABLE_CONTROL, false,
257              encodeValue(isInactive, isReset, isExpired, remainingGraceLogins,
258                          isLocked, secondsBeforeUnlock));
259    
260    
261        this.isInactive           = isInactive;
262        this.isReset              = isReset;
263        this.isExpired            = isExpired;
264        this.remainingGraceLogins = remainingGraceLogins;
265        this.isLocked             = isLocked;
266        this.secondsBeforeUnlock  = secondsBeforeUnlock;
267    
268        isUsable                = false;
269        secondsBeforeExpiration = -1;
270      }
271    
272    
273    
274      /**
275       * Creates a new account usability response control that may be used to
276       * indicate that the account is not available and provide information about
277       * the underlying reason.
278       *
279       * @param  oid                   The OID for this account usability response
280       *                               control.
281       * @param  isCritical            Indicates whether this control should be
282       *                               marked critical.
283       * @param  isInactive            Indicates whether the user's account has been
284       *                               inactivated by an administrator.
285       * @param  isReset               Indicates whether the user's password has
286       *                               been reset by an administrator.
287       * @param  isExpired             Indicates whether the user's password is
288       *                               expired.
289       * @param  remainingGraceLogins  The number of grace logins remaining.  A
290       *                               value of zero indicates that there are none
291       *                               remaining.  A value of -1 indicates that
292       *                               grace login functionality is not enabled.
293       * @param  isLocked              Indicates whether the user's account is
294       *                               currently locked out.
295       * @param  secondsBeforeUnlock   The length of time in seconds until the
296       *                               account is unlocked.  A value of -1 indicates
297       *                               that the account will not be automatically
298       *                               unlocked and must be reset by an
299       *                               administrator.
300       */
301      public AccountUsableResponseControl(String oid, boolean isCritical,
302                                          boolean isInactive, boolean isReset,
303                                          boolean isExpired,
304                                          int remainingGraceLogins,
305                                          boolean isLocked, int secondsBeforeUnlock)
306      {
307        super(oid, isCritical,
308              encodeValue(isInactive, isReset, isExpired, remainingGraceLogins,
309                          isLocked, secondsBeforeUnlock));
310    
311    
312        this.isInactive           = isInactive;
313        this.isReset              = isReset;
314        this.isExpired            = isExpired;
315        this.remainingGraceLogins = remainingGraceLogins;
316        this.isLocked             = isLocked;
317        this.secondsBeforeUnlock  = secondsBeforeUnlock;
318    
319        isUsable                = false;
320        secondsBeforeExpiration = -1;
321      }
322    
323    
324    
325      /**
326       * Creates a new account usability response control using the provided
327       * information.  This version of the constructor is only intended for internal
328       * use.
329       *
330       * @param  oid                      The OID for this account usability
331       *                                  response control.
332       * @param  isCritical               Indicates whether this control should be
333       *                                  marked critical.
334       * @param  isAvailable              Indicates whether the user's account is
335       *                                  available for use.
336       * @param  secondsBeforeExpiration  The length of time in seconds until the
337       *                                  user's password expires, or -1 if the
338       *                                  user's password will not expire or the
339       *                                  expiration time is unknown.
340       * @param  isInactive               Indicates whether the user's account has
341       *                                  been inactivated by an administrator.
342       * @param  isReset                  Indicates whether the user's password has
343       *                                  been reset by an administrator.
344       * @param  isExpired                Indicates whether the user's password is
345       *                                  expired.
346       * @param  remainingGraceLogins     The number of grace logins remaining.  A
347       *                                  value of zero indicates that there are
348       *                                  none remaining.  A value of -1 indicates
349       *                                  that grace login functionality is not
350       *                                  enabled.
351       * @param  isLocked                 Indicates whether the user's account is
352       *                                  currently locked out.
353       * @param  secondsBeforeUnlock      The length of time in seconds until the
354       *                                  account is unlocked.  A value of -1
355       *                                  indicates that the account will not be
356       *                                  automatically unlocked and must be reset
357       *                                  by an administrator.
358       * @param  encodedValue             The pre-encoded value for this account
359       *                                  usable response control.
360       */
361      private AccountUsableResponseControl(String oid, boolean isCritical,
362                                                 boolean isAvailable,
363                                                 int secondsBeforeExpiration,
364                                                 boolean isInactive,
365                                                 boolean isReset, boolean isExpired,
366                                                 int remainingGraceLogins,
367                                                 boolean isLocked,
368                                                 int secondsBeforeUnlock,
369                                                 ASN1OctetString encodedValue)
370      {
371        super(oid, isCritical, encodedValue);
372    
373    
374        this.isUsable                = isAvailable;
375        this.secondsBeforeExpiration = secondsBeforeExpiration;
376        this.isInactive              = isInactive;
377        this.isReset                 = isReset;
378        this.isExpired               = isExpired;
379        this.remainingGraceLogins    = remainingGraceLogins;
380        this.isLocked                = isLocked;
381        this.secondsBeforeUnlock     = secondsBeforeUnlock;
382      }
383    
384    
385    
386      /**
387       * Encodes the provided information into an ASN.1 octet string suitable for
388       * use as the value for an account usable response control in which the use's
389       * account is available.
390       *
391       * @param  secondsBeforeExpiration  The length of time in seconds until the
392       *                                  user's password expires, or -1 if the
393       *                                  user's password will not expire or the
394       *                                  expiration time is unknown.
395       *
396       * @return  An ASN.1 octet string containing the encoded control value.
397       */
398      private static ASN1OctetString encodeValue(int secondsBeforeExpiration)
399      {
400        ASN1Integer sbeInteger = new ASN1Integer(TYPE_SECONDS_BEFORE_EXPIRATION,
401                                                 secondsBeforeExpiration);
402    
403        return new ASN1OctetString(sbeInteger.encode());
404      }
405    
406    
407    
408      /**
409       * Encodes the provided information into an ASN.1 octet string suitable for
410       * use as the value for an account usable response control in which the user's
411       * account is not available.
412       *
413       *
414       * @param  isInactive            Indicates whether the user's account has been
415       *                               inactivated by an administrator.
416       * @param  isReset               Indicates whether the user's password has
417       *                               been reset by an administrator.
418       * @param  isExpired             Indicates whether the user's password is
419       *                               expired.
420       * @param  remainingGraceLogins  The number of grace logins remaining.  A
421       *                               value of zero indicates that there are none
422       *                               remaining.  A value of -1 indicates that
423       *                               grace login functionality is not enabled.
424       * @param  isLocked              Indicates whether the user's account is
425       *                               currently locked out.
426       * @param  secondsBeforeUnlock   The length of time in seconds until the
427       *                               account is unlocked.  A value of -1 indicates
428       *                               that the account will not be automatically
429       *                               unlocked and must be reset by an
430       *                               administrator.
431       *
432       * @return  An ASN.1 octet string containing the encoded control value.
433       */
434      private static ASN1OctetString encodeValue(boolean isInactive,
435                                                 boolean isReset, boolean isExpired,
436                                                 int remainingGraceLogins,
437                                                 boolean isLocked,
438                                                 int secondsBeforeUnlock)
439      {
440        ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(5);
441    
442        if (isInactive)
443        {
444          elements.add(new ASN1Boolean(TYPE_INACTIVE, true));
445        }
446    
447        if (isReset)
448        {
449          elements.add(new ASN1Boolean(TYPE_RESET, true));
450        }
451    
452        if (isExpired)
453        {
454          elements.add(new ASN1Boolean(TYPE_EXPIRED, true));
455    
456          if (remainingGraceLogins >= 0)
457          {
458            elements.add(new ASN1Integer(TYPE_REMAINING_GRACE_LOGINS,
459                                         remainingGraceLogins));
460          }
461        }
462    
463        if (isLocked)
464        {
465          elements.add(new ASN1Integer(TYPE_SECONDS_BEFORE_UNLOCK,
466                                       secondsBeforeUnlock));
467        }
468    
469        ASN1Sequence moreInfoSequence = new ASN1Sequence(TYPE_MORE_INFO,
470                                                         elements);
471        return new ASN1OctetString(moreInfoSequence.encode());
472      }
473    
474    
475    
476      /**
477       * Creates a new account usable response control from the contents of the
478       * provided control.
479       *
480       * @param  control  The generic control containing the information to use to
481       *                  create this account usable response control.
482       *
483       * @return  The account usable response control decoded from the provided
484       *          control.
485       *
486       * @throws  LDAPException  If this control cannot be decoded as a valid
487       *                         account usable response control.
488       */
489      public static AccountUsableResponseControl decodeControl(Control control)
490             throws LDAPException
491      {
492        ASN1OctetString controlValue = control.getValue();
493        if (controlValue == null)
494        {
495          // The response control must always have a value.
496          Message message = ERR_ACCTUSABLERES_NO_CONTROL_VALUE.get();
497          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
498        }
499    
500    
501        try
502        {
503          ASN1Element valueElement = ASN1Element.decode(controlValue.value());
504          switch (valueElement.getType())
505          {
506            case TYPE_SECONDS_BEFORE_EXPIRATION:
507              int secondsBeforeExpiration =
508                       valueElement.decodeAsInteger().intValue();
509              return new AccountUsableResponseControl(control.getOID(),
510                                                      control.isCritical(), true,
511                                                      secondsBeforeExpiration,
512                                                      false, false, false, -1,
513                                                      false, 0, controlValue);
514            case TYPE_MORE_INFO:
515              boolean isInactive = false;
516              boolean isReset = false;
517              boolean isExpired = false;
518              boolean isLocked = false;
519              int     remainingGraceLogins = -1;
520              int     secondsBeforeUnlock = 0;
521    
522              for (ASN1Element e : valueElement.decodeAsSequence().elements())
523              {
524                switch (e.getType())
525                {
526                  case TYPE_INACTIVE:
527                    isInactive = e.decodeAsBoolean().booleanValue();
528                    break;
529                  case TYPE_RESET:
530                    isReset = e.decodeAsBoolean().booleanValue();
531                    break;
532                  case TYPE_EXPIRED:
533                    isExpired = e.decodeAsBoolean().booleanValue();
534                    break;
535                  case TYPE_REMAINING_GRACE_LOGINS:
536                    remainingGraceLogins = e.decodeAsInteger().intValue();
537                    break;
538                  case TYPE_SECONDS_BEFORE_UNLOCK:
539                    isLocked = true;
540                    secondsBeforeUnlock = e.decodeAsInteger().intValue();
541                    break;
542                  default:
543                    Message message = ERR_ACCTUSABLERES_UNKNOWN_UNAVAILABLE_TYPE.
544                        get(byteToHex(e.getType()));
545                    throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
546                }
547              }
548    
549              return new AccountUsableResponseControl(control.getOID(),
550                                                      control.isCritical(), false,
551                                                      -1, isInactive, isReset,
552                                                      isExpired,
553                                                      remainingGraceLogins,
554                                                      isLocked, secondsBeforeUnlock,
555                                                      controlValue);
556    
557            default:
558              Message message = ERR_ACCTUSABLERES_UNKNOWN_VALUE_ELEMENT_TYPE.get(
559                  byteToHex(valueElement.getType()));
560              throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
561          }
562        }
563        catch (LDAPException le)
564        {
565          throw le;
566        }
567        catch (ASN1Exception ae)
568        {
569          if (debugEnabled())
570          {
571            TRACER.debugCaught(DebugLogLevel.ERROR, ae);
572          }
573    
574          Message message = ERR_ACCTUSABLERES_DECODE_ERROR.get(ae.getMessage());
575          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
576        }
577        catch (Exception e)
578        {
579          if (debugEnabled())
580          {
581            TRACER.debugCaught(DebugLogLevel.ERROR, e);
582          }
583    
584          Message message =
585              ERR_ACCTUSABLERES_DECODE_ERROR.get(getExceptionMessage(e));
586          throw new LDAPException(LDAPResultCode.PROTOCOL_ERROR, message);
587        }
588      }
589    
590    
591    
592      /**
593       * Indicates whether the associated user account is available for use.
594       *
595       * @return  <CODE>true</CODE> if the associated user account is available, or
596       *          <CODE>false</CODE> if not.
597       */
598      public boolean isUsable()
599      {
600        return isUsable;
601      }
602    
603    
604    
605      /**
606       * Retrieves the length of time in seconds before the user's password expires.
607       * This value is unreliable if the account is not available.
608       *
609       * @return  The length of time in seconds before the user's password expires,
610       *          or -1 if it is unknown or password expiration is not enabled for
611       *          the user.
612       */
613      public int getSecondsBeforeExpiration()
614      {
615        return secondsBeforeExpiration;
616      }
617    
618    
619    
620      /**
621       * Indicates whether the user's account has been inactivated by an
622       * administrator.
623       *
624       * @return  <CODE>true</CODE> if the user's account has been inactivated by
625       *          an administrator, or <CODE>false</CODE> if not.
626       */
627      public boolean isInactive()
628      {
629        return isInactive;
630      }
631    
632    
633    
634      /**
635       * Indicates whether the user's password has been administratively reset and
636       * the user must change that password before any other operations will be
637       * allowed.
638       *
639       * @return  <CODE>true</CODE> if the user's password has been administratively
640       *          reset, or <CODE>false</CODE> if not.
641       */
642      public boolean isReset()
643      {
644        return isReset;
645      }
646    
647    
648    
649      /**
650       * Indicates whether the user's password is expired.
651       *
652       * @return  <CODE>true</CODE> if the user's password is expired, or
653       *          <CODE>false</CODE> if not.
654       */
655      public boolean isExpired()
656      {
657        return isExpired;
658      }
659    
660    
661    
662      /**
663       * Retrieves the number of remaining grace logins for the user.  This value is
664       * unreliable if the user's password is not expired.
665       *
666       * @return  The number of remaining grace logins for the user, or -1 if the
667       *          grace logins feature is not enabled for the user.
668       */
669      public int getRemainingGraceLogins()
670      {
671        return remainingGraceLogins;
672      }
673    
674    
675    
676      /**
677       * Indicates whether the user's account is locked for some reason.
678       *
679       * @return  <CODE>true</CODE> if the user's account is locked, or
680       *          <CODE>false</CODE> if it is not.
681       */
682      public boolean isLocked()
683      {
684        return isLocked;
685      }
686    
687    
688    
689      /**
690       * Retrieves the length of time in seconds before the user's account is
691       * automatically unlocked.  This value is unreliable is the user's account is
692       * not locked.
693       *
694       * @return  The length of time in seconds before the user's account is
695       *          automatically unlocked, or -1 if it requires administrative action
696       *          to unlock the account.
697       */
698      public int getSecondsBeforeUnlock()
699      {
700        return secondsBeforeUnlock;
701      }
702    
703    
704    
705      /**
706       * Retrieves a string representation of this password policy response control.
707       *
708       * @return  A string representation of this password policy response control.
709       */
710      public String toString()
711      {
712        StringBuilder buffer = new StringBuilder();
713        toString(buffer);
714        return buffer.toString();
715      }
716    
717    
718    
719      /**
720       * Appends a string representation of this password policy response control to
721       * the provided buffer.
722       *
723       * @param  buffer  The buffer to which the information should be appended.
724       */
725      public void toString(StringBuilder buffer)
726      {
727        buffer.append("AccountUsableResponseControl(isUsable=");
728        buffer.append(isUsable);
729    
730        if (isUsable)
731        {
732          buffer.append(",secondsBeforeExpiration=");
733          buffer.append(secondsBeforeExpiration);
734        }
735        else
736        {
737          buffer.append(",isInactive=");
738          buffer.append(isInactive);
739          buffer.append(",isReset=");
740          buffer.append(isReset);
741          buffer.append(",isExpired=");
742          buffer.append(isExpired);
743          buffer.append(",remainingGraceLogins=");
744          buffer.append(remainingGraceLogins);
745          buffer.append(",isLocked=");
746          buffer.append(isLocked);
747          buffer.append(",secondsBeforeUnlock=");
748          buffer.append(secondsBeforeUnlock);
749        }
750    
751        buffer.append(")");
752      }
753    }
754