2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 package java.util.jar;
20 import java.io.IOException;
21 import java.nio.charset.Charsets;
22 import java.util.Arrays;
26 * Reads a JAR file manifest. The specification is here:
27 * http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html
30 private final byte[] buf;
34 private Attributes.Name name;
38 private final UnsafeByteSequence valueBuffer = new UnsafeByteSequence(80);
39 private int consecutiveLineBreaks = 0;
41 InitManifest(byte[] buf, Attributes main, Attributes.Name ver) throws IOException {
44 // check a version attribute
45 if (!readHeader() || (ver != null && !name.equals(ver))) {
46 throw new IOException("Missing version attribute: " + ver);
49 main.put(name, value);
50 while (readHeader()) {
51 main.put(name, value);
55 void initEntries(Map<String, Attributes> entries,
56 Map<String, Manifest.Chunk> chunks) throws IOException {
59 while (readHeader()) {
60 if (!Attributes.Name.NAME.equals(name)) {
61 throw new IOException("Entry is not named");
63 String entryNameValue = value;
65 Attributes entry = entries.get(entryNameValue);
67 entry = new Attributes(12);
70 while (readHeader()) {
71 entry.put(name, value);
75 if (chunks.get(entryNameValue) != null) {
76 // TODO A bug: there might be several verification chunks for
77 // the same name. I believe they should be used to update
78 // signature in order of appearance; there are two ways to fix
79 // this: either use a list of chunks, or decide on used
80 // signature algorithm in advance and reread the chunks while
81 // updating the signature; for now a defensive error is thrown
82 throw new IOException("A jar verifier does not support more than one entry with the same name");
84 chunks.put(entryNameValue, new Manifest.Chunk(mark, pos));
88 entries.put(entryNameValue, entry);
97 * Read a single line from the manifest buffer.
99 private boolean readHeader() throws IOException {
100 if (consecutiveLineBreaks > 1) {
101 // break a section on an empty line
102 consecutiveLineBreaks = 0;
106 consecutiveLineBreaks = 0;
108 // if the last line break is missed, the line
109 // is ignored by the reference implementation
110 return consecutiveLineBreaks > 0;
113 private void readName() throws IOException {
117 while (pos < buf.length) {
121 byte[] nameBuffer = Arrays.copyOfRange(buf, mark, pos - 1);
123 if (buf[pos++] != ' ') {
124 String name = new String(nameBuffer, Charsets.UTF_8);
125 throw new IOException(String.format("Invalid value for attribute '%s'", name));
128 name = new Attributes.Name(nameBuffer);
132 if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
133 || b == '-' || (b >= '0' && b <= '9'))) {
134 throw new IOException("Invalid byte " + b + " in attribute");
138 throw new IOException("Invalid attribute name: " +
139 Arrays.toString(Arrays.copyOfRange(buf, mark, buf.length)));
143 private void readValue() throws IOException {
144 boolean lastCr = false;
147 valueBuffer.rewind();
148 while (pos < buf.length) {
149 byte next = buf[pos++];
152 throw new IOException("NUL character in a manifest");
157 consecutiveLineBreaks++;
162 consecutiveLineBreaks++;
165 if (consecutiveLineBreaks == 1) {
166 valueBuffer.write(buf, mark, last - mark);
168 consecutiveLineBreaks = 0;
173 if (consecutiveLineBreaks >= 1) {
180 valueBuffer.write(buf, mark, last - mark);
181 value = valueBuffer.toString(Charsets.UTF_8);