Add resume option to acsmdownloader
This commit is contained in:
parent
5d3112bc38
commit
2f2e4e193e
|
@ -99,10 +99,11 @@ namespace gourou
|
|||
* @param contentType Optional content type of POST Data
|
||||
* @param responseHeaders Optional Response headers of HTTP request
|
||||
* @param fd Optional file descriptor to write request result
|
||||
* @param resume false if target file should be truncated, true to try resume download (works only in combination with a valid fd)
|
||||
*
|
||||
* @return data of HTTP response
|
||||
*/
|
||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0, int fd=0) = 0;
|
||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0, int fd=0, bool resume=false) = 0;
|
||||
};
|
||||
|
||||
class RSAInterface
|
||||
|
|
|
@ -81,10 +81,11 @@ namespace gourou
|
|||
*
|
||||
* @param item Item from fulfill() method
|
||||
* @param path Output file path
|
||||
* @param resume false if target file should be truncated, true to try resume download
|
||||
*
|
||||
* @return Type of downloaded item
|
||||
*/
|
||||
ITEM_TYPE download(FulfillmentItem* item, std::string path);
|
||||
ITEM_TYPE download(FulfillmentItem* item, std::string path, bool resume=false);
|
||||
|
||||
/**
|
||||
* @brief SignIn into ACS Server (required to activate device)
|
||||
|
@ -135,10 +136,11 @@ namespace gourou
|
|||
* @param contentType Optional content type of POST Data
|
||||
* @param responseHeaders Optional Response headers of HTTP request
|
||||
* @param fd Optional File descriptor to write received data
|
||||
* @param resume false if target file should be truncated, true to try resume download (works only in combination of a valid fd)
|
||||
*
|
||||
* @return data of HTTP response
|
||||
*/
|
||||
ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map<std::string, std::string>* responseHeaders=0, int fd=0);
|
||||
ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map<std::string, std::string>* responseHeaders=0, int fd=0, bool resume=false);
|
||||
|
||||
/**
|
||||
* @brief Send HTTP POST request to URL with document as POSTData
|
||||
|
|
|
@ -287,13 +287,19 @@ namespace gourou
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Open a file descriptor on path. If it already exists, it's truncated
|
||||
* @brief Open a file descriptor on path. If it already exists and truncate == true, it's truncated
|
||||
*
|
||||
* @return Created fd, must be closed
|
||||
*/
|
||||
static inline int createNewFile(std::string path)
|
||||
static inline int createNewFile(std::string path, bool truncate=true)
|
||||
{
|
||||
int fd = open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||
int options = O_CREAT|O_WRONLY;
|
||||
if (truncate)
|
||||
options |= O_TRUNC;
|
||||
else
|
||||
options |= O_APPEND;
|
||||
|
||||
int fd = open(path.c_str(), options, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||
|
||||
if (fd <= 0)
|
||||
EXCEPTION(GOUROU_FILE_ERROR, "Unable to create " << path);
|
||||
|
|
|
@ -300,11 +300,11 @@ namespace gourou
|
|||
appendTextElem(root, "adept:expiration", buffer);
|
||||
}
|
||||
|
||||
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map<std::string, std::string>* responseHeaders, int fd)
|
||||
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map<std::string, std::string>* responseHeaders, int fd, bool resume)
|
||||
{
|
||||
if (contentType == 0)
|
||||
contentType = "";
|
||||
std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders, fd);
|
||||
std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders, fd, resume);
|
||||
|
||||
if (fd) return ByteArray();
|
||||
|
||||
|
@ -583,7 +583,7 @@ namespace gourou
|
|||
return new FulfillmentItem(fulfillReply, user);
|
||||
}
|
||||
|
||||
DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path)
|
||||
DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path, bool resume)
|
||||
{
|
||||
ITEM_TYPE res = EPUB;
|
||||
|
||||
|
@ -592,9 +592,9 @@ namespace gourou
|
|||
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
int fd = createNewFile(path);
|
||||
int fd = createNewFile(path, !resume);
|
||||
|
||||
sendRequest(item->getDownloadURL(), "", 0, &headers, fd);
|
||||
sendRequest(item->getDownloadURL(), "", 0, &headers, fd, resume);
|
||||
|
||||
close(fd);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ static const char* acsmFile = 0;
|
|||
static bool exportPrivateKey = false;
|
||||
static const char* outputFile = 0;
|
||||
static const char* outputDir = 0;
|
||||
static bool resume = false;
|
||||
|
||||
|
||||
class ACSMDownloader
|
||||
|
@ -104,7 +105,7 @@ public:
|
|||
filename = std::string(outputDir) + "/" + filename;
|
||||
}
|
||||
|
||||
gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename);
|
||||
gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename, resume);
|
||||
|
||||
if (!outputFile)
|
||||
{
|
||||
|
@ -133,7 +134,7 @@ static void usage(const char* cmd)
|
|||
{
|
||||
std::cout << "Download EPUB file from ACSM request file" << std::endl;
|
||||
|
||||
std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << std::endl;
|
||||
std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-r|--resume)] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << std::endl;
|
||||
|
||||
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
|
||||
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
|
||||
|
@ -142,6 +143,7 @@ static void usage(const char* cmd)
|
|||
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default <title.(epub|pdf|der)>)" << std::endl;
|
||||
std::cout << " " << "-f|--acsm-file" << "\t" << "ACSM request file for epub download" << std::endl;
|
||||
std::cout << " " << "-e|--export-private-key"<< "\t" << "Export private key in DER format" << std::endl;
|
||||
std::cout << " " << "-r|--resume" << "\t\t" << "Try to resume download (in case of previous failure)" << std::endl;
|
||||
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
||||
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
||||
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
||||
|
@ -171,13 +173,14 @@ int main(int argc, char** argv)
|
|||
{"output-file", required_argument, 0, 'o' },
|
||||
{"acsm-file", required_argument, 0, 'f' },
|
||||
{"export-private-key",no_argument, 0, 'e' },
|
||||
{"resume", no_argument, 0, 'r' },
|
||||
{"verbose", no_argument, 0, 'v' },
|
||||
{"version", no_argument, 0, 'V' },
|
||||
{"help", no_argument, 0, 'h' },
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "d:a:k:O:o:f:evVh",
|
||||
c = getopt_long(argc, argv, "d:a:k:O:o:f:ervVh",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -204,6 +207,9 @@ int main(int argc, char** argv)
|
|||
case 'e':
|
||||
exportPrivateKey = true;
|
||||
break;
|
||||
case 'r':
|
||||
resume = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
|
|
|
@ -175,7 +175,7 @@ static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userda
|
|||
return size*nitems;
|
||||
}
|
||||
|
||||
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders, int fd)
|
||||
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders, int fd, bool resume)
|
||||
{
|
||||
gourou::ByteArray replyData;
|
||||
std::map<std::string, std::string> localHeaders;
|
||||
|
@ -191,6 +191,18 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||
|
||||
unsigned prevDownloadedBytes;
|
||||
downloadedBytes = 0;
|
||||
if (fd && resume)
|
||||
{
|
||||
struct stat _stat;
|
||||
if (!fstat(fd, &_stat))
|
||||
{
|
||||
GOUROU_LOG(gourou::WARN, "Resume download @ " << _stat.st_size << " bytes");
|
||||
downloadedBytes = _stat.st_size;
|
||||
}
|
||||
else
|
||||
GOUROU_LOG(gourou::WARN, "Want to resume, but fstat failed");
|
||||
}
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
CURLcode res;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
|
||||
|
@ -244,7 +256,7 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||
// Connexion failed, wait & retry
|
||||
if (res == CURLE_COULDNT_CONNECT)
|
||||
{
|
||||
GOUROU_LOG(gourou::WARN, "Connection failed, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
GOUROU_LOG(gourou::WARN, "\nConnection failed, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
}
|
||||
// Transfer failed but some data has been received
|
||||
// --> try again without incrementing tries
|
||||
|
@ -252,11 +264,11 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||
{
|
||||
if (prevDownloadedBytes != downloadedBytes)
|
||||
{
|
||||
GOUROU_LOG(gourou::WARN, "Connection broken, but data received, try again");
|
||||
GOUROU_LOG(gourou::WARN, "\nConnection broken, but data received, try again");
|
||||
i--;
|
||||
}
|
||||
else
|
||||
GOUROU_LOG(gourou::WARN, "Connection broken and no data received, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
GOUROU_LOG(gourou::WARN, "\nConnection broken and no data received, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||
}
|
||||
// Other error --> fail
|
||||
else
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
virtual void randBytes(unsigned char* bytesOut, unsigned int length);
|
||||
|
||||
/* HTTP interface */
|
||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0, int fd=0);
|
||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0, int fd=0, bool resume=false);
|
||||
|
||||
virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||
const RSA_KEY_TYPE keyType, const std::string& password,
|
||||
|
|
Loading…
Reference in New Issue
Block a user