Integration Issues with new and old ContentModule/Filter implementations
rkwright opened this issue · 2 comments
This issue is a Discussion
Related issue(s) and/or pull request(s)
None
Expected Behaviour
N/A
Observed behaviour
Sony team is trying to integrate URMS with Readium but the design/implementation of ContentModule/Filters has changed and there are questions about the proper integration approach.
Sony/URMS team wrote:
We have some questions after checking “feature/lcp” branch which seems to contain a lot of changes.
-
Our solution does not use ContentModule approach, just one ContentFilter which is decrypting appropriate resources. Is this way still valid/supported or do we need to implement new ContentModule for this purpose ? The code snippet below contains some comments about non-encrypted loading(RED comment below) when no ContentModule is available. However it seems to be working with encrypted resources when suitable ContentFilter is registered. using commit [24e004a] in this branch.
ContainerPtr Container::OpenContainer(const string &path) {
ContainerPtr container = ContentModuleManager::Instance()->LoadContentAtPath(path); // 1: everything went well, we've got an encrypted EPUB handled by a ContentModule if (bool(container)) { return std::move(container); } // 2: no ContentModule was suitable, let's try non-encrypted loading // <— HERE container = OpenContainerForContentModule(path); if (bool(container)) { return std::move(container); } else { return nullptr; } // 3: there's always the option of a raised exception, which the caller captures to degrade gracefully
}
-
Some of our customers will probably try to use more DRM systems in one Application.
For example, would be possible to make working solution with LCP and standalone Marlin
ContentFilter or is needed to implement one more ContentModule for Marlin and register both
in ContentModuleManager? Basically what is suggested way to implement multi-DRM support using one Readium-SDK instance.
Test file(s)
N/A
Product
SDK
Additional information
None yet
At this point in time, the feature/lcp
branch of the readium-sdk
GitHub repository ( https://github.com/readium/readium-sdk/tree/feature/lcp ) contains many changes that do not directly relate to LCP / DRM functionality, but that were nonetheless seen as important / necessary in order to efficiently debug the LCP "client lib" implementation, and to work around some compiler toolchain limitations. For example (just from the top of my head), we removed dead code / unused functionality, we disabled thread usage, and we eliminated the levels of indirections that managed smart pointer allocations (templated C++ code).
I plan to break down the code diff into separate manageable pieces, to narrow down the DRM scope (essentially: "Content Filter" and "Content Module"). I appreciate that right now, a Pull Request could not realistically be created based on the existing bulk code changes.
The key API "entry-points" are RegisterFilter()
:
https://github.com/readium/readium-sdk/blob/feature/lcp/ePub3/ePub/filter_manager_impl.cpp#L37
...and RegisterContentModule()
(the Content Module would in turn invokes RegisterFilter()
):
https://github.com/readium/readium-sdk/blob/feature/lcp/ePub3/ePub/content_module_manager.cpp#L63
Although it is recommended to use the "Content Module" facility to bootstrap multiple DRM schemes, it is technically possible to directly use the lower-level "Content Filter" construct. However, an implementor would pretty much have to write from scratch the equivalent of Readium's "Content Module" in order to manage several "Content Filters".
The ContentModuleManager::LoadContentAtPath()
function "dispatches" the actual resource decryption requests to actual compatible Content Module / Filters:
https://github.com/readium/readium-sdk/blob/feature/lcp/ePub3/ePub/content_module_manager.cpp#L141
ContainerPtr
ContentModuleManager::LoadContentAtPath(const string& path)
{
if (_known_modules.empty())
{
return nullptr;
}
for (auto& item : _known_modules)
{
std::shared_ptr<ContentModule> modulePtr = item.second;
ContainerPtr container = modulePtr->ProcessFile(path);
if (bool(container)) {
// Singleton FilterManagerImpl::RegisterFilter() uses .emplace() with unique keys,
// so ContentFilters with the same name => only the first inserted one is preserved (subsequent insertions ignored).
modulePtr->RegisterContentFilters();
// Problem: container was loaded okay, but if Navigation Document was encrypted,
// then because the ContentFilters were not registered yet:
// the NavDoc XHTML silently failed to load (populate the internal ReadiumSDK data structures)
// as the byte buffers were scrambled. So, we need to either reload the entire EPUB, or reload only the navdoc.
std::shared_ptr<Package> package = container->DefaultPackage();
package->Unpack_Finally(true);
return std::move(container);
}
}
return nullptr;
}
Each concrete (registered) Content Module's ProcessFile()
function call determines whether a particular DRM scheme handles a given EPUB publication. If no DRM module is capable of handling the loading EPUB, or if the EPUB is not encrypted, or if no Content Modules are registered at all, then OpenContainerForContentModule()
is invoked to completely bypass the DRM pipeline, as shown in Container::OpenContainer()
:
https://github.com/readium/readium-sdk/blob/feature/lcp/ePub3/ePub/container.cpp#L169
ContainerPtr Container::OpenContainer(const string &path) {
ContainerPtr container = ContentModuleManager::Instance()->LoadContentAtPath(path);
// 1: everything went well, we've got an encrypted EPUB handled by a ContentModule
if (bool(container)) {
return std::move(container);
}
// 2: no ContentModule was suitable, let's try non-encrypted loading
container = OpenContainerForContentModule(path);
if (bool(container)) {
return std::move(container);
} else {
return nullptr;
}
// 3: there's always the option of a raised exception, which the caller captures to degrade gracefully
}
Now documented here. Closing