mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Various fixes
This commit is contained in:
parent
64aa306cf9
commit
314f02d36d
7 changed files with 190 additions and 74 deletions
|
|
@ -16,97 +16,157 @@ private struct Asn1Entry {
|
|||
let length: Int
|
||||
}
|
||||
|
||||
private func parse(_ data: Data, startIndex: Int = 0) -> Asn1Entry {
|
||||
private func dataByte(_ data: Data, at index: Int) -> UInt8? {
|
||||
guard index >= 0 && index < data.count else {
|
||||
return nil
|
||||
}
|
||||
return data[index]
|
||||
}
|
||||
|
||||
private func parse(_ data: Data, startIndex: Int = 0) -> Asn1Entry? {
|
||||
guard startIndex >= 0 && startIndex < data.count else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var index = startIndex
|
||||
var value = data[index]
|
||||
guard var value = dataByte(data, at: index) else {
|
||||
return nil
|
||||
}
|
||||
index += 1
|
||||
var tagValue = Int32(value & 0x1f)
|
||||
if tagValue == 31 {
|
||||
value = data[index]
|
||||
index += 1
|
||||
while (value & 0x80) != 0 {
|
||||
repeat {
|
||||
guard let nextValue = dataByte(data, at: index) else {
|
||||
return nil
|
||||
}
|
||||
value = nextValue
|
||||
index += 1
|
||||
guard tagValue <= Int32.max >> 8 else {
|
||||
return nil
|
||||
}
|
||||
tagValue <<= 8
|
||||
tagValue |= Int32(value & 0x7f)
|
||||
value = data[index]
|
||||
index += 1
|
||||
}
|
||||
tagValue <<= 8
|
||||
tagValue |= Int32(value & 0x7f)
|
||||
} while (value & 0x80) != 0
|
||||
}
|
||||
|
||||
var length = 0
|
||||
var nextTag = 0
|
||||
value = data[index]
|
||||
guard let lengthValue = dataByte(data, at: index) else {
|
||||
return nil
|
||||
}
|
||||
value = lengthValue
|
||||
let isIndefiniteLength = value == 0x80
|
||||
index += 1
|
||||
if value & 0x80 == 0 {
|
||||
if !isIndefiniteLength && value & 0x80 == 0 {
|
||||
length = Int(value)
|
||||
nextTag = index + length
|
||||
} else if value != 0x80 {
|
||||
} else if !isIndefiniteLength {
|
||||
let octetsCount = Int(value & 0x7f)
|
||||
guard octetsCount > 0 else {
|
||||
return nil
|
||||
}
|
||||
for _ in 0 ..< octetsCount {
|
||||
guard length <= Int.max >> 8 else {
|
||||
return nil
|
||||
}
|
||||
length <<= 8
|
||||
value = data[index]
|
||||
guard let nextValue = dataByte(data, at: index) else {
|
||||
return nil
|
||||
}
|
||||
value = nextValue
|
||||
index += 1
|
||||
length |= Int(value) & 0xff
|
||||
}
|
||||
nextTag = index + length
|
||||
} else {
|
||||
var scanIndex = index
|
||||
while data[scanIndex] != 0 && data[scanIndex + 1] != 0 {
|
||||
while true {
|
||||
guard scanIndex + 1 < data.count else {
|
||||
return nil
|
||||
}
|
||||
if data[scanIndex] == 0 && data[scanIndex + 1] == 0 {
|
||||
break
|
||||
}
|
||||
scanIndex += 1
|
||||
}
|
||||
length = scanIndex - index
|
||||
nextTag = scanIndex + 2
|
||||
}
|
||||
return Asn1Entry(tag: tagValue, data: data.subdata(in: index ..< (index + length)), length: nextTag - startIndex)
|
||||
|
||||
guard length >= 0, length <= data.count - index else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let payloadEndIndex = index + length
|
||||
let entryEndIndex: Int
|
||||
if isIndefiniteLength {
|
||||
entryEndIndex = payloadEndIndex + 2
|
||||
guard entryEndIndex <= data.count else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
entryEndIndex = payloadEndIndex
|
||||
}
|
||||
|
||||
return Asn1Entry(tag: tagValue, data: data.subdata(in: index ..< payloadEndIndex), length: entryEndIndex - startIndex)
|
||||
}
|
||||
|
||||
private func parseSequence(_ data: Data) -> [Asn1Entry] {
|
||||
private func parseSequence(_ data: Data) -> [Asn1Entry]? {
|
||||
var result : [Asn1Entry] = []
|
||||
var index = 0
|
||||
while index < data.count {
|
||||
let entry = parse(data, startIndex: index)
|
||||
guard let entry = parse(data, startIndex: index), entry.length > 0 else {
|
||||
return nil
|
||||
}
|
||||
result.append(entry)
|
||||
index += entry.length
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func parseInteger(_ data: Data) -> Int32 {
|
||||
let length = data.count
|
||||
var value: Int32 = 0
|
||||
for i in 0 ..< length {
|
||||
if i == 0 {
|
||||
value = Int32(data[i] & 0x7f)
|
||||
} else {
|
||||
value <<= 8
|
||||
value |= Int32(data[i])
|
||||
}
|
||||
private func parseInteger(_ data: Data) -> Int32? {
|
||||
guard !data.isEmpty, data.count <= MemoryLayout<Int32>.size else {
|
||||
return nil
|
||||
}
|
||||
if length > 0 && data[0] & 0x80 != 0 {
|
||||
let complement: Int32 = 1 << (length * 8)
|
||||
value -= complement
|
||||
|
||||
var value: UInt32 = 0
|
||||
for byte in data {
|
||||
value = (value << 8) | UInt32(byte)
|
||||
}
|
||||
return value
|
||||
|
||||
if let firstByte = data.first, firstByte & 0x80 != 0 && data.count < MemoryLayout<UInt32>.size {
|
||||
let shift = UInt32(data.count * 8)
|
||||
value |= ~UInt32(0) << shift
|
||||
}
|
||||
|
||||
return Int32(bitPattern: value)
|
||||
}
|
||||
|
||||
private func parseObjectIdentifier(_ data: Data, startIndex: Int = 0, length: Int? = nil) -> [Int32] {
|
||||
private func parseObjectIdentifier(_ data: Data, startIndex: Int = 0, length: Int? = nil) -> [Int32]? {
|
||||
let dataLen = length ?? data.count
|
||||
guard startIndex >= 0, dataLen > 0, startIndex <= data.count, dataLen <= data.count - startIndex else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let endIndex = startIndex + dataLen
|
||||
var index = startIndex
|
||||
var identifier: [Int32] = []
|
||||
while index < startIndex + dataLen {
|
||||
var subidentifier: Int32 = 0
|
||||
var value = data[index]
|
||||
index += 1
|
||||
while (value & 0x80) != 0 {
|
||||
subidentifier <<= 7
|
||||
subidentifier |= Int32(value & 0x7f)
|
||||
value = data[index]
|
||||
while index < endIndex {
|
||||
var subidentifier: Int64 = 0
|
||||
while true {
|
||||
guard let value = dataByte(data, at: index) else {
|
||||
return nil
|
||||
}
|
||||
index += 1
|
||||
guard subidentifier <= Int64(Int32.max >> 7) else {
|
||||
return nil
|
||||
}
|
||||
subidentifier <<= 7
|
||||
subidentifier |= Int64(value & 0x7f)
|
||||
if (value & 0x80) == 0 {
|
||||
break
|
||||
}
|
||||
guard index < endIndex else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
subidentifier <<= 7
|
||||
subidentifier |= Int32(value & 0x7f)
|
||||
identifier.append(subidentifier)
|
||||
identifier.append(Int32(subidentifier))
|
||||
}
|
||||
return identifier
|
||||
}
|
||||
|
|
@ -137,51 +197,71 @@ struct Receipt {
|
|||
}
|
||||
|
||||
func parseReceipt(_ data: Data) -> Receipt? {
|
||||
let root = parseSequence(data)
|
||||
guard let root = parseSequence(data) else {
|
||||
return nil
|
||||
}
|
||||
guard root.count == 1 && root[0].tag == Asn1Tag.sequence else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let rootSeq = parseSequence(root[0].data)
|
||||
guard let rootSeq = parseSequence(root[0].data) else {
|
||||
return nil
|
||||
}
|
||||
guard rootSeq.count == 2 && rootSeq[0].tag == Asn1Tag.objectIdentifier && parseObjectIdentifier(rootSeq[0].data) == ObjectIdentifier.pkcs7SignedData else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let signedData = parseSequence(rootSeq[1].data)
|
||||
guard let signedData = parseSequence(rootSeq[1].data) else {
|
||||
return nil
|
||||
}
|
||||
guard signedData.count == 1 && signedData[0].tag == Asn1Tag.sequence else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let signedDataSeq = parseSequence(signedData[0].data)
|
||||
guard let signedDataSeq = parseSequence(signedData[0].data) else {
|
||||
return nil
|
||||
}
|
||||
guard signedDataSeq.count > 3 && signedDataSeq[2].tag == Asn1Tag.sequence else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let contentData = parseSequence(signedDataSeq[2].data)
|
||||
guard let contentData = parseSequence(signedDataSeq[2].data) else {
|
||||
return nil
|
||||
}
|
||||
guard contentData.count == 2 && contentData[0].tag == Asn1Tag.objectIdentifier && parseObjectIdentifier(contentData[0].data) == ObjectIdentifier.pkcs7Data else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let payload = parse(contentData[1].data)
|
||||
guard let payload = parse(contentData[1].data) else {
|
||||
return nil
|
||||
}
|
||||
guard payload.tag == Asn1Tag.octetString else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let payloadRoot = parse(payload.data)
|
||||
guard let payloadRoot = parse(payload.data) else {
|
||||
return nil
|
||||
}
|
||||
guard payloadRoot.tag == Asn1Tag.set else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var purchases: [Receipt.Purchase] = []
|
||||
|
||||
let receiptAttributes = parseSequence(payloadRoot.data)
|
||||
guard let receiptAttributes = parseSequence(payloadRoot.data) else {
|
||||
return nil
|
||||
}
|
||||
for attribute in receiptAttributes {
|
||||
if attribute.tag != Asn1Tag.sequence { continue }
|
||||
let attributeEntries = parseSequence(attribute.data)
|
||||
guard let attributeEntries = parseSequence(attribute.data) else {
|
||||
return nil
|
||||
}
|
||||
guard attributeEntries.count == 3 && attributeEntries[0].tag == Asn1Tag.integer && attributeEntries[1].tag == Asn1Tag.integer && attributeEntries[2].tag == Asn1Tag.octetString else { return nil
|
||||
}
|
||||
|
||||
let type = parseInteger(attributeEntries[0].data)
|
||||
guard let type = parseInteger(attributeEntries[0].data) else {
|
||||
return nil
|
||||
}
|
||||
let value = attributeEntries[2].data
|
||||
switch (type) {
|
||||
case Receipt.Tag.purchases:
|
||||
|
|
@ -202,22 +282,41 @@ private func parseRfc3339Date(_ str: String) -> Date? {
|
|||
formatter1.locale = posixLocale
|
||||
formatter1.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssX5"
|
||||
formatter1.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
|
||||
let result = formatter1.date(from: str)
|
||||
|
||||
var result = formatter1.date(from: str)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
let formatter2 = DateFormatter()
|
||||
formatter2.locale = posixLocale
|
||||
formatter2.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSX5"
|
||||
formatter2.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
|
||||
result = formatter2.date(from: str)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
return formatter2.date(from: str)
|
||||
let formatterWithFractionalSeconds = ISO8601DateFormatter()
|
||||
formatterWithFractionalSeconds.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
formatterWithFractionalSeconds.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
||||
|
||||
if let result = formatterWithFractionalSeconds.date(from: str) {
|
||||
return result
|
||||
}
|
||||
|
||||
let formatterWithoutFractionalSeconds = ISO8601DateFormatter()
|
||||
formatterWithoutFractionalSeconds.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
formatterWithoutFractionalSeconds.formatOptions = [.withInternetDateTime]
|
||||
|
||||
return formatterWithoutFractionalSeconds.date(from: str)
|
||||
}
|
||||
|
||||
private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? {
|
||||
let root = parse(data)
|
||||
guard let root = parse(data) else {
|
||||
return nil
|
||||
}
|
||||
guard root.tag == Asn1Tag.set else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -226,26 +325,38 @@ private func parsePurchaseAttributes(_ data: Data) -> Receipt.Purchase? {
|
|||
var transactionId: String?
|
||||
var expirationDate: Date?
|
||||
|
||||
let receiptAttributes = parseSequence(root.data)
|
||||
guard let receiptAttributes = parseSequence(root.data) else {
|
||||
return nil
|
||||
}
|
||||
for attribute in receiptAttributes {
|
||||
if attribute.tag != Asn1Tag.sequence { continue }
|
||||
let attributeEntries = parseSequence(attribute.data)
|
||||
guard let attributeEntries = parseSequence(attribute.data) else {
|
||||
return nil
|
||||
}
|
||||
guard attributeEntries.count == 3 && attributeEntries[0].tag == Asn1Tag.integer && attributeEntries[1].tag == Asn1Tag.integer && attributeEntries[2].tag == Asn1Tag.octetString else { return nil
|
||||
}
|
||||
|
||||
let type = parseInteger(attributeEntries[0].data)
|
||||
guard let type = parseInteger(attributeEntries[0].data) else {
|
||||
return nil
|
||||
}
|
||||
let value = attributeEntries[2].data
|
||||
switch (type) {
|
||||
case Receipt.Purchase.Tag.productIdentifier:
|
||||
let valEntry = parse(value)
|
||||
guard let valEntry = parse(value) else {
|
||||
return nil
|
||||
}
|
||||
guard valEntry.tag == Asn1Tag.utf8String else { return nil }
|
||||
productId = String(bytes: valEntry.data, encoding: .utf8)
|
||||
case Receipt.Purchase.Tag.transactionIdentifier:
|
||||
let valEntry = parse(value)
|
||||
guard let valEntry = parse(value) else {
|
||||
return nil
|
||||
}
|
||||
guard valEntry.tag == Asn1Tag.utf8String else { return nil }
|
||||
transactionId = String(bytes: valEntry.data, encoding: .utf8)
|
||||
case Receipt.Purchase.Tag.expirationDate:
|
||||
let valEntry = parse(value)
|
||||
guard let valEntry = parse(value) else {
|
||||
return nil
|
||||
}
|
||||
guard valEntry.tag == Asn1Tag.date else { return nil }
|
||||
expirationDate = parseRfc3339Date(String(bytes: valEntry.data, encoding: .utf8) ?? "")
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -2765,7 +2765,7 @@ final class ShareWithPeersScreenComponent: Component {
|
|||
let navigationLeftButtonSize = self.navigationLeftButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
size: CGSize(width: 44.0, height: 44.0),
|
||||
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
|
||||
isDark: environment.theme.overallDarkAppearance,
|
||||
state: .generic,
|
||||
|
|
|
|||
|
|
@ -911,7 +911,7 @@ final class DataUsageScreenComponent: Component {
|
|||
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)).withUserData(AnimationHint(value: .modeChanged)))
|
||||
})),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - (environment.safeInsets.left + 16.0 + 44.0 + 10.0) * 2.0, height: 100.0)
|
||||
containerSize: CGSize(width: availableSize.width - (environment.safeInsets.left + 48.0) * 2.0, height: 100.0)
|
||||
)
|
||||
let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - segmentedSize.width) * 0.5), y: contentHeight), size: segmentedSize)
|
||||
if let segmentedControlComponentView = self.segmentedControlView.view {
|
||||
|
|
|
|||
|
|
@ -377,6 +377,7 @@ public final class StoryPeerListComponent: Component {
|
|||
self.scrollView.alwaysBounceVertical = false
|
||||
self.scrollView.alwaysBounceHorizontal = true
|
||||
self.scrollView.clipsToBounds = false
|
||||
self.scrollView.scrollsToTop = false
|
||||
|
||||
self.scrollContainerView = UIView()
|
||||
self.scrollContainerView.clipsToBounds = true
|
||||
|
|
|
|||
|
|
@ -965,7 +965,7 @@ public final class StoryPeerListItemComponent: Component {
|
|||
var titleTransition = transition
|
||||
if let previousAnimation = previousComponent?.ringAnimation, case .progress = previousAnimation, component.ringAnimation == nil {
|
||||
if let titleView = self.title.view, let snapshotView = titleView.snapshotView(afterScreenUpdates: false) {
|
||||
self.button.addSubview(snapshotView)
|
||||
self.titleContainer.addSubview(snapshotView)
|
||||
snapshotView.frame = titleView.frame
|
||||
snapshotView.layer.animateAlpha(from: component.expandedAlphaFraction, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ private final class VideoMessageCameraScreenComponent: CombinedComponent {
|
|||
let availableSize = context.component.containerSize
|
||||
|
||||
var sideInset: CGFloat = 8.0
|
||||
if component.safeInsets.bottom <= 32.0 {
|
||||
if component.safeInsets.bottom <= 32.0 && environment.inputHeight == 0.0 {
|
||||
sideInset += 18.0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -606,7 +606,11 @@ extension ChatControllerImpl {
|
|||
}
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: screenWidth - layout.safeInsets.right - 50.0, y: layout.size.height - insets.bottom - 128.0), size: CGSize())
|
||||
var sideOffset: CGFloat = 18.0
|
||||
if let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
||||
sideOffset = 0.0
|
||||
}
|
||||
let location = CGRect(origin: CGPoint(x: screenWidth - layout.safeInsets.right - 50.0 - sideOffset, y: layout.size.height - insets.bottom - 128.0), size: CGSize())
|
||||
|
||||
let tooltipController = TooltipScreen(
|
||||
account: self.context.account,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue