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 EnvelopeEntry(int type, Properties properties)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 EnvelopeEntry(int type)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 */ add(Entry entry)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 */ containsAlias(String alias)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 */ containsEntry(Entry entry)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 */ getEntries()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 */ get(String alias)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 */ getAliasList()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 */ remove(Entry entry)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 */ remove(String alias)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 toString()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 encodePayload()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 setContainingEnvelope(EnvelopeEntry e)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 decodeEnvelope(DataInputStream in)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 makeAliasList()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 } 441