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