diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..d9ce658a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @aferditamuriqi diff --git a/Cartfile b/Cartfile index 0fdf56a1..04c27f7e 100644 --- a/Cartfile +++ b/Cartfile @@ -1,4 +1,4 @@ -github "readium/r2-shared-swift" == 1.4.2 +github "readium/r2-shared-swift" == 1.4.3 github "dexman/Minizip" == 1.4.0 github "cezheng/Fuzi" == 3.1.1 github "krzyzanowskim/CryptoSwift" == 1.2.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 4c0d5f87..9abe2b96 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -2,4 +2,4 @@ github "cezheng/Fuzi" "3.1.1" github "dexman/Minizip" "1.4.0" github "edrlab/GCDWebServer" "3.6.2" github "krzyzanowskim/CryptoSwift" "1.2.0" -github "readium/r2-shared-swift" "1.4.2" +github "readium/r2-shared-swift" "1.4.3" diff --git a/R2Streamer.podspec b/R2Streamer.podspec index e6d74003..1bccd16b 100644 --- a/R2Streamer.podspec +++ b/R2Streamer.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |s| s.name = "R2Streamer" - s.version = "1.2.4" + s.version = "1.2.5" s.license = "BSD 3-Clause License" s.summary = "R2 Streamer" s.homepage = "http://readium.github.io" s.author = { "Aferdita Muriqi" => "aferdita.muriqi@gmail.com" } - s.source = { :git => "https://github.com/readium/r2-streamer-swift.git", :tag => "1.2.4" } + s.source = { :git => "https://github.com/readium/r2-streamer-swift.git", :tag => "1.2.5" } s.exclude_files = ["**/Info*.plist"] s.requires_arc = true s.resources = ['r2-streamer-swift/Resources/**'] diff --git a/r2-streamer-swift/Fetcher/ContentFilter.swift b/r2-streamer-swift/Fetcher/ContentFilter.swift index 5984575e..94f23b04 100644 --- a/r2-streamer-swift/Fetcher/ContentFilter.swift +++ b/r2-streamer-swift/Fetcher/ContentFilter.swift @@ -33,10 +33,7 @@ internal extension ContentFilters { func apply(to input: Data, of publication: Publication, with container: Container, at path: String) throws -> Data { let inputStream = DataInputStream(data: input) let decodedInputStream = try apply(to: inputStream, of: publication, with: container, at: path) - guard let decodedDataStream = decodedInputStream as? DataInputStream else { - return Data() - } - return decodedDataStream.data + return try Data.reading(decodedInputStream) } } @@ -126,13 +123,13 @@ final internal class ContentFiltersEpub: ContentFilters { // Inserting at the start of . guard let headStart = resourceHtml.endIndex(of: "") else { - log(.error, "Invalid resource") - abort() + log(.error, "Invalid HTML resource: missing ") + return stream } guard let baseUrl = publication.baseURL?.deletingLastPathComponent() else { log(.error, "Invalid host") - abort() + return stream } @@ -146,8 +143,8 @@ final internal class ContentFiltersEpub: ContentFilters { // Inserting at the end of . guard let headEnd = resourceHtml.startIndex(of: "") else { - log(.error, "Invalid resource") - abort() + log(.error, "Invalid HTML resource: missing ") + return stream } let fontStyle = getHtmlFontStyle(forResource: "\(baseUrl)fonts/OpenDyslexic-Regular.otf", fontFamily: "OpenDyslexic") resourceHtml = resourceHtml.insert(string: fontStyle, at: headEnd) diff --git a/r2-streamer-swift/Fetcher/DRM/CBCDRMInputStream.swift b/r2-streamer-swift/Fetcher/DRM/CBCDRMInputStream.swift index 76662ba9..30fb4c10 100644 --- a/r2-streamer-swift/Fetcher/DRM/CBCDRMInputStream.swift +++ b/r2-streamer-swift/Fetcher/DRM/CBCDRMInputStream.swift @@ -20,8 +20,6 @@ final class CBCDRMInputStream: DRMInputStream { enum Error: Swift.Error { case invalidStream case emptyDecryptedData - case readOutOfRange - case readEncryptedOutOfRange case readFailed case decryptionFailed } @@ -63,17 +61,12 @@ final class CBCDRMInputStream: DRMInputStream { } override func read(_ buffer: UnsafeMutablePointer, maxLength len: Int) -> Int { - guard hasBytesAvailable else { - return 0 - } - - let len = Int64(len) let offset = Int64(self.offset) - guard offset + len <= length else { - fail(with: Error.readOutOfRange) - return -1 + let len = min(Int64(len), Int64(length) - offset) + guard hasBytesAvailable, len > 0 else { + return 0 // EOF } - + // Get offset result offset in the block. let blockOffset = offset % AESBlockSize // For beginning of the cipher text, IV used for XOR. @@ -101,11 +94,6 @@ final class CBCDRMInputStream: DRMInputStream { do { let bufferSize = blocksCount * AESBlockSize var buffer = Array(repeating: 0, count: Int(bufferSize)) - guard readPosition + bufferSize <= stream.length else { - fail(with: Error.readEncryptedOutOfRange) - return -1 - } - stream.open() try stream.seek(offset: Int64(readPosition), whence: .startOfFile) let numberOfBytesRead = stream.read(&buffer, maxLength: Int(bufferSize)) diff --git a/r2-streamer-swift/Fetcher/DRM/DRMInputStream.swift b/r2-streamer-swift/Fetcher/DRM/DRMInputStream.swift index 59ede650..5242f9d3 100644 --- a/r2-streamer-swift/Fetcher/DRM/DRMInputStream.swift +++ b/r2-streamer-swift/Fetcher/DRM/DRMInputStream.swift @@ -42,15 +42,11 @@ class DRMInputStream: SeekableInputStream, Loggable { let length = Int64(self.length) switch whence { case .startOfFile: - assert(0...length ~= offset) - _offset = UInt64(offset) + _offset = UInt64(min(offset, length)) case .endOfFile: - assert(-length...0 ~= offset) - _offset = UInt64(length + offset) + _offset = UInt64(min(length + offset, length)) case .currentPosition: - let newOffset = Int64(_offset) + offset - assert(0...length ~= newOffset) - _offset = UInt64(offset) + _offset = min(_offset + UInt64(offset), UInt64(length)) } } diff --git a/r2-streamer-swift/Fetcher/DRM/FullDRMInputStream.swift b/r2-streamer-swift/Fetcher/DRM/FullDRMInputStream.swift index c90b455b..7271bc3c 100644 --- a/r2-streamer-swift/Fetcher/DRM/FullDRMInputStream.swift +++ b/r2-streamer-swift/Fetcher/DRM/FullDRMInputStream.swift @@ -19,7 +19,6 @@ final class FullDRMInputStream: DRMInputStream { enum Error: Swift.Error { case emptyDecryptedData - case readOutOfRange case readFailed case decryptionFailed case inflateFailed @@ -82,29 +81,24 @@ final class FullDRMInputStream: DRMInputStream { } override func read(_ buffer: UnsafeMutablePointer, maxLength len: Int) -> Int { - guard hasBytesAvailable else { - return 0 - } - guard offset + UInt64(len) <= length else { - fail(with: Error.readOutOfRange) - log(.error, "\(link.href): Decryption read out of range") - return -1 + let len = min(len, Int(length - offset)) + guard hasBytesAvailable, len > 0 else { + return 0 // EOF } guard let data = data else { return -1 } - let readSize = (len > Int(length - offset) ? Int(length - offset) : len) let start = data.index(0, offsetBy: Int(offset)) - let end = data.index(start, offsetBy: readSize) + let end = data.index(start, offsetBy: len) let range = Range(uncheckedBounds: (start, end)) data.copyBytes(to: buffer, from: range) - _offset += UInt64(readSize) + _offset += UInt64(len) if _offset >= length { _streamStatus = .atEnd } - return readSize + return len } } diff --git a/r2-streamer-swift/Parser/EPUB/EPUBParser.swift b/r2-streamer-swift/Parser/EPUB/EPUBParser.swift index acbc4e0b..c86befd2 100644 --- a/r2-streamer-swift/Parser/EPUB/EPUBParser.swift +++ b/r2-streamer-swift/Parser/EPUB/EPUBParser.swift @@ -194,9 +194,8 @@ final public class EpubParser: PublicationParser { for mediaOverlayLink in mediaOverlays { let node = MediaOverlayNode() - guard let smilDataOptional = try? fetcher.data(forLink: mediaOverlayLink), -// let smilData = smilDataOptional, - let smilXml = try? XMLDocument(data: smilDataOptional) else + guard let smilData = try? fetcher.data(forLink: mediaOverlayLink), + let smilXml = try? XMLDocument(data: smilData) else { throw OPFParserError.invalidSmilResource } @@ -326,7 +325,7 @@ final public class EpubParser: PublicationParser { ?? Int((try? container.dataLength(relativePath: link.href)) ?? 0) // Arbitrary byte length of a single page in a resource. - let pageLength = 3500 + let pageLength = 1024 let pageCount = max(1, Int(ceil((Double(length) / Double(pageLength))))) let positionList = (1...pageCount).map { position in diff --git a/r2-streamer-swift/Parser/PDF/PDFFileCGParser.swift b/r2-streamer-swift/Parser/PDF/PDFFileCGParser.swift index f8986b72..8b83aabb 100644 --- a/r2-streamer-swift/Parser/PDF/PDFFileCGParser.swift +++ b/r2-streamer-swift/Parser/PDF/PDFFileCGParser.swift @@ -220,8 +220,8 @@ final class PDFFileCGParser: PDFFileParser, Loggable { } let current = stream.offset - // SeekWhence.currentPosition is not supported at this time do { + // SeekWhence.currentPosition is not supported at this time try stream.seek(offset: Int64(current) + count, whence: .startOfFile) } catch { PDFParser.log(.error, error) diff --git a/r2-streamer-swift/Parser/PDF/PDFParser.swift b/r2-streamer-swift/Parser/PDF/PDFParser.swift index 3a3c80b8..a23b217b 100644 --- a/r2-streamer-swift/Parser/PDF/PDFParser.swift +++ b/r2-streamer-swift/Parser/PDF/PDFParser.swift @@ -190,9 +190,10 @@ public final class PDFParser: PublicationParser, Loggable { // Calculates the page count of each resource from the reading order. let resources = publication.readingOrder.map { link -> (Int, Link) in - guard let optionalData = try? fetcher.data(forLink: link), -// let data = optionalData, - let parser = try? parserType.init(stream: DataInputStream(data: optionalData)), + guard let stream = try? fetcher.dataStream(forLink: link), + // FIXME: We should be able to use the stream directly here instead of reading it fully into a Data object, but somehow it fails with random access in CBCDRMInputStream. + let data = try? Data.reading(stream), + let parser = try? parserType.init(stream: DataInputStream(data: data)), let pageCount = try? parser.parseNumberOfPages() else { log(.warning, "Can't get the number of pages from PDF document at \(link)") diff --git a/r2-streamer-swift/Toolkit/DataExtension.swift b/r2-streamer-swift/Toolkit/DataExtension.swift index 175c67c0..50a50367 100644 --- a/r2-streamer-swift/Toolkit/DataExtension.swift +++ b/r2-streamer-swift/Toolkit/DataExtension.swift @@ -12,18 +12,34 @@ import Foundation extension Data { - init(reading input: InputStream) { - self.init() - input.open() + + static func reading(_ stream: InputStream) throws -> Data { + if let dataStream = stream as? DataInputStream { + return dataStream.data + } + + var data = Data() + stream.open() + defer { + stream.close() + } let bufferSize = 1024 let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) - while input.hasBytesAvailable { - let read = input.read(buffer, maxLength: bufferSize) - self.append(buffer, count: read) + defer { + buffer.deallocate() + } + while stream.hasBytesAvailable { + let read = stream.read(buffer, maxLength: bufferSize) + if read < 0 { + throw stream.streamError ?? NSError() + } else if read == 0 { + break // EOF + } + data.append(buffer, count: read) } - buffer.deallocate() - input.close() + return data } + }