Source for gnu.inet.ldap.BERDecoder

   1: /*
   2:  * BERDecoder.java
   3:  * Copyright (C) 2004 The Free Software Foundation
   4:  * 
   5:  * This file is part of GNU inetlib, a library.
   6:  * 
   7:  * GNU inetlib is free software; you can redistribute it and/or modify
   8:  * it under the terms of the GNU General Public License as published by
   9:  * the Free Software Foundation; either version 2 of the License, or
  10:  * (at your option) any later version.
  11:  * 
  12:  * GNU inetlib is distributed in the hope that it will be useful,
  13:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15:  * GNU General Public License for more details.
  16:  * 
  17:  * You should have received a copy of the GNU General Public License
  18:  * along with this library; if not, write to the Free Software
  19:  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20:  * 
  21:  * Linking this library statically or dynamically with other modules is
  22:  * making a combined work based on this library.  Thus, the terms and
  23:  * conditions of the GNU General Public License cover the whole
  24:  * combination.
  25:  *
  26:  * As a special exception, the copyright holders of this library give you
  27:  * permission to link this library with independent modules to produce an
  28:  * executable, regardless of the license terms of these independent
  29:  * modules, and to copy and distribute the resulting executable under
  30:  * terms of your choice, provided that you also meet, for each linked
  31:  * independent module, the terms and conditions of the license of that
  32:  * module.  An independent module is a module which is not derived from
  33:  * or based on this library.  If you modify this library, you may extend
  34:  * this exception to your version of the library, but you are not
  35:  * obliged to do so.  If you do not wish to do so, delete this
  36:  * exception statement from your version.
  37:  */
  38: 
  39: package gnu.inet.ldap;
  40: 
  41: import java.io.ByteArrayOutputStream;
  42: import java.io.UnsupportedEncodingException;
  43: 
  44: /**
  45:  * Utility class for decoding BER values.
  46:  * For each value to be read, the application must call <code>parseType</code>
  47:  * to return the type of the value, then the specific
  48:  * <code>parse<i>XXX</i></code> method for the type to return the actual
  49:  * value, or <code>skip</code> to skip the value.
  50:  *
  51:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  52:  */
  53: public class BERDecoder
  54: {
  55: 
  56:   private byte[] buffer;
  57:   private int offset;
  58:   private int type;
  59:   private int len;
  60:   private boolean control;
  61:   private boolean utf8;
  62: 
  63:   public BERDecoder(byte[] data, boolean utf8)
  64:   {
  65:     buffer = data;
  66:     offset = 0;
  67:     control = true;
  68:     this.utf8 = utf8;
  69:   }
  70: 
  71:   /**
  72:    * Returns the type of the current value record.
  73:    * If there are no more records to read, this method returns -1.
  74:    */
  75:   public int parseType()
  76:     throws BERException
  77:   {
  78:     if (offset >= buffer.length)
  79:       {
  80:         return -1;
  81:       }
  82:     type = byteToInt(buffer[offset++]);
  83:     len = byteToInt(buffer[offset++]);
  84:     if ((len & 0x80) != 0)
  85:       {
  86:         int lsize = len - 0x80;
  87:         if (lsize > 4)
  88:           {
  89:             throw new BERException("Data too long: " + lsize);
  90:           }
  91:         if (buffer.length - offset < lsize)
  92:           {
  93:             throw new BERException("Insufficient data");
  94:           }
  95:         len = 0;
  96:         for (int i = 0; i < lsize; i++)
  97:           {
  98:             len = (len << 8) + byteToInt(buffer[offset++]);
  99:           }
 100:         if (buffer.length - offset < len)
 101:           {
 102:             throw new BERException("Insufficient data");
 103:           }
 104:       }
 105:     control = false;
 106:     return type;
 107:   }
 108: 
 109:   static int byteToInt(byte b)
 110:   {
 111:     int ret = (int) b;
 112:     if (ret < 0)
 113:       {
 114:         ret += 0x100;
 115:       }
 116:     return ret;
 117:   }
 118: 
 119:   int getLength()
 120:   {
 121:     return len;
 122:   }
 123: 
 124:   public boolean available()
 125:   {
 126:     return (offset < buffer.length);
 127:   }
 128: 
 129:   public void skip()
 130:   {
 131:     offset += len;
 132:     control = true;
 133:   }
 134: 
 135:   public boolean parseBoolean()
 136:     throws BERException
 137:   {
 138:     if (control)
 139:       {
 140:         parseType();
 141:       }
 142:     if (type != BERConstants.BOOLEAN)
 143:       {
 144:         throw new BERException("Unexpected type: " + type);
 145:       }
 146:     int c = (int) buffer[offset++];
 147:     control = true;
 148:     return (c != 0);
 149:   }
 150: 
 151:   public int parseInt()
 152:     throws BERException
 153:   {
 154:     if (control)
 155:       {
 156:         parseType();
 157:       }
 158:     if (type != BERConstants.INTEGER && type != BERConstants.ENUMERATED)
 159:       {
 160:         throw new BERException("Unexpected type: " + type);
 161:       }
 162:     byte c = buffer[offset++];
 163:     int val = ((int) c) & 0x7f;
 164:     for (int i = 1; i < len; i++)
 165:       {
 166:         val <<= 0x08;
 167:         val |= ((int) buffer[offset++]) & 0xff;
 168:       }
 169:     if ((c & 0x80) != 0)
 170:       {
 171:         val = -val;
 172:       }
 173:     control = true;
 174:     return val;
 175:   }
 176: 
 177:   public String parseString()
 178:     throws BERException
 179:   {
 180:     if (control)
 181:       {
 182:         parseType();
 183:       }
 184:     if (len == 0)
 185:       {
 186:         control = true;
 187:         return "";
 188:       }
 189:     if (type != BERConstants.UTF8_STRING && type != BERConstants.OCTET_STRING)
 190:       {
 191:         throw new BERException("Unexpected type: " + type);
 192:       }
 193:     String encoding = (type == BERConstants.UTF8_STRING) ? "UTF-8" :
 194:       "ISO-8859-1";
 195:     try
 196:       {
 197:         String ret = new String(buffer, offset, len, encoding);
 198:         offset += len;
 199:         control = true;
 200:         return ret;
 201:       }
 202:     catch(UnsupportedEncodingException e)
 203:       {
 204:         throw new BERException("JVM does not support " + encoding);
 205:       }
 206:   }
 207: 
 208:   public byte[] parseOctetString()
 209:     throws BERException
 210:   {
 211:     if (control)
 212:       {
 213:         parseType();
 214:       }
 215:     if (type != BERConstants.OCTET_STRING)
 216:       {
 217:         throw new BERException("Unexpected type: " + type);
 218:       }
 219:     byte[] ret = new byte[len];
 220:     System.arraycopy(buffer, offset, ret, 0, len);
 221:     offset += len;
 222:     control = true;
 223:     return ret;
 224:   }
 225: 
 226:   public BERDecoder parseSequence()
 227:     throws BERException
 228:   {
 229:     return parseSequence(BERConstants.SEQUENCE);
 230:   }
 231:   
 232:   public BERDecoder parseSequence(int code)
 233:     throws BERException
 234:   {
 235:     if (control)
 236:       {
 237:         parseType();
 238:       }
 239:     if (code != -1 && type != code)
 240:       {
 241:         throw new BERException("Unexpected type: " + type);
 242:       }
 243:     byte[] ret = new byte[len];
 244:     System.arraycopy(buffer, offset, ret, 0, len);
 245:     offset += len;
 246:     control = true;
 247:     return new BERDecoder(ret, utf8);
 248:   }
 249:   
 250:   public BERDecoder parseSet()
 251:     throws BERException
 252:   {
 253:     return parseSet(BERConstants.SET);
 254:   }
 255: 
 256:   public BERDecoder parseSet(int code)
 257:     throws BERException
 258:   {
 259:     return parseSequence(code);
 260:   }
 261: 
 262:   public static void main(String[] args)
 263:   {
 264:     try
 265:       {
 266:         ByteArrayOutputStream out = new ByteArrayOutputStream();
 267:         for (int c = System.in.read(); c != -1; c = System.in.read())
 268:           {
 269:             out.write(c);
 270:           }
 271:         byte[] code = out.toByteArray();
 272:         BERDecoder decoder = new BERDecoder(code, true);
 273:         debug(decoder, 0);
 274:       }
 275:     catch (Exception e)
 276:       {
 277:         e.printStackTrace(System.err);
 278:       }
 279:   }
 280: 
 281:   private static void debug(BERDecoder decoder, int depth)
 282:     throws BERException
 283:   {
 284:     for (int t = decoder.parseType(); t != -1; t = decoder.parseType())
 285:       {
 286:         for (int i = 0; i < depth; i++)
 287:           {
 288:             System.out.print('\t');
 289:           } 
 290:         switch (t)
 291:           {
 292:           case BERConstants.BOOLEAN:
 293:             System.out.println("BOOLEAN: " + decoder.parseBoolean());
 294:             break;
 295:           case BERConstants.INTEGER:
 296:             System.out.println("INTEGER: " + decoder.parseInt());
 297:             break;
 298:           case BERConstants.ENUMERATED:
 299:             System.out.println("ENUMERATED: " + decoder.parseInt());
 300:             break;
 301:           case BERConstants.OCTET_STRING:
 302:             System.out.println("OCTET-STRING: " +
 303:                                 toString(decoder.parseOctetString()));
 304:             break;
 305:           case BERConstants.UTF8_STRING:
 306:             System.out.println("STRING: \"" + decoder.parseString() + "\"");
 307:             break;
 308:           default:
 309:             System.out.println("SEQUENCE " + t + "(0x" +
 310:                                 Integer.toHexString(t) + "): " +
 311:                                 decoder.getLength());
 312:             BERDecoder sequence = decoder.parseSequence(t);
 313:             debug(sequence, depth + 1);            
 314:             break;
 315:           }
 316:       }
 317:   }
 318: 
 319:   private static String toString(byte[] bytes)
 320:   {
 321:     try
 322:       {
 323:         return "\"" + new String(bytes, "UTF-8") + "\"";
 324:       }
 325:     catch (UnsupportedEncodingException e)
 326:       {
 327:         return bytes.toString();
 328:       }
 329:   }
 330:   
 331: }