1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * ident        "%Z%%M% %I%     %E% SMI"
  24  *
  25  * Copyright 1998-2003 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  */
  28 
  29 package com.sun.dhcpmgr.data;
  30 
  31 import java.util.*;
  32 
  33 /**
  34  * Option is a simple data class which encapsulates an option record in
  35  * the dhcptab.  See dhcptab(4) for the gory details on options (aka symbols).
  36  *
  37  * @see DhcptabRecord
  38  * @see Macro
  39  */
  40 public class Option extends DhcptabRecord implements Cloneable {
  41     private byte context;
  42     private short code;
  43     private byte type;
  44     private int granularity;
  45     private int maximum;
  46     private Vector vendors;
  47     private boolean valueClean = false;
  48     private boolean validValue = true;
  49 
  50     // The Option attributes that must match their native values.
  51 
  52     // Definition for attribute limits
  53     public static short MAX_NAME_SIZE = 128;
  54 
  55     // Option contexts.
  56     public static byte STANDARD = 0;
  57     public static byte EXTEND = 1;
  58     public static byte VENDOR = 2;
  59     public static byte SITE = 3;
  60     public static byte CONTEXTS = 4;
  61     public static OptionContext [] ctxts = {
  62         new OptionContext(STANDARD, "Standard", "standard_option"),
  63         new OptionContext(EXTEND, "Extend", "extended_option"),
  64         new OptionContext(VENDOR, "Vendor=", "vendor_option"),
  65         new OptionContext(SITE, "Site", "site_option") };
  66 
  67     // Option types.
  68     public static byte ASCII = 0;
  69     public static byte OCTET = 1;
  70     public static byte IP = 2;
  71     public static byte NUMBER = 3;
  72     public static byte BOOLEAN = 4;
  73     public static byte UNUMBER8 = 5;
  74     public static byte UNUMBER16 = 6;
  75     public static byte UNUMBER32 = 7;
  76     public static byte UNUMBER64 = 8;
  77     public static byte SNUMBER8 = 9;
  78     public static byte SNUMBER16 = 10;
  79     public static byte SNUMBER32 = 11;
  80     public static byte SNUMBER64 = 12;
  81     public static byte TYPES = 13;
  82 
  83     public static OptionType [] types = {
  84         new OptionType((byte)0, "ASCII", "ascii_type"),
  85         new OptionType((byte)1, "OCTET", "octet_type"),
  86         new OptionType((byte)2, "IP", "ip_type"),
  87         new OptionType((byte)3, "NUMBER", "number_type"),
  88         new OptionType((byte)4, "BOOL", "boolean_type"),
  89         new OptionType((byte)6, "UNUMBER8", "unumber8_type"),
  90         new OptionType((byte)7, "UNUMBER16", "unumber16_type"),
  91         new OptionType((byte)8, "UNUMBER32", "unumber32_type"),
  92         new OptionType((byte)9, "UNUMBER64", "unumber64_type"),
  93         new OptionType((byte)10, "SNUMBER8", "snumber8_type"),
  94         new OptionType((byte)11, "SNUMBER16", "snumber16_type"),
  95         new OptionType((byte)12, "SNUMBER32", "snumber32_type"),
  96         new OptionType((byte)13, "SNUMBER64", "snumber64_type") };
  97         
  98     /*
  99      * These need to be the same as the definitions in libdhcputil's
 100      * parser in dhcp_symbol.c
 101      */  
 102     public static String DSYM_CLASS_DEL_SPACE = " ";
 103     public static String DSYM_CLASS_DEL = DSYM_CLASS_DEL_SPACE + "\t\n";
 104     public static String DSYM_CLASS_DEL_REGEXP = ".*[" + DSYM_CLASS_DEL + "].*";
 105     public static char DSYM_FIELD_DEL = ',';
 106     public static char DSYM_QUOTE = '"';
 107 
 108 
 109     // Serialization id for this class
 110     static final long serialVersionUID = 7468266817375654444L;
 111 
 112     /**
 113      * Construct an empty instance.  Default to Site option, IP type.
 114      */
 115     public Option() {
 116         super("", DhcptabRecord.OPTION, "");
 117         valueClean = false;
 118         vendors = new Vector();
 119         context = ctxts[SITE].getCode();
 120         type = types[IP].getCode();
 121         granularity = 1;
 122     }
 123 
 124     /**
 125      * Construct a fully defined instance. Used by the server to create
 126      * Options.
 127      * @param name the option name/key
 128      * @param context the option context/category code
 129      * @param vendors the list of vendors (if any)
 130      * @param code the option code
 131      * @param type the option type code
 132      * @param gran the option granularity
 133      * @param max the option maximum
 134      * @param sig the signature from the dhcptab
 135      * @param isValid the flag indicating option definition validity
 136      */
 137     public Option(String name, byte context, String [] vendors, short code,
 138         byte type, int gran, int max, String sig, boolean isValid) {
 139 
 140         super("", DhcptabRecord.OPTION, "");
 141 
 142         valueClean = false;
 143         validValue = isValid;
 144 
 145         setKey(name);
 146         setContext(context);
 147         setVendors(vendors);
 148         setCode(code);
 149         setType(type);
 150         setGranularity(gran);
 151         setMaximum(max);
 152 
 153         if (sig != null) {
 154                 setSignature(sig);
 155         }
 156     }
 157 
 158     /**
 159      * Set the option name as specified.
 160      * @param name a string representing the option name.
 161      */
 162     public void setKey(String name) {
 163         try {
 164                 super.setKey(name);
 165         } catch (ValidationException e) {
 166                 // Can't happen.
 167         }
 168     }
 169     
 170     /**
 171      * Get the context for this option
 172      * @return a byte for the option context (context codes are
 173      * defined by the OptionContext objects in the ctxts array).
 174      */
 175     public byte getContext() {
 176         return context;
 177     }
 178     
 179     /**
 180      * Set the context for this option (context codes are defined
 181      *  by the OptionContext objects in the ctxts array).
 182      */
 183     public void setContext(byte c) {
 184         context = c;
 185         valueClean = false;
 186     }
 187     
 188     /**
 189      * Enumerate the vendor list.
 190      * @return an Enumeration of the vendors, which will be empty for
 191      * non-vendor options.
 192      */
 193     public Enumeration getVendors() {
 194         return vendors.elements();
 195     }
 196     
 197     /**
 198      * Get the number of vendors for this option.
 199      * @return an int count of the vendors, zero for non-vendor options.
 200      */
 201     public int getVendorCount() {
 202         return vendors.size();
 203     }
 204     
 205     /**
 206      * Add a vendor to the list for this option.
 207      * @param v the vendor name as a String.
 208      */
 209     public void addVendor(String v) throws ValidationException {
 210         if (v.indexOf(DSYM_FIELD_DEL) != -1) {
 211             throw new ValidationException(v);
 212         }
 213         vendors.addElement(v);
 214         valueClean = false;
 215     }
 216     
 217     /**
 218      * Empty the vendor list.
 219      */
 220     public void clearVendors() {
 221         vendors = new Vector();
 222         valueClean = false;
 223     }
 224     
 225     /**
 226      * Remove a vendor from the list.
 227      * @param index the position of the vendor to remove in the list of vendors
 228      */
 229     public void removeVendorAt(int index) {
 230         vendors.removeElementAt(index);
 231         valueClean = false;
 232     }
 233     
 234     /**
 235      * Get the vendor at a specified index in the vendor list.
 236      * @param index the index of the vendor to retrieve
 237      * @return the vendor name
 238      */
 239     public String getVendorAt(int index) {
 240         return (String)vendors.elementAt(index);
 241     }
 242 
 243     private void setVendors(String [] vendors) {
 244 
 245         this.vendors = new Vector();
 246 
 247         if (vendors == null) {
 248             return;
 249         }
 250 
 251         for (int i = 0; i < vendors.length; i++) {
 252             this.vendors.addElement(vendors[i]);
 253         }
 254 
 255     }
 256     
 257     /**
 258      * Set the vendor name at a specified index in the list.
 259      * @param vendor the vendor name
 260      * @param index the position in the list to set.
 261      */
 262     public void setVendorAt(String vendor, int index) {
 263         if (index >= vendors.size()) {
 264             vendors.setSize(index+1);
 265         }
 266         vendors.setElementAt(vendor, index);
 267         valueClean = false;
 268     }
 269     
 270     /**
 271      * Get the option code.
 272      * @return the code as a short.
 273      */
 274     public short getCode() {
 275         return code;
 276     }
 277     
 278     /**
 279      * Set the option code.
 280      * @param c the code to use
 281      */
 282     public void setCode(short c) {
 283         code = c;
 284         valueClean = false;
 285     }
 286     
 287     /**
 288      * Get the type.
 289      * @return a byte value for the type (type codes are
 290      * defined by the OptionTypes objects in the type array).
 291      * OCTET
 292      */
 293     public byte getType() {
 294         return type;
 295     }
 296     
 297     /**
 298      * Set the type.
 299      * @param t the type code (type codes are defined by the
 300      * OptionTypes objects in the type array).
 301      * or OCTET.
 302      */
 303     public void setType(byte t) {
 304         type = t;
 305         valueClean = false;
 306     }
 307     
 308     /**
 309      * Get the granularity.  See dhcptab(4) for an explanation of granularity
 310      * interpretations.
 311      * @return the granularity as an int
 312      */
 313     public int getGranularity() {
 314         return granularity;
 315     }
 316     
 317     /**
 318      * Set the granularity.  See dhcptab(4) for an explanation of granularity
 319      * interpretations.
 320      * @param g the granularity as an int.
 321      */
 322     public void setGranularity(int g) {
 323         granularity = g;
 324         valueClean = false;
 325     }
 326     
 327     /**
 328      * Get the maximum.  See dhcptab(4) for an explanation of maximum.
 329      * @return the maximum as an int.
 330      */
 331     public int getMaximum() {
 332         return maximum;
 333     }
 334     
 335     /**
 336      * Set the maximum.  See dhcptab(4) for an explanation of maximum.
 337      * @param m the maximum as an int.
 338      */
 339     public void setMaximum(int m) {
 340         maximum = m;
 341         valueClean = false;
 342     }
 343     
 344     /**
 345      * Return validity of this option.
 346      * @return true if the option is correctly defined, false if not
 347      */
 348     public boolean isValid() {
 349         return (validValue);
 350     }
 351 
 352     /**
 353      * Get the definition as a string in the format specified by dhcptab(4)
 354      * @return a String containing the definition
 355      */
 356     public String getValue() {
 357         /* The value string stored is not clean, regenerate */
 358         if (!valueClean) {
 359             StringBuffer b = new StringBuffer();
 360             // Start with context
 361             b.append(getContextDhcptabString(context));
 362             // Vendor context next adds the vendors, separate by blanks
 363             if (context == ctxts[VENDOR].getCode()) {
 364                 boolean first = true;
 365                 for (Enumeration e = getVendors(); e.hasMoreElements(); ) {
 366                     String s = (String)e.nextElement();
 367                     if (!first) {
 368                         b.append(DSYM_CLASS_DEL_SPACE);
 369                     } else {
 370                         first = false;
 371                     }
 372                     // If vendor class contains whitespace, need to quote it
 373                     boolean needQuoting = s.matches(DSYM_CLASS_DEL_REGEXP);
 374                     if (needQuoting) {
 375                         b.append(DSYM_QUOTE);
 376                     }
 377                     b.append(s);
 378                     if (needQuoting) {
 379                         b.append(DSYM_QUOTE);
 380                     }
 381                 }
 382             }
 383             b.append(DSYM_FIELD_DEL);
 384             // Add the code
 385             b.append(code);
 386             b.append(DSYM_FIELD_DEL);
 387             // Add the type
 388             b.append(getTypeDhcptabString(type));
 389             b.append(DSYM_FIELD_DEL);
 390             // Add the granularity
 391             b.append(granularity);
 392             b.append(DSYM_FIELD_DEL);
 393             // Add the maximum
 394             b.append(maximum);
 395             // Save it and note as such so we can avoid doing this again
 396             try {
 397                 super.setValue(b.toString());
 398             } catch (ValidationException e) {
 399                 // This should never happen!
 400             }
 401             valueClean = true;   
 402         }
 403         return super.getValue();
 404     }
 405             
 406     // Make a copy of this option
 407     public Object clone() {
 408         Option o = new Option();
 409 
 410         o.setKey(getKey());
 411         o.setContext(getContext());
 412         o.setCode(getCode());
 413         o.vendors = new Vector();
 414         for (Enumeration en = vendors.elements(); en.hasMoreElements(); ) {
 415             String s = (String)en.nextElement();
 416             o.vendors.addElement(new String(s));
 417         }
 418         o.setType(getType());
 419         o.setGranularity(getGranularity());
 420         o.setMaximum(getMaximum());
 421         o.setSignature(getSignature());
 422 
 423         return o;
 424     }
 425     
 426     /**
 427      * Returns a string representation of this object.
 428      * @return a string representation of this object.
 429      */
 430     public String toString() {
 431         return (getKey() + " s " + getValue());
 432     }
 433 
 434     /**
 435      * Returns the context definition for the specified context.
 436      * @param code the context code.
 437      * @return the OptionContext for the context.
 438      */
 439     public static OptionContext findContext(byte code) {
 440 
 441         OptionContext ctxt = null;
 442 
 443         for (int i = 0; i < CONTEXTS; i++) {
 444             if (ctxts[i].getCode() == code) {
 445                 ctxt = ctxts[i];
 446                 break;
 447             }
 448         }
 449 
 450         return (ctxt);
 451     }
 452 
 453     /**
 454      * Returns the dhcptab string representation of the specified context.
 455      * @param code the context code.
 456      * @return the dhcptab string representation of the context.
 457      */
 458     public static String getContextDhcptabString(byte code) {
 459 
 460         OptionContext ctxt = findContext(code);
 461 
 462         if (ctxt == null) {
 463             return ("undefined");
 464         } else {
 465             return (ctxt.getDhcptabString());
 466         }
 467     }
 468 
 469     /**
 470      * Returns the string representation of the specified context.
 471      * @param code the context code.
 472      * @return a string representation of the context.
 473      */
 474     public static String getContextString(byte code) {
 475 
 476         OptionContext ctxt = findContext(code);
 477 
 478         if (ctxt == null) {
 479             return ("undefined");
 480         } else {
 481             return (ctxt.toString());
 482         }
 483     }
 484 
 485     /**
 486      * Returns the type definition for the specified type.
 487      * @param code the type code.
 488      * @return the OptionType for the type.
 489      */
 490     public static OptionType findType(byte code) {
 491 
 492         OptionType type = null;
 493 
 494         for (int i = 0; i < TYPES; i++) {
 495             if (types[i].getCode() == code) {
 496                 type = types[i];
 497                 break;
 498             }
 499         }
 500 
 501         return (type);
 502     }
 503 
 504     /**
 505      * Returns the dhcptab string representation of the specified type.
 506      * @param code the type code.
 507      * @return the dhcptab string representation of the type.
 508      */
 509     public static String getTypeDhcptabString(byte code) {
 510 
 511         OptionType type = findType(code);
 512 
 513         if (type == null) {
 514             return ("undefined"); // should never happen
 515         } else {
 516             return (type.getDhcptabString());
 517         }
 518     }
 519 
 520     /**
 521      * Returns the string representation of the specified type.
 522      * @param code the type code.
 523      * @return a string representation of the type.
 524      */
 525     public static String getTypeString(byte code) {
 526 
 527         OptionType type = findType(code);
 528 
 529         if (type == null) {
 530             return ("undefined"); // should never happen
 531         } else {
 532             return (type.toString());
 533         }
 534     }
 535 }