...
In `Parser/EPUB/EPUBParser.swift` return Adobe DRM (added code is bold):
Code Block | ||
---|---|---|
| ||
private static func scanForDRM(in container: Container) -> DRM? { /// LCP. // Check if a LCP license file is present in the container. if ((try? container.data(relativePath: EPUBConstant.lcplFilePath)) != nil) { return DRM(brand: .lcp) } return DRM(brand: .adobe) // return nil } |
private static func scanForDRM(in container: Container) -> DRM? {
/// LCP.
// Check if a LCP license file is present in the container.
if ((try? container.data(relativePath: EPUBConstant.lcplFilePath)) != nil) {
return DRM(brand: .lcp)
}
return DRM(brand: .adobe)
// return nil
}
Commit:
https://github.com/NYPL-Simplified/r2-streamer-swift/commit/9a53944a6bac0280eb4902155647c1e5bf0e5a9e
In `Parser/EPUB/EPUBEncryptionParser.swift` added Adobe encryption scheme (added code in bold):
func parseEncryptions() -> [String: Encryption] {
guard let document = document else {
return [:]
}
var encryptions: [String: Encryption] = [:]
// Loop through <EncryptedData> elements..
...
Commit:
https://github.com/NYPL-Simplified/r2-streamer-swift/commit/9a53944a6bac0280eb4902155647c1e5bf0e5a9e
In `Parser/EPUB/EPUBEncryptionParser.swift` added Adobe encryption scheme (added code in bold):
Code Block | ||
---|---|---|
| ||
func parseEncryptions() -> [String: Encryption] { guard let document = document else { return [:] } var encryptions: [String: Encryption] = [:] // Loop through <EncryptedData> elements.. for encryptedDataElement in document.xpath("./enc:EncryptedData") |
...
{ guard let algorithm = encryptedDataElement.firstChild(xpath: "enc:EncryptionMethod")?.attr( |
...
"Algorithm var resourceURI = encryptedDataElement.firstChild(xpath:"enc:CipherData/enc:CipherReference")?.attr("URI") |
...
{
continue
}
...
?.removingPercentEncoding else { continue } resourceURI = normalize(base: "/" |
...
, href: resourceURI) var encryption = Encryption(algorithm: algorithm) |
...
// LCP. Tag LCP protected resources. |
...
let keyInfoURI = encryptedDataElement.firstChild(xpath: "ds:KeyInfo/ds:RetrievalMethod")?.attr("URI") |
...
if keyInfoURI == "license.lcpl#/encryption/content_key", |
...
drm?.brand == DRM.Brand.lcp |
...
{
...
{ encryption.scheme = drm?.scheme.rawValue |
...
}
// LCP END.
...
} // LCP END. if drm?.brand |
...
== .adobe { encryption.scheme = drm?.scheme.rawValue |
...
}
...
} |
Commit:
https://github.com/NYPL-Simplified/r2-streamer-swift/commit/08c6d6a204cebff5137f868794a412b453437a5b
In `Parser/EPUB/EPUBEncryptionParser.swift` changed how Adobe encryption scheme is assigned:
Code Block | ||
---|---|---|
| ||
scheme = drm?.scheme.rawValue |
In `Parser/EPUB/EPUBParser.swift` added `links` to the initialized publication
Code Block | ||
---|---|---|
| ||
publication.links = links |
Commit:
https://github.com/NYPL-Simplified/r2-streamer-swift/commit/ffffd5e37f7e422d42aa7478b845a313d8767187
...
In `Simplified/Reader2/Internal/LibrayService.swift` init adds AdobeDRMLibraryService to drmLibraryServices array (added code in bold): init
Code Block | ||
---|---|---|
| ||
init(publicationServer: PublicationServer) |
...
{ self.publicationServer = |
...
#if LCP
...
publicationServer #if LCP drmLibraryServices.append(LCPLibraryService()) |
...
#endif
...
#endif drmLibraryServices.append(AdobeDRMLibraryService()) |
...
} |
Decrypting Table of Contents
The current version of Readium 2 parses navigation document without decrypting it. As a result, the table of contents appears blank in the app. To show table of contents in the app, additional decryption was required, and LibraryService.swift parsePublication(at url: URL) function was modified (added code is bold): private func
Code Block | ||
---|---|---|
| ||
private func parsePublication(at url: URL) -> PubBox? |
...
do {
...
{ do { guard let (pubBox, parsingCallback) = try Publication.parse(at: |
...
return nil
}
let (publication, container) = pubBox
// TODO: SIMPLY-2840
// Parse .ncx document to update TOC and page list if publication doesn't contain TOC
// -- the code below should be removed as described in SIMPLY-2840 --
if publication.tableOfContents.isEmpty {
...
url) else { return nil } let (publication, container) = pubBox // TODO: SIMPLY-2840 // Parse .ncx document to update TOC and page list if publication doesn't contain TOC // -- the code below should be removed as described in SIMPLY-2840 -- if publication.tableOfContents.isEmpty { publication.otherCollections.append(contentsOf: parseNCXDocument(in: container, links: publication.links)) |
...
}
// -- end of cleanup --
...
} // -- end of cleanup -- items[url.lastPathComponent] = (container, parsingCallback) |
...
return (publication, |
...
} catch {
...
container) } catch { log(.error, "Error parsing publication at '\(url.absoluteString)': \(error.localizedDescription)") |
...
return nil
}
}
return nil } } } |
And parseNCXDocument(from container: Container, to publication: inout Publication) function was added in an extension to LibraryService.
The way Readium 2 creates Publication and Container objects may change in the future, the idea is to decrypt ncxDocumentData as shown below:
Original code: private func
Code Block | ||
---|---|---|
| ||
private func parseNCXDocument(in container: Container, links: [Link]) -> [PublicationCollection] |
...
{ // Get the link in the readingOrder pointing to the NCX document. |
...
guard let ncxLink = links.first(withType: |
...
.ncx), let ncxDocumentData = try? container.data(relativePath: ncxLink.href) |
...
{
return []
}
...
else { return [] } let ncx = NCXParser(data: ncxDocumentData, at: ncxLink.href) |
...
func makeCollection(_ type: NCXParser.NavType, role: String) -> PublicationCollection? |
...
let links = ncx.links(for: type)
...
{ let links = ncx.links(for: type) guard !links.isEmpty else |
...
return nil
}
...
{ return nil } return PublicationCollection(role: role, links: links) |
...
}
return [
...
} return [ makeCollection(.tableOfContents, role: "toc"), |
...
makeCollection(.pageList, role: "pageList") |
...
].compactMap { $0 } } |
}Modified code decrypts ncx data (in bold): private func
Code Block | ||
---|---|---|
| ||
private func parseNCXDocument(in container: Container, links: [Link]) -> [PublicationCollection] |
...
{ // Get the link in the readingOrder pointing to the NCX document. |
...
guard let ncxLink = links.first(withType: .ncx), |
...
let ncxDocumentData = try? container.data(relativePath: ncxLink.href) |
...
{
return []
}
// this part is added to decrypt ncx data
var data = ncxDocumentData
let license = AdobeDRMLicense(for: container)
...
else { return [] } // this part is added to decrypt ncx data var data = ncxDocumentData let license = AdobeDRMLicense(for: container) if let optionalDecipheredData = try? license.decipher(ncxDocumentData), |
...
let decipheredData = optionalDecipheredData {
data = decipheredData
}
// NCXParser here parses data instead of ncxDocumentData
let ncx = NCXParser(data: data, at: ncxLink.href)
func makeCollection(_ type: NCXParser.NavType, role: String) -> PublicationCollection? {
...
let decipheredData = optionalDecipheredData { data = decipheredData } // NCXParser here parses data instead of ncxDocumentData let ncx = NCXParser(data: data, at: ncxLink.href) func makeCollection(_ type: NCXParser.NavType, role: String) -> PublicationCollection? { let links = ncx.links(for: type) |
...
guard !links.isEmpty else {
return nil
}
...
guard !links.isEmpty else { return nil } return PublicationCollection(role: role, links: links) |
...
}
return [
...
} return [ makeCollection(.tableOfContents, role: "toc"), |
...
makeCollection(.pageList, role: "pageList") |
...
].compactMap { $0 } |
...
}
|
}
This workaround is temporary, the version of Readium 2 that supports multiple DRM services won’t need additional parsing of the navigation document, and this workaround should be removed.
...