wangzhibo
4 天以前 ae6f40460dcd56af6c5f60ba52c883854c3bac55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*  Copyright (C) 2022-2024 José Rebelo, MPeter
 
    This file is part of Gadgetbridge.
 
    Gadgetbridge is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    Gadgetbridge 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 Affero General Public License for more details.
 
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
 
/**
 * Utility class for recognition and reading of ZIP archives.
 */
public class GBZipFile {
    private static final Logger LOG = LoggerFactory.getLogger(GBZipFile.class);
    public static final byte[] ZIP_HEADER = new byte[]{
        0x50, 0x4B, 0x03, 0x04
    };
 
    private final byte[] zipBytes;
 
    /**
     * Open ZIP file from byte array already in memory.
     * @param zipBytes data to handle as a ZIP file.
     */
    public GBZipFile(byte[] zipBytes) {
        this.zipBytes = zipBytes;
    }
 
    /**
     * Open ZIP file from InputStream.<br>
     * This will read the entire file into memory at once.
     * @param inputStream data to handle as a ZIP file.
     */
    public GBZipFile(InputStream inputStream) throws IOException {
        this.zipBytes = readAllBytes(inputStream);
    }
 
    /**
     * Checks if data resembles a ZIP file.<br>
     * The check is not infallible: it may report self-extracting or other exotic ZIP archives as not a ZIP file, and it may report a corrupted ZIP file as a ZIP file.
     * @param data The data to check.
     * @return Whether data resembles a ZIP file.
     */
    public static boolean isZipFile(byte[] data) {
        return ArrayUtils.equals(data, ZIP_HEADER, 0);
    }
 
    /**
     * Reads the contents of file at path into a byte array.
     * @param path Path of the file in the ZIP file.
     * @return byte array contatining the contents of the requested file.
     * @throws ZipFileException If the specified path does not exist or references a directory, or if some other I/O error occurs. In other words, if return value would otherwise be null.
     */
    public byte[] getFileFromZip(final String path) throws ZipFileException {
        try (InputStream is = new ByteArrayInputStream(zipBytes); ZipInputStream zipInputStream = new ZipInputStream(is)) {
            ZipEntry zipEntry;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                if (!zipEntry.getName().equals(path)) continue; // TODO: is this always a path? The documentation is very vague.
 
                if (zipEntry.isDirectory()) {
                    throw new ZipFileException(String.format("Path in ZIP file is a directory: %s", path));
                }
 
                return readAllBytes(zipInputStream);
            }
 
            throw new ZipFileException(String.format("Path in ZIP file was not found: %s", path));
 
        } catch (ZipException e) {
            throw new ZipFileException("The ZIP file might be corrupted", e);
        } catch (IOException e) {
            throw new ZipFileException("General IO error", e);
        }
    }
 
    public boolean fileExists(final String path) throws ZipFileException {
        try (InputStream is = new ByteArrayInputStream(zipBytes); ZipInputStream zipInputStream = new ZipInputStream(is)) {
            ZipEntry zipEntry;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                if (!zipEntry.getName().equals(path)) {
                    continue;
                }
 
                if (zipEntry.isDirectory()) {
                    return false;
                }
 
                return true;
            }
 
            return false;
        } catch (final ZipException e) {
            throw new ZipFileException("The ZIP file might be corrupted", e);
        } catch (final IOException e) {
            throw new ZipFileException("General IO error", e);
        }
    }
 
    public static byte[] readAllBytes(final InputStream is) throws IOException {
        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 
        int n;
        byte[] buf = new byte[16384];
 
        while ((n = is.read(buf, 0, buf.length)) != -1) {
            buffer.write(buf, 0, n);
        }
 
        return buffer.toByteArray();
    }
}