Index: java/net/deterlab/abac/Context.java
===================================================================
--- java/net/deterlab/abac/Context.java	(revision ad24705156d10e68c900c05fab3769ecd41aa766)
+++ java/net/deterlab/abac/Context.java	(revision 18d9cfae93520637cb9678e68d2c4bb00ffc3800)
@@ -547,4 +547,26 @@
 
     /**
+     * Read the current ZipEntry's bytes from z.  Tedious because there's no
+     * way to reliably tell how big the entry is, so we have to rely on a
+     * simple expanding array read of the bytes.
+     */
+    protected byte[] readCurrentZipEntry(ZipInputStream z) throws IOException {
+	final int bsize = 4096;
+	byte[] buf = new byte[bsize];
+	byte[] rv = new byte[0];
+	int r = 0;
+
+	// z.read returns -1 at the end of entry
+	while ((r = z.read(buf, 0, bsize)) != -1 ) {
+	    byte[] b = new byte[rv.length + r];
+
+	    System.arraycopy(rv, 0, b, 0, rv.length);
+	    System.arraycopy(buf, 0, b, rv.length, r);
+	    rv = b;
+	}
+	return rv;
+    }
+
+    /**
      * Import a zip file.  First import all the identities
      * (pem), then the credentials (der) into the credential graph then any
@@ -552,5 +574,5 @@
      * PEM files are put in there.  If errors is not null, errors reading files
      * are added indexed by filename.  This is a jabac extension.
-     * @param zf the File to read
+     * @param s the InputStream to read
      * @param keys a Collection into which to insert unmatched keys
      * @param errors a Map from entry name to generated exception
@@ -558,18 +580,19 @@
      *			   returned in the errors parameter.
      */
