Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

In `DRM/DRM.swift` the new brand and scheme for Adobe DRM (added code is bold):


Code Block
languageapplescriptcpp
public struct DRM {
    public let brand: Brand
    public let scheme: Scheme
    
    /// The license will be filled when passed back to the DRM module.
    
    public var license: DRMLicense?
    public enum Brand: String {
        case lcp
        case adobe
    }

    public enum Scheme: String {
        case lcp = "http://readium.org/2014/01/lcp"
        case adobe = "http://ns.adobe.com/adept"
    }

    public init(brand: Brand) {
        self.brand = brand
        switch brand {
        case .lcp:
            scheme = .lcp
        case .adobe:
            scheme = .adobe
        }
    }
}

...

In `Parser/EPUB/EPUBParser.swift` return Adobe DRM (added code is bold):


Code Block
languageapplescriptcpp
 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
  }

...

In `Parser/EPUB/EPUBEncryptionParser.swift` added Adobe encryption scheme (added code in bold):    func


Code Block
languagecpp
 func parseEncryptions() -> [String: Encryption]

...

        guard let document = document else {

            return [:]

        }

        var encryptions: [String: Encryption] = [:]

        

        // Loop through <EncryptedData> elements..

...

 {
        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")?.removingPercentEncoding

...

            {

                continue

            }

...

 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
languagecpp
scheme = drm?.scheme.rawValue

In `Parser/EPUB/EPUBParser.swift` added `links` to the initialized publication


Code Block
languagecpp
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
languagecpp
  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
languagecpp
private func parsePublication(at url: URL) -> PubBox?

...

    do {

...

 {
    do {
      guard let (pubBox, parsingCallback) = try Publication.parse(at: url) else

...

        return nil

      }

...

 {
        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, container)

...

    } catch {

...


    } 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
languagecpp
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)

...


          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
languagecpp
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

          }

...

 {
              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.

...