1 /*
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 
18 package android.util.jar;
19 
20 import android.annotation.Nullable;
21 
22 import libcore.io.Streams;
23 
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.nio.ByteBuffer;
28 import java.nio.CharBuffer;
29 import java.nio.charset.CharsetEncoder;
30 import java.nio.charset.CoderResult;
31 import java.nio.charset.StandardCharsets;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.Map;
35 import java.util.jar.Attributes;
36 
37 /**
38  * The {@code StrictJarManifest} class is used to obtain attribute information for a
39  * {@code StrictJarFile} and its entries.
40  *
41  * @hide
42  */
43 public class StrictJarManifest implements Cloneable {
44     static final int LINE_LENGTH_LIMIT = 72;
45 
46     private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' };
47 
48     private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
49 
50     /** The attribute name "Name". */
51     static final Attributes.Name ATTRIBUTE_NAME_NAME = new Attributes.Name("Name");
52 
53     private final Attributes mainAttributes;
54     private final HashMap<String, Attributes> entries;
55 
56     static final class Chunk {
57         final int start;
58         final int end;
59 
Chunk(int start, int end)60         Chunk(int start, int end) {
61             this.start = start;
62             this.end = end;
63         }
64     }
65 
66     private HashMap<String, Chunk> chunks;
67 
68     /**
69      * The end of the main attributes section in the manifest is needed in
70      * verification.
71      */
72     private int mainEnd;
73 
74     /**
75      * Creates a new {@code StrictJarManifest} instance.
76      */
StrictJarManifest()77     public StrictJarManifest() {
78         entries = new HashMap<String, Attributes>();
79         mainAttributes = new Attributes();
80     }
81 
82     /**
83      * Creates a new {@code StrictJarManifest} instance using the attributes obtained
84      * from the input stream.
85      *
86      * @param is
87      *            {@code InputStream} to parse for attributes.
88      * @throws IOException
89      *             if an IO error occurs while creating this {@code StrictJarManifest}
90      */
StrictJarManifest(InputStream is)91     public StrictJarManifest(InputStream is) throws IOException {
92         this();
93         read(Streams.readFully(is));
94     }
95 
96     /**
97      * Creates a new {@code StrictJarManifest} instance. The new instance will have the
98      * same attributes as those found in the parameter {@code StrictJarManifest}.
99      *
100      * @param man
101      *            {@code StrictJarManifest} instance to obtain attributes from.
102      */
103     @SuppressWarnings("unchecked")
StrictJarManifest(StrictJarManifest man)104     public StrictJarManifest(StrictJarManifest man) {
105         mainAttributes = (Attributes) man.mainAttributes.clone();
106         entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
107                 .getEntries()).clone();
108     }
109 
StrictJarManifest(byte[] manifestBytes, boolean readChunks)110     StrictJarManifest(byte[] manifestBytes, boolean readChunks) throws IOException {
111         this();
112         if (readChunks) {
113             chunks = new HashMap<String, Chunk>();
114         }
115         read(manifestBytes);
116     }
117 
118     /**
119      * Resets the both the main attributes as well as the entry attributes
120      * associated with this {@code StrictJarManifest}.
121      */
clear()122     public void clear() {
123         entries.clear();
124         mainAttributes.clear();
125     }
126 
127     /**
128      * Returns the {@code Attributes} associated with the parameter entry
129      * {@code name}.
130      *
131      * @param name
132      *            the name of the entry to obtain {@code Attributes} from.
133      * @return the Attributes for the entry or {@code null} if the entry does
134      *         not exist.
135      */
getAttributes(String name)136     public Attributes getAttributes(String name) {
137         return getEntries().get(name);
138     }
139 
140     /**
141      * Returns a map containing the {@code Attributes} for each entry in the
142      * {@code StrictJarManifest}.
143      *
144      * @return the map of entry attributes.
145      */
getEntries()146     public Map<String, Attributes> getEntries() {
147         return entries;
148     }
149 
150     /**
151      * Returns the main {@code Attributes} of the {@code JarFile}.
152      *
153      * @return main {@code Attributes} associated with the source {@code
154      *         JarFile}.
155      */
getMainAttributes()156     public Attributes getMainAttributes() {
157         return mainAttributes;
158     }
159 
160     /**
161      * Creates a copy of this {@code StrictJarManifest}. The returned {@code StrictJarManifest}
162      * will equal the {@code StrictJarManifest} from which it was cloned.
163      *
164      * @return a copy of this instance.
165      */
166     @Override
clone()167     public Object clone() {
168         return new StrictJarManifest(this);
169     }
170 
171     /**
172      * Writes this {@code StrictJarManifest}'s name/attributes pairs to the given {@code OutputStream}.
173      * The {@code MANIFEST_VERSION} or {@code SIGNATURE_VERSION} attribute must be set before
174      * calling this method, or no attributes will be written.
175      *
176      * @throws IOException
177      *             If an error occurs writing the {@code StrictJarManifest}.
178      */
write(OutputStream os)179     public void write(OutputStream os) throws IOException {
180         write(this, os);
181     }
182 
183     /**
184      * Merges name/attribute pairs read from the input stream {@code is} into this manifest.
185      *
186      * @param is
187      *            The {@code InputStream} to read from.
188      * @throws IOException
189      *             If an error occurs reading the manifest.
190      */
read(InputStream is)191     public void read(InputStream is) throws IOException {
192         read(Streams.readFullyNoClose(is));
193     }
194 
read(byte[] buf)195     private void read(byte[] buf) throws IOException {
196         if (buf.length == 0) {
197             return;
198         }
199 
200         StrictJarManifestReader im = new StrictJarManifestReader(buf, mainAttributes);
201         mainEnd = im.getEndOfMainSection();
202         im.readEntries(entries, chunks);
203     }
204 
205     /**
206      * Returns the hash code for this instance.
207      *
208      * @return this {@code StrictJarManifest}'s hashCode.
209      */
210     @Override
hashCode()211     public int hashCode() {
212         return mainAttributes.hashCode() ^ getEntries().hashCode();
213     }
214 
215     /**
216      * Determines if the receiver is equal to the parameter object. Two {@code
217      * StrictJarManifest}s are equal if they have identical main attributes as well as
218      * identical entry attributes.
219      *
220      * @param o
221      *            the object to compare against.
222      * @return {@code true} if the manifests are equal, {@code false} otherwise
223      */
224     @Override
equals(@ullable Object o)225     public boolean equals(@Nullable Object o) {
226         if (o == null) {
227             return false;
228         }
229         if (o.getClass() != this.getClass()) {
230             return false;
231         }
232         if (!mainAttributes.equals(((StrictJarManifest) o).mainAttributes)) {
233             return false;
234         }
235         return getEntries().equals(((StrictJarManifest) o).getEntries());
236     }
237 
getChunk(String name)238     Chunk getChunk(String name) {
239         return chunks.get(name);
240     }
241 
removeChunks()242     void removeChunks() {
243         chunks = null;
244     }
245 
getMainAttributesEnd()246     int getMainAttributesEnd() {
247         return mainEnd;
248     }
249 
250     /**
251      * Writes out the attribute information of the specified manifest to the
252      * specified {@code OutputStream}
253      *
254      * @param manifest
255      *            the manifest to write out.
256      * @param out
257      *            The {@code OutputStream} to write to.
258      * @throws IOException
259      *             If an error occurs writing the {@code StrictJarManifest}.
260      */
write(StrictJarManifest manifest, OutputStream out)261     static void write(StrictJarManifest manifest, OutputStream out) throws IOException {
262         CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
263         ByteBuffer buffer = ByteBuffer.allocate(LINE_LENGTH_LIMIT);
264 
265         Attributes.Name versionName = Attributes.Name.MANIFEST_VERSION;
266         String version = manifest.mainAttributes.getValue(versionName);
267         if (version == null) {
268             versionName = Attributes.Name.SIGNATURE_VERSION;
269             version = manifest.mainAttributes.getValue(versionName);
270         }
271         if (version != null) {
272             writeEntry(out, versionName, version, encoder, buffer);
273             Iterator<?> entries = manifest.mainAttributes.keySet().iterator();
274             while (entries.hasNext()) {
275                 Attributes.Name name = (Attributes.Name) entries.next();
276                 if (!name.equals(versionName)) {
277                     writeEntry(out, name, manifest.mainAttributes.getValue(name), encoder, buffer);
278                 }
279             }
280         }
281         out.write(LINE_SEPARATOR);
282         Iterator<String> i = manifest.getEntries().keySet().iterator();
283         while (i.hasNext()) {
284             String key = i.next();
285             writeEntry(out, ATTRIBUTE_NAME_NAME, key, encoder, buffer);
286             Attributes attributes = manifest.entries.get(key);
287             Iterator<?> entries = attributes.keySet().iterator();
288             while (entries.hasNext()) {
289                 Attributes.Name name = (Attributes.Name) entries.next();
290                 writeEntry(out, name, attributes.getValue(name), encoder, buffer);
291             }
292             out.write(LINE_SEPARATOR);
293         }
294     }
295 
writeEntry(OutputStream os, Attributes.Name name, String value, CharsetEncoder encoder, ByteBuffer bBuf)296     private static void writeEntry(OutputStream os, Attributes.Name name,
297             String value, CharsetEncoder encoder, ByteBuffer bBuf) throws IOException {
298         String nameString = name.toString();
299         os.write(nameString.getBytes(StandardCharsets.US_ASCII));
300         os.write(VALUE_SEPARATOR);
301 
302         encoder.reset();
303         bBuf.clear().limit(LINE_LENGTH_LIMIT - nameString.length() - 2);
304 
305         CharBuffer cBuf = CharBuffer.wrap(value);
306 
307         while (true) {
308             CoderResult r = encoder.encode(cBuf, bBuf, true);
309             if (CoderResult.UNDERFLOW == r) {
310                 r = encoder.flush(bBuf);
311             }
312             os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position());
313             os.write(LINE_SEPARATOR);
314             if (CoderResult.UNDERFLOW == r) {
315                 break;
316             }
317             os.write(' ');
318             bBuf.clear().limit(LINE_LENGTH_LIMIT - 1);
319         }
320     }
321 }
322