-    public void load_zip_file(File zf, Collection<KeyPair> keys, 
+    public void load_zip(InputStream s, Collection<KeyPair> keys, 
 	    Map<String, Exception> errors) throws IOException {
-	Vector<ZipEntry> derEntries = new Vector<ZipEntry>();
+	Map<String, byte[]> derEntries = new HashMap<String, byte[]>();
 	Map<String, Identity> ids = new TreeMap<String, Identity>();
 	Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
-
-	ZipFile z = new ZipFile(zf);
-
-	for (Enumeration<? extends ZipEntry> ze = z.entries(); 
-		ze.hasMoreElements();) {
-	    ZipEntry  f = ze.nextElement();
+	int entries = 0;
+
+	ZipInputStream z = new ZipInputStream(s);
+
+	for (ZipEntry ze = z.getNextEntry(); ze != null; ze = z.getNextEntry()){
 	    try {
+		entries++;
+		byte[] buf = readCurrentZipEntry(z);
 		PEMReader r = new PEMReader(
-			new InputStreamReader(z.getInputStream(f)));
+			new InputStreamReader(new ByteArrayInputStream(buf)));
 		Object o = readPEM(r);
 
@@ -605,35 +628,85 @@
 		else {
 		    // Not a PEM file
-		    derEntries.add(f);
+		    derEntries.put(ze.getName(),buf);
 		    continue;
 		}
 	    }
 	    catch (Exception e ) {
-		if (errors != null ) errors.put(f.getName(), e);
-	    }
-	}
-
-	for ( ZipEntry f : derEntries ) {
+		if (errors != null ) errors.put(ze.getName(), e);
+	    }
+	}
+
+	for ( String k: derEntries.keySet() ) {
 	    try {
-		add_credential(new Credential(z.getInputStream(f),
+		add_credential(new Credential(
+			    new ByteArrayInputStream(derEntries.get(k)),
 			    m_identities));
 	    }
 	    catch (Exception e ) {
-		if (errors != null ) errors.put(f.getName(), e);
-	    }
-	}
-    }
-    /**
-     * Equivalent to load_zip_file(d, null, null).
+		if (errors != null ) errors.put(k, e);
+	    }
+	}
+
+	if (entries == 0) 
+	    throw new IOException("Not a ZIP file (or empty ZIP file)");
+    }
+    /**
+     * Equivalent to load_zip(s, null, null).
+     * @param s the InputStream to read
+     * @throws IOException if the file is unreadable. To see per-entry
+     *			    exceptions use a signature with the errors parameter
+     */
+    public void load_zip(InputStream s) 
+	    throws IOException {
+	load_zip(s, null, null);
+    }
+    /**
+     * Equivalent to load_zip(s, null, errors).
+     * @param s the InputStream to read
+     * @param errors a Map from entry name to generated exception
+     * @throws IOException if the file is unreadable.  Per entry exceptions are
+     *			   returned in the errors parameter.
+     */
+    public void load_zip(InputStream s, 
+	    Map<String, Exception> errors) throws IOException {
+	load_zip(s, null, errors);
+    }
+    /**
+     * Equivalent to load_zip(s, keys, null).
+     * @param s the InputStream to read
+     * @param keys a Collection into which to insert unmatched keys
+     * @throws IOException if the file is unreadable. To see per-entry
+     *			    exceptions use a signature with the errors parameter
+     */
+    public void load_zip(InputStream s, 
+	    Collection<KeyPair> keys) throws IOException {
+	load_zip(s, keys, null);
+    }
+
+    /**
+     * Loads a zip file.  Equivalent to 
+     * load_zip(new FileInputStream(zf), keys, errors).
+     * @param zf the File to read
+     * @param keys a Collection into which to insert unmatched keys
+     * @param errors a Map from entry name to generated exception
+     * @throws IOException if the file is unreadable.  Per entry exceptions are
+     *			   returned in the errors parameter.
+     */
+    public void load_zip(File zf, Collection<KeyPair> keys, 
+	    Map<String, Exception> errors) throws IOException {
+	load_zip(new FileInputStream(zf), keys, errors);
+    }
+    /**
+     * Equivalent to load_zip(d, null, null).
      * @param d the File to read
      * @throws IOException if the file is unreadable. To see per-entry
      *			    exceptions use a signature with the errors parameter
      */
-    public void load_zip_file(File d) 
+    public void load_zip(File d) 
 	    throws IOException {
-	load_zip_file(d, null, null);
-    }
-    /**
-     * Equivalent to load_zip_file(d, null, errors).
+	load_zip(d, null, null);
+    }
+    /**
+     * Equivalent to load_zip(d, null, errors).
      * @param d the File to read
      * @param errors a Map from entry name to generated exception
@@ -641,10 +714,10 @@
      *			   returned in the errors parameter.
      */
-    public void load_zip_file(File d, 
+    public void load_zip(File d, 
 	    Map<String, Exception> errors) throws IOException {
-	load_zip_file(d, null, errors);
-    }
-    /**
-     * Equivalent to load_zip_file(d, keys, null).
+	load_zip(d, null, errors);
+    }
+    /**
+     * Equivalent to load_zip(d, keys, null).
      * @param d the File to read
      * @param keys a Collection into which to insert unmatched keys
@@ -652,7 +725,7 @@
      *			    exceptions use a signature with the errors parameter
      */
-    public void load_zip_file(File d, 
+    public void load_zip(File d, 
 	    Collection<KeyPair> keys) throws IOException {
-	load_zip_file(d, keys, null);
+	load_zip(d, keys, null);
     }
 
@@ -821,13 +894,14 @@
      * Spaces are not significant around the arrow and the tail can be as long
      * as needed.
-     * @param f the File to load
+     * @param s the InputStream to load
      * @param errors a Map of lines to errors
-     * @throws IOException if there is an error getting the file open
-     */
-    public void load_rt0_file(File f, Map<String, Exception> errors) 
+     * @throws IOException if there is an error getting the file open or in
+     * format
+     */
+    public void load_rt0(InputStream s) 
 	    throws IOException {
 	Pattern comment = Pattern.compile("(^\\s*#|^\\s*$)");
-	Pattern rule = Pattern.compile("([\\w\\.]+)\\s*<-+\\s*([\\w\\.\\&]+)");
-	LineNumberReader r = new LineNumberReader(new FileReader(f));
+	Pattern rule = Pattern.compile("([\\w\\.]+)\\s*<-+\\s*(.+)");
+	LineNumberReader r = new LineNumberReader(new InputStreamReader(s));
 	String line = null;
 
@@ -840,17 +914,16 @@
 		add_credential(new Credential(new Role(rm.group(1)), 
 			    new Role(rm.group(2))));
-	    else if (errors != null) 
-		errors.put("Line " + r.getLineNumber(), 
-			new RuntimeException("Unexpected format"));
-	}
-    }
-
-    /**
-     * Equivalent to load_rt0_file(d, null).
+	    else 
+		throw new RuntimeException("Unexpected format: line " + 
+			r.getLineNumber());
+	}
+    }
+    /**
+     * Equivalent to load_rt0(new FileInputStream(f)
      * @param f the File to load
      * @throws IOException if there is an error getting the file open
      */
-    public void load_rt0_file(File f) throws IOException {
-	load_rt0_file(f, null);
+    public void load_rt0(File f) throws IOException {
+	load_rt0(new FileInputStream(f));
     }
 	
@@ -860,5 +933,5 @@
      * entry for each credential or identity.  The files are all zipped in a
      * directory derived from the filename.
-     * @param f the File to write
+     * @param s the OutputStream to write
      * @param allIDs a boolean, if true write certificates for all Identities,
      * whether used in signing a credential or not.
@@ -867,9 +940,9 @@
      * @throws IOException if there is a problem writing the file.
      */
-    public void write_zip_file(File f, boolean allIDs, boolean withPrivateKeys) 
-	    throws IOException {
-	ZipOutputStream z = new ZipOutputStream(new FileOutputStream(f));
+    public void write_zip(OutputStream s, boolean allIDs, 
+	    boolean withPrivateKeys) throws IOException {
+	ZipOutputStream z = new ZipOutputStream(s);
 	Set<Identity> ids = allIDs ?  m_identities : new TreeSet<Identity>();
-	String baseDir = f.getName();
+	String baseDir = "creds";
 	int idx = baseDir.indexOf('.');
 
@@ -895,4 +968,18 @@
 	}
 	z.close();
+    }
+    /**
+     * Equivalent to
+     * write_zip(new FileOutputStream(f), allIDs, withPrivateKeys).
+     * @param f the File to write
+     * @param allIDs a boolean, if true write certificates for all Identities,
+     * whether used in signing a credential or not.
+     * @param withPrivateKeys a boolean, if true write the Identities as PEM
+     * file containing both the certificate and the private keys.
+     * @throws IOException if there is a problem writing the file.
+     */
+    public void write_zip(File f, boolean allIDs, boolean withPrivateKeys) 
+	    throws IOException {
+	write_zip(new FileOutputStream(f), allIDs, withPrivateKeys);
     }
 
