685 lines
20 KiB
C
685 lines
20 KiB
C
|
/*
|
||
|
Copyright 2021 Grégory Soutadé
|
||
|
|
||
|
This is a free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
It is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with it. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#ifndef RMSDK_WRAPPER_H
|
||
|
#define RMSDK_WRAPPER_H
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <exception>
|
||
|
|
||
|
#include <adept.h>
|
||
|
#include <librmsdk.h>
|
||
|
#include <dpdrm.h>
|
||
|
#include <dpio.h>
|
||
|
#include <dpdev.h>
|
||
|
#include <dpnet.h>
|
||
|
#include <zip.h>
|
||
|
|
||
|
#include <QNetworkAccessManager>
|
||
|
#include <QNetworkReply>
|
||
|
#include <QCoreApplication>
|
||
|
#include <QFile>
|
||
|
#include <QDir>
|
||
|
|
||
|
#include <log.h>
|
||
|
|
||
|
class MockDevice;
|
||
|
|
||
|
class MockCallback : public dp::Callback
|
||
|
{
|
||
|
virtual void* getOptionalInterface(char const*p0) {return 0;}
|
||
|
virtual void reportError(dp::String const&) {LOG_FUNC();}
|
||
|
virtual void invoke(dp::Unknown*) {LOG_FUNC();}
|
||
|
virtual void virtfunc0(){LOG_FUNC();}
|
||
|
virtual void virtfunc4(){LOG_FUNC();}
|
||
|
virtual void virtfunc8(){LOG_FUNC();}
|
||
|
virtual void virtfunc12(){LOG_FUNC();}
|
||
|
};
|
||
|
|
||
|
class MockArchiveListener : public zip::ArchiveListener
|
||
|
{
|
||
|
virtual void endReadDirectory(zip::Archive* archive){LOG_FUNC();}
|
||
|
virtual void virtfunc20(){LOG_FUNC();}
|
||
|
};
|
||
|
|
||
|
class MockDRMProcessorClient: public dpdrm::DRMProcessorClient
|
||
|
{
|
||
|
public:
|
||
|
MockDRMProcessorClient(const char* outputDir, const char* outputFile, const char* tmpDir=0):
|
||
|
outputDir(outputDir), outputFile(outputFile), tmpDir(tmpDir),
|
||
|
sourceName(0), targetName(0), rightsXML(0), errors(0)
|
||
|
{}
|
||
|
|
||
|
virtual ~MockDRMProcessorClient() {
|
||
|
if (sourceName) free(sourceName);
|
||
|
if (targetName) free(targetName);
|
||
|
if (rightsXML) free(rightsXML);
|
||
|
}
|
||
|
|
||
|
virtual void mockclientfn0() {LOG_FUNC();}
|
||
|
virtual void mockclientfn1() {LOG_FUNC();}
|
||
|
|
||
|
virtual void workflowFinished(unsigned int workflow, dp::String& str)
|
||
|
{
|
||
|
LOG(DEBUG, "Workflow finished " << workflow);
|
||
|
if (str.utf8())
|
||
|
{
|
||
|
LOG(DEBUG, str.utf8());
|
||
|
}
|
||
|
if ((workflow & WORKFLOW_FULFILLMENT) && !errors)
|
||
|
{
|
||
|
dp::list<dpdrm::FulfillmentItem> list = processor->getFulfillmentItems();
|
||
|
if (list.length())
|
||
|
{
|
||
|
dp::String title = getMetadata(list[0], "title");
|
||
|
uft::String fullName;
|
||
|
|
||
|
if (outputFile)
|
||
|
targetName = strdup(outputFile);
|
||
|
else
|
||
|
{
|
||
|
if (title.utf8())
|
||
|
{
|
||
|
uft::String fullName = title.uft() + ".epub";
|
||
|
targetName = fullName.utf8();
|
||
|
|
||
|
// Duplicate code here because fullName is destroyed
|
||
|
if (!targetName || !strlen(targetName))
|
||
|
targetName = strdup("output.epub");
|
||
|
else
|
||
|
targetName = strdup(targetName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!targetName || !strlen(targetName))
|
||
|
targetName = strdup("output.epub");
|
||
|
else
|
||
|
targetName = strdup(targetName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dp::String author = getMetadata(list[0], "creator");
|
||
|
dp::String publisher = getMetadata(list[0], "publisher");
|
||
|
dp::String language = getMetadata(list[0], "language");
|
||
|
|
||
|
LOG(INFO, "Title : " << title.utf8());
|
||
|
LOG(INFO, "Author : " << author.utf8());
|
||
|
LOG(INFO, "Publisher : " << publisher.utf8());
|
||
|
LOG(INFO, "Language : " << language.utf8());
|
||
|
|
||
|
dp::Data postData = list[0]->getPostData();
|
||
|
if (postData.data())
|
||
|
{
|
||
|
LOG(DEBUG, "Post data " << std::endl << postData.data());
|
||
|
}
|
||
|
|
||
|
dp::ref<dpdrm::Rights> rights = list[0]->getRights();
|
||
|
dp::Data _rightsXML = rights->serialize();
|
||
|
if (_rightsXML.data())
|
||
|
{
|
||
|
rightsXML = strdup((const char*)_rightsXML.data());
|
||
|
LOG(DEBUG, "Rights : " << std::endl << rightsXML);
|
||
|
}
|
||
|
|
||
|
// Downloaded file
|
||
|
dp::list<dpdrm::License> licenses = rights->getLicenses();
|
||
|
// Should be wrote @ /tmp/<voucher_id>
|
||
|
if (licenses.length())
|
||
|
{
|
||
|
uft::StringBuffer fullTmpName = uft::String("file:///tmp/");
|
||
|
fullTmpName.append(licenses[0]->getVoucherID().utf8());
|
||
|
sourceName = strdup(fullTmpName.utf8());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (workflow & WORKFLOW_AUTH_SIGN_IN)
|
||
|
{
|
||
|
}
|
||
|
else if (workflow & WORKFLOW_ACTIVATION)
|
||
|
{
|
||
|
QDir dir;
|
||
|
if (!errors)
|
||
|
{
|
||
|
LOG(DEBUG, "Move dir " << tmpDir << "/.adept -> " << outputDir);
|
||
|
if (dir.rename(QString(tmpDir) + "/.adept", QString(outputDir)))
|
||
|
{
|
||
|
LOG(INFO, "Created " << outputDir);
|
||
|
dir.setPath(tmpDir);
|
||
|
dir.removeRecursively();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LOG(ERROR, "Error : Unable to move " << tmpDir << "/.adept into " << outputDir);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (verbose >= DEBUG)
|
||
|
{
|
||
|
LOG(DEBUG, "Temp path available at " << tmpDir << "/.adept");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LOG(INFO, "Cleaning temp path");
|
||
|
dir.setPath(tmpDir);
|
||
|
dir.removeRecursively();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void mockclientfn3(void) {LOG_FUNC();}
|
||
|
virtual void mockclientfn4(void) {LOG_FUNC();}
|
||
|
virtual void mockclientfn5(void) {LOG_FUNC();}
|
||
|
virtual void workflowProgress(unsigned int workflow, dp::String& title, double progress)
|
||
|
{
|
||
|
LOG(WARN, "Progress " << workflow << " " << title.utf8() << " " << progress << " %%");
|
||
|
if (workflow & WORKFLOW_DOWNLOAD)
|
||
|
{
|
||
|
if (progress == 100.0)
|
||
|
{
|
||
|
// Copy tmp file into destination
|
||
|
if (!outputDir)
|
||
|
outputDir = ".";
|
||
|
|
||
|
// Add rights into downloaded file
|
||
|
MockCallback callback;
|
||
|
MockArchiveListener archiveListener;
|
||
|
|
||
|
dpdev::DeviceProvider* provider = dpdev::DeviceProvider::getProvider(0);
|
||
|
dpio::Partition* partition = provider->getDevice(0)->getPartition(0);
|
||
|
|
||
|
dpio::BufferedStreamClient streamClient;
|
||
|
|
||
|
LOG(DEBUG, "Insert rights into " << sourceName);
|
||
|
// Auto deleted
|
||
|
dpio::Stream* stream = partition->readFile(sourceName, &streamClient, 0);
|
||
|
if (stream)
|
||
|
{
|
||
|
zip::Archive archive(stream, &archiveListener);
|
||
|
archive.readDirectory();
|
||
|
// Auto deleted
|
||
|
zip::EditableStream* editableStream = new zip::EditableStream(&archive);
|
||
|
editableStream->addFile("META-INF/rights.xml", dp::String(rightsXML));
|
||
|
uft::StringBuffer targetFileName(outputDir);
|
||
|
targetFileName.append("/");
|
||
|
targetFileName.append(targetName);
|
||
|
partition->writeFile(targetFileName.utf8(), editableStream, &callback);
|
||
|
partition->removeFile(sourceName, &callback);
|
||
|
LOG(INFO, "Created " << outputDir << "/" << targetName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LOG(ERROR, "Source file " << sourceName << " not found");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
virtual void workflowError(unsigned int workflow, dp::String& error)
|
||
|
{
|
||
|
LOG(ERROR, "Workflow error " << workflow << " " << error.utf8());
|
||
|
errors |= workflow;
|
||
|
}
|
||
|
|
||
|
void setProcessor(adept::DRMProcessorImpl* processor)
|
||
|
{ this->processor = processor; }
|
||
|
|
||
|
unsigned int getErrors(void) {return errors;}
|
||
|
|
||
|
private:
|
||
|
adept::DRMProcessorImpl* processor;
|
||
|
const char *outputDir, *outputFile, *tmpDir;
|
||
|
char* sourceName;
|
||
|
char* targetName;
|
||
|
char* rightsXML;
|
||
|
unsigned int errors;
|
||
|
|
||
|
/*
|
||
|
Try to get metadata with :
|
||
|
* DC. + name
|
||
|
* dc. + name
|
||
|
* name
|
||
|
*/
|
||
|
dp::String getMetadata(dp::ref<dpdrm::FulfillmentItem> item, const char* name)
|
||
|
{
|
||
|
uft::String newName = uft::String("DC.") + name;
|
||
|
|
||
|
dp::String res = item->getMetadata(newName.utf8());
|
||
|
if (res.utf8())
|
||
|
return res;
|
||
|
|
||
|
newName = uft::String("dc.") + name;
|
||
|
res = item->getMetadata(newName);
|
||
|
if (res.utf8())
|
||
|
return res;
|
||
|
|
||
|
return item->getMetadata(name);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class MockPartition : public dpio::FilesystemPartition
|
||
|
{
|
||
|
public:
|
||
|
MockPartition(dpdev::Device* device, int index,
|
||
|
dp::String name, dp::String type,
|
||
|
dp::String rootPath, dp::String docPath,
|
||
|
dp::String tempPath):
|
||
|
FilesystemPartition(device, index, type, rootPath, docPath, tempPath),
|
||
|
docPath(docPath), tempPath(tempPath)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
virtual void* getInterfaceID(){return 0;}
|
||
|
virtual dp::String getDocumentFolderURL() {LOG_FUNC(); return docPath;}
|
||
|
virtual dp::String getTemporaryFolderURL() {LOG_FUNC(); return tempPath;}
|
||
|
|
||
|
virtual void createUniqueFile( const dp::String& path, const dp::String& extension, dp::Callback* callback )
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
|
||
|
uft::StringBuffer _path(path.utf8());
|
||
|
_path.append(extension.utf8());
|
||
|
|
||
|
QFile file(_path.utf8());
|
||
|
file.open(QIODevice::WriteOnly|QIODevice::Truncate);
|
||
|
file.close();
|
||
|
}
|
||
|
|
||
|
virtual void writeFile( const dp::String& path, dpio::Stream* streamData, dp::Callback* callback )
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
|
||
|
QFile file(path.utf8());
|
||
|
bool ret = file.open(QIODevice::WriteOnly|QIODevice::Truncate);
|
||
|
if (!ret)
|
||
|
{
|
||
|
LOG(ERROR, "Unable to open file " << path.utf8());
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
LOG(WARN, "Write file " << path.utf8());
|
||
|
|
||
|
dp::Data data = dpio::Stream::readSynchronousStream(streamData);
|
||
|
LOG(DEBUG, "Read from stream " << data.length() << " bytes");
|
||
|
|
||
|
file.write((const char*)data.data(), data.length());
|
||
|
file.close();
|
||
|
|
||
|
callback->reportProgress(100.0);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
dp::String docPath;
|
||
|
dp::String tempPath;
|
||
|
};
|
||
|
|
||
|
class MockProvider : public dpdev::DeviceProvider
|
||
|
{
|
||
|
|
||
|
public:
|
||
|
MockProvider(dpdev::Device* device):device(device) {}
|
||
|
void getIdentifier() {LOG_FUNC();}
|
||
|
virtual int getIndex() {LOG_FUNC(); return 0;}
|
||
|
virtual dpdev::Device* getDevice( int index ){
|
||
|
LOG_FUNC();
|
||
|
if (index == 0)
|
||
|
return device;
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
virtual bool mount(const dp::String& root, const dp::String& name, const dp::String& type)
|
||
|
{
|
||
|
LOG(INFO, "Mount " << root.utf8() << " " << name.utf8() << " " << type.utf8());
|
||
|
return true;
|
||
|
}
|
||
|
virtual bool unmount(const dp::String& root) {LOG_FUNC(); return true;}
|
||
|
virtual void mockproviderfn0(void) {LOG_FUNC();}
|
||
|
virtual void mockproviderfn1(void) {LOG_FUNC();}
|
||
|
virtual void mockproviderfn2(void) {LOG_FUNC();}
|
||
|
virtual void mockproviderfn3(void) {LOG_FUNC();}
|
||
|
|
||
|
private:
|
||
|
dpdev::Device* device;
|
||
|
};
|
||
|
|
||
|
|
||
|
class MockDevice : public dpdev::Device, public mdom::DocumentHandler
|
||
|
{
|
||
|
public:
|
||
|
class InvalidDeviceFile : public std::exception {
|
||
|
const char * what () const throw () {
|
||
|
return "Invalid device File";
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class InvalidDeviceKeyFile : public std::exception {
|
||
|
const char * what () const throw () {
|
||
|
return "Invalid device key File";
|
||
|
}
|
||
|
};
|
||
|
|
||
|
MockDevice(dpdrm::DRMProcessorClient* processorClient, const char* deviceFile, const char* activationFile, const char* devkeyFile) :
|
||
|
deviceProvider(0), processorClient(processorClient), activationFile(activationFile)
|
||
|
{
|
||
|
partition = new MockPartition(this, 0, "root", "Fixed", "/", "/tmp", "/tmp");
|
||
|
|
||
|
readFile(activationFile, &activationRecord, &activationRecordLength);
|
||
|
|
||
|
uft::String librmsdkVersion = dp::getVersionInfo("hobbes");
|
||
|
|
||
|
/* Default values */
|
||
|
dp::setVersionInfo("hobbes", "9.2.38311");
|
||
|
dp::setVersionInfo("clientVersion", "Boo Reader");
|
||
|
dp::setVersionInfo("clientOS", "Linux 2.6.32 armv7l");
|
||
|
dp::setVersionInfo("clientLocale", "fr");
|
||
|
|
||
|
unsigned char* deviceXML;
|
||
|
readFile(deviceFile, &deviceXML, 0, true);
|
||
|
|
||
|
wisdom = adept::parseXML((const char*)deviceXML);
|
||
|
if (!wisdom)
|
||
|
throw MockDevice::InvalidDeviceFile();
|
||
|
mdom::Node node = wisdom->getRoot();
|
||
|
node.walkBranch(this);
|
||
|
|
||
|
uft::String deviceVersion = dp::getVersionInfo("hobbes");
|
||
|
if (librmsdkVersion != deviceVersion)
|
||
|
{
|
||
|
LOG(INFO, "Device RMSDK version " << deviceVersion.utf8());
|
||
|
}
|
||
|
|
||
|
// deviceXML is already freed...
|
||
|
// delete[] deviceXML;
|
||
|
|
||
|
readFile(devkeyFile, &devkey, &devkeyLength);
|
||
|
|
||
|
if (devkeyLength != 16)
|
||
|
throw MockDevice::InvalidDeviceKeyFile();
|
||
|
}
|
||
|
|
||
|
void setProvider(dpdev::DeviceProvider* deviceProvider)
|
||
|
{this->deviceProvider = deviceProvider;}
|
||
|
|
||
|
void setProcessorClient(dpdrm::DRMProcessorClient* processorClient)
|
||
|
{this->processorClient = processorClient;}
|
||
|
|
||
|
virtual void * getOptionalInterface( const char * name ) {LOG_FUNC(); return 0;}
|
||
|
virtual void prepareDeviceKey() {LOG_FUNC();}
|
||
|
virtual dpdev::DeviceProvider* getProvider() {LOG_FUNC(); return deviceProvider;}
|
||
|
virtual int getIndex() {LOG_FUNC(); return 0;}
|
||
|
virtual dp::String getDeviceName() {LOG_FUNC(); return deviceName;}
|
||
|
virtual dp::Data getDeviceKey() {LOG_FUNC(); return dp::Data(devkey, devkeyLength);}
|
||
|
virtual dp::String getDeviceType() {LOG_FUNC(); return deviceType;}
|
||
|
virtual dp::Data getFingerprint() {LOG_FUNC(); return fingerprint;}
|
||
|
virtual dp::Data getActivationRecord() {LOG_FUNC(); return dp::Data(activationRecord, activationRecordLength);}
|
||
|
virtual void setActivationRecord(const dp::Data& data);
|
||
|
virtual dpio::Partition* getPartition(int index) {LOG_FUNC(); return partition;}
|
||
|
virtual dp::String getVersionInfo(const dp::String& name);
|
||
|
virtual bool isTrusted() {LOG_FUNC();return true;};
|
||
|
|
||
|
/* mdom::DocumentHandler */
|
||
|
virtual bool characters(uft::Value const&) {return true;}
|
||
|
virtual bool comment(uft::Value const&) {return true;}
|
||
|
virtual bool endDocument() {return true;}
|
||
|
virtual bool endElement(uft::Value const& p0, uft::Value const& p1, uft::Value const& p2) {return true;}
|
||
|
virtual bool endEntity(uft::Value const&) {return true;}
|
||
|
virtual bool processingInstruction(uft::Value const&, uft::Value const&) {return true;}
|
||
|
virtual bool startDocument() {return true;}
|
||
|
virtual bool startElement(mdom::Node const& node, uft::Value const& xmlns, uft::Value const& element, uft::Value const& ns, mdom::NameValueIterator* attributesIterator) {
|
||
|
if (!element.isNull() && element.isString())
|
||
|
{
|
||
|
char* name = element.toString().c_str();
|
||
|
uft::String value = adept::getChildValue(node, 3);
|
||
|
|
||
|
if (!strcmp(name, "deviceClass"))
|
||
|
deviceClass = dp::String(value.utf8());
|
||
|
else if (!strcmp(name, "deviceSerial"))
|
||
|
deviceSerial = dp::String(value.utf8());
|
||
|
else if (!strcmp(name, "deviceName"))
|
||
|
deviceName = dp::String(value.utf8());
|
||
|
else if (!strcmp(name, "deviceType"))
|
||
|
deviceType = dp::String(value.utf8());
|
||
|
// <adept:version name="hobbes" value="9.2.38311">
|
||
|
else if (!strcmp(name, "version"))
|
||
|
{
|
||
|
/* Hardcode decode because p0 is a structure */
|
||
|
uft::Value p0, p1;
|
||
|
attributesIterator->next(&p0, &p1);
|
||
|
char* attrName = p1.toString().c_str();
|
||
|
attributesIterator->next(&p0, &p1);
|
||
|
char* attrValue = p1.toString().c_str();
|
||
|
if (attrName && attrValue)
|
||
|
dp::setVersionInfo(attrName, attrValue);
|
||
|
}
|
||
|
else if (!strcmp(name, "fingerprint"))
|
||
|
{
|
||
|
unsigned char rawFingerprint[32];
|
||
|
int res = dp::decodeBase64(value.c_str(), rawFingerprint, sizeof(rawFingerprint));
|
||
|
fingerprint = dp::Data(rawFingerprint, res);
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
virtual bool startEntity(uft::Value const&) {return true;}
|
||
|
|
||
|
static int readFile(const char* filePath, unsigned char** res, int* length = 0, bool addFinalZero=false)
|
||
|
{
|
||
|
int _length, fd;
|
||
|
QFile file(filePath);
|
||
|
|
||
|
*res = 0;
|
||
|
if (!length) length = &_length;
|
||
|
|
||
|
if (!file.exists())
|
||
|
{
|
||
|
*length = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (addFinalZero)
|
||
|
*res = new unsigned char[file.size()+1];
|
||
|
else
|
||
|
*res = new unsigned char[file.size()];
|
||
|
fd = open(filePath, O_RDONLY);
|
||
|
*length = read(fd, *res, file.size());
|
||
|
close (fd);
|
||
|
if (*length > 0 && addFinalZero)
|
||
|
{
|
||
|
(*res)[*length] = 0;
|
||
|
*length += 1;
|
||
|
}
|
||
|
|
||
|
return *length;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
dpdev::DeviceProvider* deviceProvider;
|
||
|
dpdrm::DRMProcessorClient* processorClient;
|
||
|
const char *activationFile;
|
||
|
unsigned char *activationRecord;
|
||
|
int activationRecordLength;
|
||
|
unsigned char *devkey;
|
||
|
int devkeyLength;
|
||
|
dpio::Partition* partition;
|
||
|
dp::String deviceSerial;
|
||
|
dp::String deviceClass;
|
||
|
dp::String deviceName;
|
||
|
dp::String deviceType;
|
||
|
dp::Data fingerprint;
|
||
|
MetroWisDOM* wisdom;
|
||
|
};
|
||
|
|
||
|
void MockDevice::setActivationRecord( const dp::Data& data ) {
|
||
|
LOG_FUNC();
|
||
|
if (activationRecord)
|
||
|
delete[] activationRecord;
|
||
|
activationRecordLength = data.length();
|
||
|
activationRecord = new unsigned char[activationRecordLength];
|
||
|
memcpy(activationRecord, data.data(), activationRecordLength);
|
||
|
|
||
|
LOG(DEBUG, "New activation record :" << std::endl << activationRecord);
|
||
|
|
||
|
if (activationFile)
|
||
|
{
|
||
|
QFile file(activationFile);
|
||
|
|
||
|
bool ret = file.open(QIODevice::WriteOnly|QIODevice::Truncate);
|
||
|
if (!ret)
|
||
|
{
|
||
|
LOG(ERROR, "Unable to open file " << activationFile);
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
LOG(WARN, "Write file " << activationFile);
|
||
|
|
||
|
file.write((const char*)data.data(), data.length());
|
||
|
file.close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dp::String MockDevice::getVersionInfo( const dp::String& name ) {
|
||
|
LOG_FUNC();
|
||
|
LOG(DEBUG, ">>> " << name.utf8());
|
||
|
dp::String res = dp::getVersionInfo(name);
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
class MockStream : public dpio::Stream {
|
||
|
public:
|
||
|
MockStream(const dp::String& method, const dp::String& url, dpio::StreamClient* client,
|
||
|
dpio::Stream* postData):
|
||
|
method(method), url(url), client(client)
|
||
|
{
|
||
|
|
||
|
LOG(DEBUG, "New stream " << method.utf8() << " " << url.utf8());
|
||
|
|
||
|
QNetworkRequest request(QUrl(url.utf8()));
|
||
|
request.setRawHeader("Accept", "*/*");
|
||
|
request.setRawHeader("User-Agent", "book2png");
|
||
|
if (method.uft() == "POST")
|
||
|
{
|
||
|
request.setRawHeader("Content-Type", "application/vnd.adobe.adept+xml");
|
||
|
dp::Data data = dpio::Stream::readSynchronousStream(postData);
|
||
|
LOG(DEBUG, "Len " << data.length());
|
||
|
LOG(DEBUG, "data " << data.data());
|
||
|
reply = networkManager.post(request, QByteArray((const char*)data.data(), data.length()));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
reply = networkManager.get(request);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void release() {LOG_FUNC();}
|
||
|
virtual void setStreamClient(dpio::StreamClient * client)
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
this->client = client;
|
||
|
}
|
||
|
virtual int getCapabilities()
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
return 1;
|
||
|
}
|
||
|
virtual void requestInfo()
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
|
||
|
QCoreApplication* app = QCoreApplication::instance();
|
||
|
networkManager.moveToThread(app->thread());
|
||
|
while (!reply->isFinished())
|
||
|
app->processEvents();
|
||
|
requestBytes(0, -1);
|
||
|
}
|
||
|
|
||
|
virtual void requestBytes(unsigned int offset, unsigned int len)
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
|
||
|
LOG(DEBUG, "Offset " << offset << ", Len " << len);
|
||
|
|
||
|
if (!client)
|
||
|
{
|
||
|
LOG(ERROR, "Error, No client");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
size_t bytes = (size_t)reply->bytesAvailable();
|
||
|
QByteArray _replyData = reply->readAll();
|
||
|
const unsigned char* replyData = (const unsigned char*)_replyData.constData();
|
||
|
LOG(DEBUG, "Reply " << bytes << " bytes");
|
||
|
|
||
|
if (reply->hasRawHeader("Content-Type"))
|
||
|
{
|
||
|
client->propertyReady("Content-Type", reply->rawHeader("Content-Type").constData());
|
||
|
LOG(DEBUG, "Content-Type " << reply->rawHeader("Content-Type").constData());
|
||
|
if (!strcmp(reply->rawHeader("Content-Type").constData(), "application/vnd.adobe.adept+xml"))
|
||
|
LOG(DEBUG, "<<< " << replyData);
|
||
|
}
|
||
|
if (reply->hasRawHeader("Content-Length"))
|
||
|
client->propertyReady("Content-Length", reply->rawHeader("Content-Length").constData());
|
||
|
|
||
|
client->totalLengthReady(bytes);
|
||
|
client->bytesReady(0, dp::Data(replyData, bytes), true);
|
||
|
}
|
||
|
|
||
|
virtual void reportWriteError(const dp::String& error)
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
}
|
||
|
|
||
|
virtual void adjustModifiedStream(unsigned int offset, unsigned int len, void* param2, dpio::StreamClient* client)
|
||
|
{
|
||
|
LOG_FUNC();
|
||
|
setStreamClient(client);
|
||
|
requestBytes(offset, len);
|
||
|
}
|
||
|
virtual void streamfn2() {LOG_FUNC();}
|
||
|
virtual void streamfn3() {LOG_FUNC();}
|
||
|
virtual void streamfn4() {LOG_FUNC();}
|
||
|
|
||
|
private:
|
||
|
dp::String method;
|
||
|
dp::String url;
|
||
|
dpio::StreamClient *client;
|
||
|
QNetworkAccessManager networkManager;
|
||
|
QNetworkReply *reply;
|
||
|
};
|
||
|
|
||
|
class MockNetProvider : public dpnet::NetProvider
|
||
|
{
|
||
|
virtual void netfn0() {LOG_FUNC();}
|
||
|
virtual void netfn1() {LOG_FUNC();}
|
||
|
virtual void netfn2() {LOG_FUNC();}
|
||
|
virtual void netfn3() {LOG_FUNC();}
|
||
|
virtual dpio::Stream * netfn4(dp::String& method, dp::String& url, adept::UrlLoader<adept::DRMProcessorImpl>* client, unsigned int param1, dpio::Stream* stream) {
|
||
|
LOG_FUNC();
|
||
|
return new MockStream(method, url, (dpio::StreamClient*)(client), stream);
|
||
|
}
|
||
|
virtual void netfn5() {LOG_FUNC();}
|
||
|
virtual void netfn6() {LOG_FUNC();}
|
||
|
};
|
||
|
|
||
|
#endif
|