@onekeyfe/react-native-range-downloader 3.0.79 → 3.0.81-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReactNativeRangeDownloader.podspec +8 -0
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt +284 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactStore.kt +842 -0
- package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/ReactNativeRangeDownloader.kt +148 -0
- package/android/src/test/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRulesTest.kt +75 -0
- package/ios/FirmwareArchiveMinizipBridge.h +33 -0
- package/ios/FirmwareArchiveMinizipBridge.mm +213 -0
- package/ios/FirmwareArtifactStore.swift +1276 -0
- package/ios/FirmwareBackgroundSessionEventRouter.swift +17 -0
- package/ios/ReactNativeRangeDownloader.swift +638 -6
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts +95 -1
- package/lib/typescript/src/ReactNativeRangeDownloader.nitro.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JDownloadChannel.hpp +3 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveExpectedEntry.hpp +69 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeParams.hpp +86 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializeResult.hpp +80 -0
- package/nitrogen/generated/android/c++/JFirmwareArchiveMaterializedArtifact.hpp +63 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactCancelParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactCapabilities.hpp +88 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactDownloadParams.hpp +98 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLease.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseCreateParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReconcileParams.hpp +76 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseReleaseParams.hpp +61 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactLeaseRetainParams.hpp +61 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReaderCloseParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReaderInfo.hpp +61 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReaderReadParams.hpp +65 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactReceipt.hpp +65 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactRefParams.hpp +57 -0
- package/nitrogen/generated/android/c++/JFirmwareArtifactSweepResult.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.cpp +267 -0
- package/nitrogen/generated/android/c++/JHybridReactNativeRangeDownloaderSpec.hpp +13 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/DownloadChannel.kt +2 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveExpectedEntry.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeParams.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializeResult.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveMaterializedArtifact.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCancelParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactCapabilities.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactDownloadParams.kt +68 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLease.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseCreateParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReconcileParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseReleaseParams.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactLeaseRetainParams.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderCloseParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderInfo.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReaderReadParams.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactReceipt.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactRefParams.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/FirmwareArtifactSweepResult.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/reactnativerangedownloader/HybridReactNativeRangeDownloaderSpec.kt +53 -0
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.cpp +48 -0
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Bridge.hpp +329 -0
- package/nitrogen/generated/ios/ReactNativeRangeDownloader-Swift-Cxx-Umbrella.hpp +56 -0
- package/nitrogen/generated/ios/c++/HybridReactNativeRangeDownloaderSpecSwift.hpp +163 -0
- package/nitrogen/generated/ios/swift/DownloadChannel.swift +4 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveExpectedEntry.swift +69 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeParams.swift +70 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializeResult.swift +48 -0
- package/nitrogen/generated/ios/swift/FirmwareArchiveMaterializedArtifact.swift +47 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactCancelParams.swift +36 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactCapabilities.swift +81 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactDownloadParams.swift +177 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLease.swift +36 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseCreateParams.swift +36 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReconcileParams.swift +48 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseReleaseParams.swift +47 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactLeaseRetainParams.swift +47 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReaderCloseParams.swift +36 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReaderInfo.swift +47 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReaderReadParams.swift +58 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactReceipt.swift +58 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactRefParams.swift +36 -0
- package/nitrogen/generated/ios/swift/FirmwareArtifactSweepResult.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArchiveMaterializeResult.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactLease.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReaderInfo.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactReceipt.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_FirmwareArtifactSweepResult.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_ArrayBuffer_.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec.swift +13 -0
- package/nitrogen/generated/ios/swift/HybridReactNativeRangeDownloaderSpec_cxx.swift +240 -0
- package/nitrogen/generated/shared/c++/DownloadChannel.hpp +4 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveExpectedEntry.hpp +87 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeParams.hpp +86 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializeResult.hpp +77 -0
- package/nitrogen/generated/shared/c++/FirmwareArchiveMaterializedArtifact.hpp +81 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactCancelParams.hpp +75 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactCapabilities.hpp +88 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactDownloadParams.hpp +116 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLease.hpp +75 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseCreateParams.hpp +75 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReconcileParams.hpp +76 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseReleaseParams.hpp +79 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactLeaseRetainParams.hpp +79 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReaderCloseParams.hpp +75 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReaderInfo.hpp +79 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReaderReadParams.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactReceipt.hpp +83 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactRefParams.hpp +75 -0
- package/nitrogen/generated/shared/c++/FirmwareArtifactSweepResult.hpp +79 -0
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.cpp +13 -0
- package/nitrogen/generated/shared/c++/HybridReactNativeRangeDownloaderSpec.hpp +62 -0
- package/package.json +4 -2
- package/src/ReactNativeRangeDownloader.nitro.ts +134 -1
- package/src/index.tsx +3 -1
|
@@ -15,6 +15,7 @@ Pod::Spec.new do |s|
|
|
|
15
15
|
|
|
16
16
|
s.source_files = [
|
|
17
17
|
"ios/**/*.{swift}",
|
|
18
|
+
"ios/**/*.{h}",
|
|
18
19
|
"ios/**/*.{m,mm}",
|
|
19
20
|
"cpp/**/*.{hpp,cpp}",
|
|
20
21
|
]
|
|
@@ -22,6 +23,13 @@ Pod::Spec.new do |s|
|
|
|
22
23
|
s.dependency 'React-jsi'
|
|
23
24
|
s.dependency 'React-callinvoker'
|
|
24
25
|
s.dependency 'ReactNativeNativeLogger'
|
|
26
|
+
s.dependency 'SniConnect', package["version"]
|
|
27
|
+
s.public_header_files = "ios/FirmwareArchiveMinizipBridge.h"
|
|
28
|
+
s.pod_target_xcconfig = {
|
|
29
|
+
'HEADER_SEARCH_PATHS' => '"$(PODS_ROOT)/SSZipArchive/SSZipArchive/minizip"',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
s.dependency 'SSZipArchive', '>= 2.5.4'
|
|
25
33
|
|
|
26
34
|
load 'nitrogen/generated/ios/ReactNativeRangeDownloader+autolinking.rb'
|
|
27
35
|
add_nitrogen_files(s)
|
package/android/build.gradle
CHANGED
|
@@ -127,6 +127,7 @@ dependencies {
|
|
|
127
127
|
implementation project(":react-native-nitro-modules")
|
|
128
128
|
|
|
129
129
|
implementation project(":onekeyfe_react-native-native-logger")
|
|
130
|
+
implementation project(":onekeyfe_react-native-sni-connect")
|
|
130
131
|
|
|
131
132
|
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
|
132
133
|
|
package/android/src/main/java/com/margelo/nitro/reactnativerangedownloader/FirmwareArchiveRules.kt
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
package com.margelo.nitro.reactnativerangedownloader
|
|
2
|
+
|
|
3
|
+
import java.io.File
|
|
4
|
+
import java.io.RandomAccessFile
|
|
5
|
+
import java.nio.ByteBuffer
|
|
6
|
+
import java.nio.ByteOrder
|
|
7
|
+
import java.nio.charset.CodingErrorAction
|
|
8
|
+
import java.nio.charset.StandardCharsets
|
|
9
|
+
import java.text.Normalizer
|
|
10
|
+
import java.util.Locale
|
|
11
|
+
|
|
12
|
+
internal data class FirmwareArchiveCentralEntry(
|
|
13
|
+
val name: String,
|
|
14
|
+
val compressedSize: Long,
|
|
15
|
+
val uncompressedSize: Long,
|
|
16
|
+
val crc32: Long,
|
|
17
|
+
val flags: Int,
|
|
18
|
+
val compressionMethod: Int,
|
|
19
|
+
val versionMadeBy: Int,
|
|
20
|
+
val externalAttributes: Long,
|
|
21
|
+
val diskNumber: Int,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
internal object FirmwareArchiveRules {
|
|
25
|
+
private const val MAX_EOCD_SEARCH_BYTES = 65_557
|
|
26
|
+
private const val EOCD_SIGNATURE = 0x06054b50L
|
|
27
|
+
private const val CENTRAL_ENTRY_SIGNATURE = 0x02014b50L
|
|
28
|
+
private const val MAX_ARCHIVE_ENTRIES = 4096
|
|
29
|
+
private const val MAX_ARCHIVE_ENTRY_BYTES = 128L * 1024 * 1024
|
|
30
|
+
private const val MAX_ARCHIVE_EXPANDED_BYTES = 512L * 1024 * 1024
|
|
31
|
+
private val nestedArchiveExtensions = setOf(
|
|
32
|
+
".zip",
|
|
33
|
+
".7z",
|
|
34
|
+
".rar",
|
|
35
|
+
".tar",
|
|
36
|
+
".gz",
|
|
37
|
+
".tgz",
|
|
38
|
+
)
|
|
39
|
+
private val sha256Pattern = Regex("^[a-fA-F0-9]{64}$")
|
|
40
|
+
|
|
41
|
+
fun validateRequirements(
|
|
42
|
+
expectedEntries: Array<FirmwareArchiveExpectedEntry>,
|
|
43
|
+
): List<FirmwareArchiveExpectedEntry> {
|
|
44
|
+
require(expectedEntries.isNotEmpty() && expectedEntries.size <= MAX_ARCHIVE_ENTRIES) {
|
|
45
|
+
"Firmware archive expected entry count is invalid"
|
|
46
|
+
}
|
|
47
|
+
val names = mutableSetOf<String>()
|
|
48
|
+
val canonicalNames = mutableSetOf<String>()
|
|
49
|
+
var totalSize = 0L
|
|
50
|
+
expectedEntries.forEach { entry ->
|
|
51
|
+
require(
|
|
52
|
+
entry.artifactId.isNotEmpty() &&
|
|
53
|
+
entry.artifactId.length <= 160 &&
|
|
54
|
+
entry.artifactId.all { it.isLetterOrDigit() || it in "._-" }
|
|
55
|
+
) {
|
|
56
|
+
"Firmware archive artifactId is invalid"
|
|
57
|
+
}
|
|
58
|
+
require(
|
|
59
|
+
names.add(entry.entryName) &&
|
|
60
|
+
validatePortableName(entry.entryName, canonicalNames)
|
|
61
|
+
) {
|
|
62
|
+
"Firmware archive expected entry name is invalid"
|
|
63
|
+
}
|
|
64
|
+
val expectedSize = entry.expectedSize.toExactPositiveLong("entry size")
|
|
65
|
+
require(expectedSize <= MAX_ARCHIVE_ENTRY_BYTES) {
|
|
66
|
+
"Firmware archive entry exceeds size limits"
|
|
67
|
+
}
|
|
68
|
+
totalSize = Math.addExact(totalSize, expectedSize)
|
|
69
|
+
require(totalSize <= MAX_ARCHIVE_EXPANDED_BYTES) {
|
|
70
|
+
"Firmware archive expected entries exceed size limits"
|
|
71
|
+
}
|
|
72
|
+
require(sha256Pattern.matches(entry.expectedSha256)) {
|
|
73
|
+
"Firmware archive entry SHA-256 is invalid"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return expectedEntries.toList()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fun validateCentralDirectory(
|
|
80
|
+
file: File,
|
|
81
|
+
requirements: List<FirmwareArchiveExpectedEntry>,
|
|
82
|
+
): List<FirmwareArchiveCentralEntry> {
|
|
83
|
+
val entries = scanCentralDirectory(file)
|
|
84
|
+
require(entries.size == requirements.size) {
|
|
85
|
+
"Firmware archive has missing or extra entries"
|
|
86
|
+
}
|
|
87
|
+
val requirementsByName = requirements.associateBy { it.entryName }
|
|
88
|
+
val names = mutableSetOf<String>()
|
|
89
|
+
val canonicalNames = mutableSetOf<String>()
|
|
90
|
+
entries.forEach { entry ->
|
|
91
|
+
val requirement = requirementsByName[entry.name]
|
|
92
|
+
?: error("Firmware archive contains an unexpected entry")
|
|
93
|
+
val expectedSize = requirement.expectedSize.toLong()
|
|
94
|
+
require(
|
|
95
|
+
names.add(entry.name) &&
|
|
96
|
+
validatePortableName(entry.name, canonicalNames) &&
|
|
97
|
+
entry.uncompressedSize == expectedSize &&
|
|
98
|
+
entry.uncompressedSize > 0 &&
|
|
99
|
+
entry.compressedSize in 0..MAX_ARCHIVE_EXPANDED_BYTES &&
|
|
100
|
+
entry.uncompressedSize <=
|
|
101
|
+
Math.multiplyExact(entry.compressedSize.coerceAtLeast(1), 1000) &&
|
|
102
|
+
entry.diskNumber == 0 &&
|
|
103
|
+
entry.flags and 1 == 0 &&
|
|
104
|
+
(entry.compressionMethod == 0 || entry.compressionMethod == 8) &&
|
|
105
|
+
isRegularEntry(entry)
|
|
106
|
+
) {
|
|
107
|
+
"Firmware archive entry metadata is invalid"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return entries
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private fun scanCentralDirectory(file: File): List<FirmwareArchiveCentralEntry> {
|
|
114
|
+
require(file.isFile && file.length() >= 22) {
|
|
115
|
+
"Firmware archive is invalid"
|
|
116
|
+
}
|
|
117
|
+
RandomAccessFile(file, "r").use { archive ->
|
|
118
|
+
val tailSize = minOf(file.length(), MAX_EOCD_SEARCH_BYTES.toLong()).toInt()
|
|
119
|
+
val tail = ByteArray(tailSize)
|
|
120
|
+
archive.seek(file.length() - tailSize)
|
|
121
|
+
archive.readFully(tail)
|
|
122
|
+
val eocdIndex = findSignatureBackwards(tail, EOCD_SIGNATURE)
|
|
123
|
+
require(eocdIndex >= 0 && eocdIndex + 22 <= tail.size) {
|
|
124
|
+
"Firmware archive EOCD is missing"
|
|
125
|
+
}
|
|
126
|
+
val eocd = ByteBuffer.wrap(tail, eocdIndex, tail.size - eocdIndex)
|
|
127
|
+
.order(ByteOrder.LITTLE_ENDIAN)
|
|
128
|
+
require(eocd.int.toUnsignedLong() == EOCD_SIGNATURE) {
|
|
129
|
+
"Firmware archive EOCD is invalid"
|
|
130
|
+
}
|
|
131
|
+
val diskNumber = eocd.short.toUnsignedInt()
|
|
132
|
+
val centralDisk = eocd.short.toUnsignedInt()
|
|
133
|
+
val entriesOnDisk = eocd.short.toUnsignedInt()
|
|
134
|
+
val totalEntries = eocd.short.toUnsignedInt()
|
|
135
|
+
val centralSize = eocd.int.toUnsignedLong()
|
|
136
|
+
val centralOffset = eocd.int.toUnsignedLong()
|
|
137
|
+
val commentLength = eocd.short.toUnsignedInt()
|
|
138
|
+
require(
|
|
139
|
+
diskNumber == 0 &&
|
|
140
|
+
centralDisk == 0 &&
|
|
141
|
+
entriesOnDisk == totalEntries &&
|
|
142
|
+
totalEntries in 1..MAX_ARCHIVE_ENTRIES &&
|
|
143
|
+
totalEntries != 0xffff &&
|
|
144
|
+
centralSize != 0xffff_ffffL &&
|
|
145
|
+
centralOffset != 0xffff_ffffL &&
|
|
146
|
+
eocdIndex + 22 + commentLength == tail.size &&
|
|
147
|
+
centralOffset <= file.length() - centralSize &&
|
|
148
|
+
centralOffset + centralSize <= file.length() - tailSize + eocdIndex
|
|
149
|
+
) {
|
|
150
|
+
"Firmware archive is multi-disk, ZIP64, or malformed"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
archive.seek(centralOffset)
|
|
154
|
+
val entries = ArrayList<FirmwareArchiveCentralEntry>(totalEntries)
|
|
155
|
+
repeat(totalEntries) {
|
|
156
|
+
val header = ByteArray(46)
|
|
157
|
+
archive.readFully(header)
|
|
158
|
+
val buffer = ByteBuffer.wrap(header).order(ByteOrder.LITTLE_ENDIAN)
|
|
159
|
+
require(buffer.int.toUnsignedLong() == CENTRAL_ENTRY_SIGNATURE) {
|
|
160
|
+
"Firmware archive central entry is malformed"
|
|
161
|
+
}
|
|
162
|
+
val versionMadeBy = buffer.short.toUnsignedInt()
|
|
163
|
+
buffer.short
|
|
164
|
+
val flags = buffer.short.toUnsignedInt()
|
|
165
|
+
val method = buffer.short.toUnsignedInt()
|
|
166
|
+
buffer.position(buffer.position() + 4)
|
|
167
|
+
val crc32 = buffer.int.toUnsignedLong()
|
|
168
|
+
val compressedSize = buffer.int.toUnsignedLong()
|
|
169
|
+
val uncompressedSize = buffer.int.toUnsignedLong()
|
|
170
|
+
val nameLength = buffer.short.toUnsignedInt()
|
|
171
|
+
val extraLength = buffer.short.toUnsignedInt()
|
|
172
|
+
val entryCommentLength = buffer.short.toUnsignedInt()
|
|
173
|
+
val entryDisk = buffer.short.toUnsignedInt()
|
|
174
|
+
buffer.short
|
|
175
|
+
val externalAttributes = buffer.int.toUnsignedLong()
|
|
176
|
+
val localHeaderOffset = buffer.int.toUnsignedLong()
|
|
177
|
+
require(
|
|
178
|
+
nameLength in 1..4096 &&
|
|
179
|
+
compressedSize != 0xffff_ffffL &&
|
|
180
|
+
uncompressedSize != 0xffff_ffffL &&
|
|
181
|
+
entryDisk != 0xffff &&
|
|
182
|
+
localHeaderOffset != 0xffff_ffffL &&
|
|
183
|
+
localHeaderOffset < centralOffset
|
|
184
|
+
) {
|
|
185
|
+
"Firmware archive ZIP64 entry is not supported"
|
|
186
|
+
}
|
|
187
|
+
val nameBytes = ByteArray(nameLength)
|
|
188
|
+
archive.readFully(nameBytes)
|
|
189
|
+
val name = decodeEntryName(nameBytes, flags)
|
|
190
|
+
val skipLength = Math.addExact(extraLength, entryCommentLength)
|
|
191
|
+
archive.seek(Math.addExact(archive.filePointer, skipLength.toLong()))
|
|
192
|
+
entries += FirmwareArchiveCentralEntry(
|
|
193
|
+
name = name,
|
|
194
|
+
compressedSize = compressedSize,
|
|
195
|
+
uncompressedSize = uncompressedSize,
|
|
196
|
+
crc32 = crc32,
|
|
197
|
+
flags = flags,
|
|
198
|
+
compressionMethod = method,
|
|
199
|
+
versionMadeBy = versionMadeBy,
|
|
200
|
+
externalAttributes = externalAttributes,
|
|
201
|
+
diskNumber = entryDisk,
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
require(archive.filePointer == centralOffset + centralSize) {
|
|
205
|
+
"Firmware archive central directory size mismatch"
|
|
206
|
+
}
|
|
207
|
+
return entries
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private fun decodeEntryName(bytes: ByteArray, flags: Int): String {
|
|
212
|
+
if (flags and (1 shl 11) == 0) {
|
|
213
|
+
require(bytes.all { it.toInt() and 0xff < 0x80 }) {
|
|
214
|
+
"Firmware archive entry name must be UTF-8 or ASCII"
|
|
215
|
+
}
|
|
216
|
+
return String(bytes, StandardCharsets.US_ASCII)
|
|
217
|
+
}
|
|
218
|
+
val decoder = StandardCharsets.UTF_8.newDecoder()
|
|
219
|
+
.onMalformedInput(CodingErrorAction.REPORT)
|
|
220
|
+
.onUnmappableCharacter(CodingErrorAction.REPORT)
|
|
221
|
+
return decoder.decode(ByteBuffer.wrap(bytes)).toString()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private fun validatePortableName(
|
|
225
|
+
name: String,
|
|
226
|
+
canonicalNames: MutableSet<String>,
|
|
227
|
+
): Boolean {
|
|
228
|
+
val normalized = Normalizer.normalize(name, Normalizer.Form.NFC)
|
|
229
|
+
val folded = normalized.lowercase(Locale.ROOT)
|
|
230
|
+
val components = name.split('/')
|
|
231
|
+
return name.isNotEmpty() &&
|
|
232
|
+
name.length <= 512 &&
|
|
233
|
+
name == normalized &&
|
|
234
|
+
!name.startsWith("/") &&
|
|
235
|
+
!name.startsWith("\\") &&
|
|
236
|
+
'\\' !in name &&
|
|
237
|
+
':' !in name &&
|
|
238
|
+
name.none { it.code < 0x20 || it.code == 0x7f } &&
|
|
239
|
+
components.all {
|
|
240
|
+
it.isNotEmpty() &&
|
|
241
|
+
it != "." &&
|
|
242
|
+
it != ".." &&
|
|
243
|
+
!it.endsWith(".") &&
|
|
244
|
+
!it.endsWith(" ")
|
|
245
|
+
} &&
|
|
246
|
+
nestedArchiveExtensions.none { folded.endsWith(it) } &&
|
|
247
|
+
canonicalNames.add(folded)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private fun isRegularEntry(entry: FirmwareArchiveCentralEntry): Boolean {
|
|
251
|
+
val hostSystem = entry.versionMadeBy ushr 8
|
|
252
|
+
if (hostSystem == 3 || hostSystem == 19) {
|
|
253
|
+
val fileType = (entry.externalAttributes ushr 16) and 0xf000
|
|
254
|
+
return fileType == 0L || fileType == 0x8000L
|
|
255
|
+
}
|
|
256
|
+
return entry.externalAttributes and 0x10 == 0L
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private fun findSignatureBackwards(bytes: ByteArray, signature: Long): Int {
|
|
260
|
+
for (index in bytes.size - 4 downTo 0) {
|
|
261
|
+
val value = (bytes[index].toLong() and 0xff) or
|
|
262
|
+
((bytes[index + 1].toLong() and 0xff) shl 8) or
|
|
263
|
+
((bytes[index + 2].toLong() and 0xff) shl 16) or
|
|
264
|
+
((bytes[index + 3].toLong() and 0xff) shl 24)
|
|
265
|
+
if (value == signature) return index
|
|
266
|
+
}
|
|
267
|
+
return -1
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private fun Int.toUnsignedLong(): Long = toLong() and 0xffff_ffffL
|
|
271
|
+
|
|
272
|
+
private fun Short.toUnsignedInt(): Int = toInt() and 0xffff
|
|
273
|
+
|
|
274
|
+
private fun Double.toExactPositiveLong(label: String): Long {
|
|
275
|
+
require(isFinite() && this > 0 && this <= Long.MAX_VALUE.toDouble()) {
|
|
276
|
+
"Invalid firmware archive $label"
|
|
277
|
+
}
|
|
278
|
+
val converted = toLong()
|
|
279
|
+
require(converted.toDouble() == this) {
|
|
280
|
+
"Invalid firmware archive $label"
|
|
281
|
+
}
|
|
282
|
+
return converted
|
|
283
|
+
}
|
|
284
|
+
}
|