Source for gnu.javax.crypto.keyring.EnvelopeEntry

   1: /* EnvelopeEntry.java --
   2:    Copyright (C) 2003, 2006, 2010 Free Software Foundation, Inc.
   3: 
   4: This file is a part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2 of the License, or (at
   9: your option) any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; if not, write to the Free Software
  18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  19: 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: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version.  */
  37: 
  38: 
  39: package gnu.javax.crypto.keyring;
  40: 
  41: import gnu.java.security.Configuration;
  42: 
  43: import java.io.ByteArrayOutputStream;
  44: import java.io.DataInputStream;
  45: import java.io.DataOutputStream;
  46: import java.io.IOException;
  47: import java.util.ArrayList;
  48: import java.util.Iterator;
  49: import java.util.LinkedList;
  50: import java.util.List;
  51: import java.util.StringTokenizer;
  52: import java.util.logging.Logger;
  53: 
  54: /**
  55:  * An envelope entry is a generic container for some number of primitive and
  56:  * other envelope entries.
  57:  */
  58: public abstract class EnvelopeEntry
  59:     extends Entry
  60: {
  61:   private static final Logger log = Configuration.DEBUG ?
  62:                 Logger.getLogger(EnvelopeEntry.class.getName()) : null;
  63:   /** The envelope that contains this one (if any). */
  64:   protected EnvelopeEntry containingEnvelope;
  65:   /** The contained entries. */
  66:   protected List entries;
  67: 
  68:   public EnvelopeEntry(int type, Properties properties)
  69:   {
  70:     super(type, properties);
  71:     entries = new LinkedList();
  72:     if (this.properties.get("alias-list") != null)
  73:       this.properties.remove("alias-list");
  74:   }
  75: 
  76:   protected EnvelopeEntry(int type)
  77:   {
  78:     super(type);
  79:     entries = new LinkedList();
  80:   }
  81: 
  82:   /**
  83:    * Adds an entry to this envelope.
  84:    *
  85:    * @param entry The entry to add.
  86:    */
  87:   public void add(Entry entry)
  88:   {
  89:     if (Configuration.DEBUG)
  90:       log.entering(this.getClass().getName(), "add", entry);
  91:     if (! containsEntry(entry))
  92:       {
  93:         if (entry instanceof EnvelopeEntry)
  94:           ((EnvelopeEntry) entry).setContainingEnvelope(this);
  95:         entries.add(entry);
  96:         if (Configuration.DEBUG)
  97:           log.fine("Payload is " + (payload == null ? "" : "not ") + "null");
  98:         makeAliasList();
  99:       }
 100:     if (Configuration.DEBUG)
 101:       log.exiting(this.getClass().getName(), "add");
 102:   }
 103: 
 104:   /**
 105:    * Tests if this envelope contains a primitive entry with the given alias.
 106:    *
 107:    * @param alias The alias to test.
 108:    * @return True if this envelope (or one of the contained envelopes) contains
 109:    *         a primitive entry with the given alias.
 110:    */
 111:   public boolean containsAlias(String alias)
 112:   {
 113:     if (Configuration.DEBUG)
 114:       log.entering(this.getClass().getName(), "containsAlias", alias);
 115:     String aliases = getAliasList();
 116:     if (Configuration.DEBUG)
 117:       log.fine("aliases = [" + aliases + "]");
 118:     boolean result = false;
 119:     if (aliases != null)
 120:       {
 121:         StringTokenizer tok = new StringTokenizer(aliases, ";");
 122:         while (tok.hasMoreTokens())
 123:           if (tok.nextToken().equals(alias))
 124:             {
 125:               result = true;
 126:               break;
 127:             }
 128:       }
 129:     if (Configuration.DEBUG)
 130:       log.exiting(this.getClass().getName(), "containsAlias",
 131:                   Boolean.valueOf(result));
 132:     return result;
 133:   }
 134: 
 135:   /**
 136:    * Tests if this envelope contains the given entry.
 137:    *
 138:    * @param entry The entry to test.
 139:    * @return True if this envelope contains the given entry.
 140:    */
 141:   public boolean containsEntry(Entry entry)
 142:   {
 143:     if (entry instanceof EnvelopeEntry)
 144:       return entries.contains(entry);
 145:     if (entry instanceof PrimitiveEntry)
 146:       for (Iterator it = entries.iterator(); it.hasNext();)
 147:         {
 148:           Entry e = (Entry) it.next();
 149:           if (e.equals(entry))
 150:             return true;
 151:           if ((e instanceof EnvelopeEntry)
 152:               && ((EnvelopeEntry) e).containsEntry(entry))
 153:             return true;
 154:         }
 155:     return false;
 156:   }
 157: 
 158:   /**
 159:    * Returns a copy of all entries this envelope contains.
 160:    *
 161:    * @return All contained entries.
 162:    */
 163:   public List getEntries()
 164:   {
 165:     return new ArrayList(entries);
 166:   }
 167: 
 168:   /**
 169:    * Gets all primitive entries that have the given alias. If there are any
 170:    * masked entries that contain the given alias, they will be returned as well.
 171:    *
 172:    * @param alias The alias of the entries to get.
 173:    * @return A list of all primitive entries that have the given alias.
 174:    */
 175:   public List get(String alias)
 176:   {
 177:     if (Configuration.DEBUG)
 178:       log.entering(this.getClass().getName(), "get", alias);
 179:     List result = new LinkedList();
 180:     for (Iterator it = entries.iterator(); it.hasNext();)
 181:       {
 182:         Entry e = (Entry) it.next();
 183:         if (e instanceof EnvelopeEntry)
 184:           {
 185:             EnvelopeEntry ee = (EnvelopeEntry) e;
 186:             if (! ee.containsAlias(alias))
 187:               continue;
 188:             if (ee instanceof MaskableEnvelopeEntry)
 189:               {
 190:                 MaskableEnvelopeEntry mee = (MaskableEnvelopeEntry) ee;
 191:                 if (mee.isMasked())
 192:                   {
 193:                     if (Configuration.DEBUG)
 194:                       log.fine("Processing masked entry: " + mee);
 195:                     result.add(mee);
 196:                     continue;
 197:                   }
 198:               }
 199:             if (Configuration.DEBUG)
 200:               log.fine("Processing unmasked entry: " + ee);
 201:             result.addAll(ee.get(alias));
 202:           }
 203:         else if (e instanceof PrimitiveEntry)
 204:           {
 205:             PrimitiveEntry pe = (PrimitiveEntry) e;
 206:             if (pe.getAlias().equals(alias))
 207:               result.add(e);
 208:           }
 209:       }
 210:     if (Configuration.DEBUG)
 211:       log.exiting(this.getClass().getName(), "get", result);
 212:     return result;
 213:   }
 214: 
 215:   /**
 216:    * Returns the list of all aliases contained by this envelope, separated by a
 217:    * semicolon (';').
 218:    *
 219:    * @return The list of aliases.
 220:    */
 221:   public String getAliasList()
 222:   {
 223:     String list = properties.get("alias-list");
 224:     if (list == null)
 225:       return "";
 226:     else
 227:       return list;
 228:   }
 229: 
 230:   /**
 231:    * Removes the specified entry.
 232:    *
 233:    * @param entry The entry.
 234:    * @return True if an entry was removed.
 235:    */
 236:   public boolean remove(Entry entry)
 237:   {
 238:     if (Configuration.DEBUG)
 239:       log.entering(this.getClass().getName(), "remove", entry);
 240:     boolean ret = false;
 241:     for (Iterator it = entries.iterator(); it.hasNext();)
 242:       {
 243:         Entry e = (Entry) it.next();
 244:         if (e instanceof EnvelopeEntry)
 245:           {
 246:             if (e == entry)
 247:               {
 248:                 it.remove();
 249:                 ret = true;
 250:                 break;
 251:               }
 252:             if (((EnvelopeEntry) e).remove(entry))
 253:               {
 254:                 ret = true;
 255:                 break;
 256:               }
 257:           }
 258:         else if (e instanceof PrimitiveEntry)
 259:           {
 260:             if (((PrimitiveEntry) e).equals(entry))
 261:               {
 262:                 it.remove();
 263:                 ret = true;
 264:                 break;
 265:               }
 266:           }
 267:       }
 268:     if (ret)
 269:       {
 270:         if (Configuration.DEBUG)
 271:           log.fine("State before: " + this);
 272:         payload = null;
 273:         makeAliasList();
 274:         if (Configuration.DEBUG)
 275:           log.fine("State after: " + this);
 276:       }
 277:     if (Configuration.DEBUG)
 278:       log.exiting(this.getClass().getName(), "remove", Boolean.valueOf(ret));
 279:     return ret;
 280:   }
 281: 
 282:   /**
 283:    * Removes all primitive entries that have the specified alias.
 284:    *
 285:    * @param alias The alias of the entries to remove.
 286:    * @return <code>true</code> if <code>alias</code> was present and was
 287:    *         successfully trmoved. Returns <code>false</code> if
 288:    *         <code>alias</code> was not present in the list of aliases in this
 289:    *         envelope.
 290:    */
 291:   public boolean remove(String alias)
 292:   {
 293:     if (Configuration.DEBUG)
 294:       log.entering(this.getClass().getName(), "remove", alias);
 295:     boolean result = false;
 296:     for (Iterator it = entries.iterator(); it.hasNext();)
 297:       {
 298:         Entry e = (Entry) it.next();
 299:         if (e instanceof EnvelopeEntry)
 300:           {
 301:             EnvelopeEntry ee = (EnvelopeEntry) e;
 302:             result = ee.remove(alias) || result;
 303:           }
 304:         else if (e instanceof PrimitiveEntry)
 305:           {
 306:             PrimitiveEntry pe = (PrimitiveEntry) e;
 307:             if (pe.getAlias().equals(alias))
 308:               {
 309:                 it.remove();
 310:                 result = true;
 311:               }
 312:           }
 313:       }
 314:     if (result)
 315:       {
 316:         if (Configuration.DEBUG)
 317:           log.fine("State before: " + this);
 318:         payload = null;
 319:         makeAliasList();
 320:         if (Configuration.DEBUG)
 321:           log.fine("State after: " + this);
 322:       }
 323:     if (Configuration.DEBUG)
 324:       log.exiting(this.getClass().getName(), "remove", Boolean.valueOf(result));
 325:     return result;
 326:   }
 327: 
 328:   public String toString()
 329:   {
 330:     return new StringBuilder("Envelope{")
 331:         .append(super.toString())
 332:         .append(", entries=").append(entries)
 333:         .append("}")
 334:         .toString();
 335:   }
 336: 
 337:   // Protected methods.
 338:   // ------------------------------------------------------------------------
 339: 
 340:   protected void encodePayload() throws IOException
 341:   {
 342:     ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
 343:     DataOutputStream out = new DataOutputStream(bout);
 344:     for (Iterator it = entries.iterator(); it.hasNext();)
 345:       ((Entry) it.next()).encode(out);
 346:   }
 347: 
 348:   protected void setContainingEnvelope(EnvelopeEntry e)
 349:   {
 350:     if (containingEnvelope != null)
 351:       throw new IllegalArgumentException("envelopes may not be shared");
 352:     containingEnvelope = e;
 353:   }
 354: 
 355:   protected void decodeEnvelope(DataInputStream in) throws IOException
 356:   {
 357:     this.entries.clear();
 358:     while (true)
 359:       {
 360:         int type = in.read();
 361:         switch (type)
 362:           {
 363:           case EncryptedEntry.TYPE:
 364:             add(EncryptedEntry.decode(in));
 365:             break;
 366:           case PasswordEncryptedEntry.TYPE:
 367:             add(PasswordEncryptedEntry.decode(in));
 368:             break;
 369:           case PasswordAuthenticatedEntry.TYPE:
 370:             add(PasswordAuthenticatedEntry.decode(in));
 371:             break;
 372:           case AuthenticatedEntry.TYPE:
 373:             add(AuthenticatedEntry.decode(in));
 374:             break;
 375:           case CompressedEntry.TYPE:
 376:             add(CompressedEntry.decode(in));
 377:             break;
 378:           case CertificateEntry.TYPE:
 379:             add(CertificateEntry.decode(in));
 380:             break;
 381:           case PublicKeyEntry.TYPE:
 382:             add(PublicKeyEntry.decode(in));
 383:             break;
 384:           case PrivateKeyEntry.TYPE:
 385:             add(PrivateKeyEntry.decode(in));
 386:             break;
 387:           case CertPathEntry.TYPE:
 388:             add(CertPathEntry.decode(in));
 389:             break;
 390:           case BinaryDataEntry.TYPE:
 391:             add(BinaryDataEntry.decode(in));
 392:             break;
 393:           case -1:
 394:             return;
 395:           default:
 396:             throw new MalformedKeyringException("unknown type " + type);
 397:           }
 398:       }
 399:   }
 400: 
 401:   private void makeAliasList()
 402:   {
 403:     if (Configuration.DEBUG)
 404:       log.entering(this.getClass().getName(), "makeAliasList");
 405:     if (! entries.isEmpty())
 406:       {
 407:         StringBuilder buf = new StringBuilder();
 408:         String aliasOrList;
 409:         for (Iterator it = entries.iterator(); it.hasNext();)
 410:           {
 411:             Entry entry = (Entry) it.next();
 412:             aliasOrList = null;
 413:             if (entry instanceof EnvelopeEntry)
 414:               aliasOrList = ((EnvelopeEntry) entry).getAliasList();
 415:             else if (entry instanceof PrimitiveEntry)
 416:               aliasOrList = ((PrimitiveEntry) entry).getAlias();
 417:             else if (Configuration.DEBUG)
 418:               log.fine("Entry with no Alias. Ignored: " + entry);
 419:             if (aliasOrList != null)
 420:               {
 421:                 aliasOrList = aliasOrList.trim();
 422:                 if (aliasOrList.trim().length() > 0)
 423:                   {
 424:                     buf.append(aliasOrList);
 425:                     if (it.hasNext())
 426:                       buf.append(';');
 427:                   }
 428:               }
 429:           }
 430:         String aliasList = buf.toString();
 431:         properties.put("alias-list", aliasList);
 432:         if (Configuration.DEBUG)
 433:           log.fine("alias-list=[" + aliasList + "]");
 434:         if (containingEnvelope != null)
 435:           containingEnvelope.makeAliasList();
 436:       }
 437:     if (Configuration.DEBUG)
 438:       log.exiting(this.getClass().getName(), "makeAliasList");
 439:   }
 440: }