18a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/* 28a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Licensed to the Apache Software Foundation (ASF) under one or more 38a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * contributor license agreements. See the NOTICE file distributed with 48a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * this work for additional information regarding copyright ownership. 58a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * The ASF licenses this file to You under the Apache License, Version 2.0 68a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * (the "License"); you may not use this file except in compliance with 78a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * the License. You may obtain a copy of the License at 88a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 98a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * http://www.apache.org/licenses/LICENSE-2.0 108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * 118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Unless required by applicable law or agreed to in writing, software 128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * distributed under the License is distributed on an "AS IS" BASIS, 138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * See the License for the specific language governing permissions and 158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * limitations under the License. 168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakpackage android.util.jar; 198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.ByteArrayOutputStream; 218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.io.IOException; 228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.nio.charset.StandardCharsets; 238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.HashMap; 248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.Map; 258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakimport java.util.jar.Attributes; 268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak/** 288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Reads a JAR file manifest. The specification is here: 298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html 308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniakclass StrictJarManifestReader { 328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // There are relatively few unique attribute names, 338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // but a manifest might have thousands of entries. 348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final HashMap<String, Attributes.Name> attributeNameCache = new HashMap<String, Attributes.Name>(); 358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final ByteArrayOutputStream valueBuffer = new ByteArrayOutputStream(80); 378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final byte[] buf; 398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private final int endOfMainSection; 418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private int pos; 438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private Attributes.Name name; 458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private String value; 478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private int consecutiveLineBreaks = 0; 498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public StrictJarManifestReader(byte[] buf, Attributes main) throws IOException { 518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.buf = buf; 528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (readHeader()) { 538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak main.put(name, value); 548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak this.endOfMainSection = pos; 568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public void readEntries(Map<String, Attributes> entries, Map<String, StrictJarManifest.Chunk> chunks) throws IOException { 598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int mark = pos; 608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (readHeader()) { 618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (!Attributes.Name.NAME.equals(name)) { 628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new IOException("Entry is not named"); 638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String entryNameValue = value; 658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak Attributes entry = entries.get(entryNameValue); 678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (entry == null) { 688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak entry = new Attributes(12); 698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (readHeader()) { 728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak entry.put(name, value); 738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (chunks != null) { 768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (chunks.get(entryNameValue) != null) { 778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // TODO A bug: there might be several verification chunks for 788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // the same name. I believe they should be used to update 798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // signature in order of appearance; there are two ways to fix 808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // this: either use a list of chunks, or decide on used 818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // signature algorithm in advance and reread the chunks while 828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // updating the signature; for now a defensive error is thrown 838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new IOException("A jar verifier does not support more than one entry with the same name"); 848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 858a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak chunks.put(entryNameValue, new StrictJarManifest.Chunk(mark, pos)); 868a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak mark = pos; 878a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 888a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 898a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak entries.put(entryNameValue, entry); 908a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 918a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 928a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 938a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak public int getEndOfMainSection() { 948a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return endOfMainSection; 958a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 968a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 978a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak /** 988a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak * Read a single line from the manifest buffer. 998a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak */ 1008a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private boolean readHeader() throws IOException { 1018a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (consecutiveLineBreaks > 1) { 1028a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // break a section on an empty line 1038a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak consecutiveLineBreaks = 0; 1048a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return false; 1058a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1068a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak readName(); 1078a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak consecutiveLineBreaks = 0; 1088a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak readValue(); 1098a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // if the last line break is missed, the line 1108a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // is ignored by the reference implementation 1118a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return consecutiveLineBreaks > 0; 1128a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1138a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1148a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private void readName() throws IOException { 1158a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int mark = pos; 1168a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1178a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (pos < buf.length) { 1188a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (buf[pos++] != ':') { 1198a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 1208a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1218a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1228a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak String nameString = new String(buf, mark, pos - mark - 1, StandardCharsets.US_ASCII); 1238a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1248a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (buf[pos++] != ' ') { 1258a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new IOException(String.format("Invalid value for attribute '%s'", nameString)); 1268a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1278a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1288a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak try { 1298a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak name = attributeNameCache.get(nameString); 1308a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (name == null) { 1318a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak name = new Attributes.Name(nameString); 1328a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak attributeNameCache.put(nameString, name); 1338a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1348a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } catch (IllegalArgumentException e) { 1358a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // new Attributes.Name() throws IllegalArgumentException but we declare IOException 1368a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new IOException(e.getMessage()); 1378a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1388a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak return; 1398a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1408a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1418a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1428a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak private void readValue() throws IOException { 1438a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak boolean lastCr = false; 1448a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int mark = pos; 1458a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak int last = pos; 1468a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak valueBuffer.reset(); 1478a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak while (pos < buf.length) { 1488a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak byte next = buf[pos++]; 1498a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak switch (next) { 1508a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak case 0: 1518a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak throw new IOException("NUL character in a manifest"); 1528a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak case '\n': 1538a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (lastCr) { 1548a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak lastCr = false; 1558a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } else { 1568a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak consecutiveLineBreaks++; 1578a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1588a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 1598a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak case '\r': 1608a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak lastCr = true; 1618a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak consecutiveLineBreaks++; 1628a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 1638a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak case ' ': 1648a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (consecutiveLineBreaks == 1) { 1658a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak valueBuffer.write(buf, mark, last - mark); 1668a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak mark = pos; 1678a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak consecutiveLineBreaks = 0; 1688a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak continue; 1698a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1708a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1718a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1728a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak if (consecutiveLineBreaks >= 1) { 1738a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak pos--; 1748a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak break; 1758a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1768a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak last = pos; 1778a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1788a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak 1798a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak valueBuffer.write(buf, mark, last - mark); 1808a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // A bit frustrating that that Charset.forName will be called 1818a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak // again. 1828a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak value = valueBuffer.toString(StandardCharsets.UTF_8.name()); 1838a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak } 1848a7c1606d88873c5a1b5764c16cb046b6f2275b2Przemyslaw Szczepaniak} 185