/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.tools.jar;

import java.io.*;
import java.util.*;
import java.security.*;

import sun.net.www.MessageHeader;
import java.util.Base64;

This is OBSOLETE. DO NOT USE THIS. Use java.util.jar.Manifest instead. It has to stay here because some apps (namely HJ and HJV) call directly into it.
Author:David Brown, Benjamin Renaud
/** * This is OBSOLETE. DO NOT USE THIS. Use java.util.jar.Manifest * instead. It has to stay here because some apps (namely HJ and HJV) * call directly into it. * * @author David Brown * @author Benjamin Renaud */
public class Manifest { /* list of headers that all pertain to a particular * file in the archive */ private Vector<MessageHeader> entries = new Vector<>(); private byte[] tmpbuf = new byte[512]; /* a hashtable of entries, for fast lookup */ private Hashtable<String, MessageHeader> tableEntries = new Hashtable<>(); static final String[] hashes = {"SHA"}; static final boolean debug = false; static final String VERSION = "1.0"; static final void debug(String s) { if (debug) System.out.println("man> " + s); } public Manifest() {} public Manifest(byte[] bytes) throws IOException { this(new ByteArrayInputStream(bytes), false); } public Manifest(InputStream is) throws IOException { this(is, true); }
Parse a manifest from a stream, optionally computing hashes for the files.
/** * Parse a manifest from a stream, optionally computing hashes * for the files. */
public Manifest(InputStream is, boolean compute) throws IOException { if (!is.markSupported()) { is = new BufferedInputStream(is); } /* do not rely on available() here! */ while (true) { is.mark(1); if (is.read() == -1) { // EOF break; } is.reset(); MessageHeader m = new MessageHeader(is); if (compute) { doHashes(m); } addEntry(m); } } /* recursively generate manifests from directory tree */ public Manifest(String[] files) throws IOException { MessageHeader globals = new MessageHeader(); globals.add("Manifest-Version", VERSION); String jdkVersion = System.getProperty("java.version"); globals.add("Created-By", "Manifest JDK "+jdkVersion); addEntry(globals); addFiles(null, files); } public void addEntry(MessageHeader entry) { entries.addElement(entry); String name = entry.findValue("Name"); debug("addEntry for name: "+name); if (name != null) { tableEntries.put(name, entry); } } public MessageHeader getEntry(String name) { return tableEntries.get(name); } public MessageHeader entryAt(int i) { return entries.elementAt(i); } public Enumeration<MessageHeader> entries() { return entries.elements(); } public void addFiles(File dir, String[] files) throws IOException { if (files == null) return; for (int i = 0; i < files.length; i++) { File file; if (dir == null) { file = new File(files[i]); } else { file = new File(dir, files[i]); } if (file.isDirectory()) { addFiles(file, file.list()); } else { addFile(file); } } }
File names are represented internally using "/"; they are converted to the local format for anything else
/** * File names are represented internally using "/"; * they are converted to the local format for anything else */
private final String stdToLocal(String name) { return name.replace('/', java.io.File.separatorChar); } private final String localToStd(String name) { name = name.replace(java.io.File.separatorChar, '/'); if (name.startsWith("./")) name = name.substring(2); else if (name.startsWith("/")) name = name.substring(1); return name; } public void addFile(File f) throws IOException { String stdName = localToStd(f.getPath()); if (tableEntries.get(stdName) == null) { MessageHeader mh = new MessageHeader(); mh.add("Name", stdName); addEntry(mh); } } public void doHashes(MessageHeader mh) throws IOException { // If unnamed or is a directory return immediately String name = mh.findValue("Name"); if (name == null || name.endsWith("/")) { return; } /* compute hashes, write over any other "Hash-Algorithms" (?) */ for (int j = 0; j < hashes.length; ++j) { InputStream is = new FileInputStream(stdToLocal(name)); try { MessageDigest dig = MessageDigest.getInstance(hashes[j]); int len; while ((len = is.read(tmpbuf, 0, tmpbuf.length)) != -1) { dig.update(tmpbuf, 0, len); } mh.set(hashes[j] + "-Digest", Base64.getMimeEncoder().encodeToString(dig.digest())); } catch (NoSuchAlgorithmException e) { throw new JarException("Digest algorithm " + hashes[j] + " not available."); } finally { is.close(); } } } /* Add a manifest file at current position in a stream */ public void stream(OutputStream os) throws IOException { PrintStream ps; if (os instanceof PrintStream) { ps = (PrintStream) os; } else { ps = new PrintStream(os); } /* the first header in the file should be the global one. * It should say "Manifest-Version: x.x"; if not add it */ MessageHeader globals = entries.elementAt(0); if (globals.findValue("Manifest-Version") == null) { /* Assume this is a user-defined manifest. If it has a Name: <..> * field, then it is not global, in which case we just add our own * global Manifest-version: <version> * If the first MessageHeader has no Name: <..>, we assume it * is a global header and so prepend Manifest to it. */ String jdkVersion = System.getProperty("java.version"); if (globals.findValue("Name") == null) { globals.prepend("Manifest-Version", VERSION); globals.add("Created-By", "Manifest JDK "+jdkVersion); } else { ps.print("Manifest-Version: "+VERSION+"\r\n"+ "Created-By: "+jdkVersion+"\r\n\r\n"); } ps.flush(); } globals.print(ps); for (int i = 1; i < entries.size(); ++i) { MessageHeader mh = entries.elementAt(i); mh.print(ps); } } public static boolean isManifestName(String name) { // remove leading / if (name.charAt(0) == '/') { name = name.substring(1, name.length()); } // case insensitive name = name.toUpperCase(); if (name.equals("META-INF/MANIFEST.MF")) { return true; } return false; } }