Episode: Moved Cookie and Util Functions to Separate File
This commit is contained in:
parent
19a379bd04
commit
6d80659af7
|
@ -0,0 +1,135 @@
|
|||
#include "cookie.h"
|
||||
|
||||
|
||||
// Cookie functions
|
||||
namespace dropout_dl {
|
||||
void cookie::get_value_from_db(sqlite3 *db, const std::string &sql_query_base, const std::string& value, bool verbose, int (*callback)(void*,int,char**,char**)) {
|
||||
#ifdef DROPOUT_DL_SQLITE
|
||||
std::string sql_mod_base = sql_query_base;
|
||||
|
||||
if (sql_mod_base.find("WHERE") == std::string::npos) {
|
||||
sql_mod_base += " WHERE ";
|
||||
}
|
||||
else {
|
||||
sql_mod_base += " AND ";
|
||||
}
|
||||
|
||||
sql_mod_base += "name='" + this->name + "';";
|
||||
|
||||
std::string sql_value_query = "SELECT " + value + ' ' + sql_mod_base;
|
||||
std::string sql_length_query = "SELECT length(" + value + ") " + sql_mod_base;
|
||||
std::string tmp;
|
||||
char *err_code = nullptr;
|
||||
int rc;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << sql_value_query << '\n' << sql_length_query << std::endl;
|
||||
}
|
||||
|
||||
rc = sqlite3_exec(db, sql_length_query.c_str(), callback, &tmp, &err_code);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "SQL error: %s\n", err_code);
|
||||
sqlite3_free(err_code);
|
||||
sqlite3_close(db);
|
||||
exit(3);
|
||||
} else if (verbose) {
|
||||
std::cout << "Got " << this->name << " cookie length\n";
|
||||
}
|
||||
|
||||
if (tmp.empty()) {
|
||||
std::cerr << "COOKIE SQLITE ERROR: No Cookie With Name " << this->name << " Exists\n";
|
||||
exit(0);
|
||||
}
|
||||
this->len = std::stoi(tmp);
|
||||
|
||||
rc = sqlite3_exec(db, sql_value_query.c_str(), callback, &tmp, &err_code);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "SQL error: %s\n", err_code);
|
||||
sqlite3_free(err_code);
|
||||
sqlite3_close(db);
|
||||
exit(3);
|
||||
} else if (verbose) {
|
||||
std::cout << "Got " << this->name << " cookie\n";
|
||||
}
|
||||
|
||||
this->value = tmp;
|
||||
#else
|
||||
std::cerr << "COOKIE ERROR: Attempted to get cookies from sqlite without having sqlite installed\n";
|
||||
exit(12);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cookie::format_from_chrome() {
|
||||
this->value = this->value.substr(3);
|
||||
this->len -= 3;
|
||||
}
|
||||
|
||||
void cookie::chrome_decrypt(const std::string &password, int iterations, const std::string &salt, int length) {
|
||||
|
||||
#ifdef DROPOUT_DL_GCRYPT
|
||||
this->format_from_chrome();
|
||||
|
||||
uint8_t key[32];
|
||||
|
||||
char output[this->len + 2];
|
||||
|
||||
char iv[16];
|
||||
|
||||
for (char& c : iv) {
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
for (char& c : output) {
|
||||
c = 0;
|
||||
}
|
||||
|
||||
gcry_kdf_derive(password.c_str(), password.size(), GCRY_KDF_PBKDF2, GCRY_KDF_ARGON2ID, salt.c_str(), salt.size(), iterations, length, key);
|
||||
|
||||
gcry_cipher_hd_t handle;
|
||||
|
||||
gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
|
||||
|
||||
gcry_cipher_setkey(handle, (const void*) &key, length);
|
||||
|
||||
gcry_cipher_setiv(handle, (const void*)&iv, 16);
|
||||
|
||||
unsigned long err = gcry_cipher_decrypt(handle, output, this->len, this->value.c_str(), this->len);
|
||||
|
||||
if (err) {
|
||||
std::cout << gcry_strerror(err) << std::endl;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
|
||||
this->value = output;
|
||||
|
||||
this->url_decode();
|
||||
|
||||
this->value = this->value.substr(0, this->len - 7);
|
||||
this->len -= 7;
|
||||
#else
|
||||
std::cerr << "CHROME COOKIE ERROR: Attempted to Decrypt Chrome Cookie Without libgcrypt\n";
|
||||
exit(12);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cookie::url_decode() {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < this->value.size() - 3; i++) {
|
||||
if (substr_is(this->value, i, "%3D")) {
|
||||
out += "=";
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
out += this->value[i];
|
||||
}
|
||||
}
|
||||
|
||||
this->value = out;
|
||||
this->len = out.size();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
#include <iostream>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace dropout_dl {
|
||||
class cookie {
|
||||
public:
|
||||
/**
|
||||
*
|
||||
* @param data - A string to write to return value of the command to
|
||||
* @param argc - The number of results from the command
|
||||
* @param argv - The results of the command
|
||||
* @param azColName - The column names from the command
|
||||
* @return 0 on success or -1 on failure
|
||||
*
|
||||
* Used by sqlite. Write the first result of the sqlite command to the data string. If there are no results print an error and return -1.
|
||||
*/
|
||||
static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName)
|
||||
{
|
||||
if (argc < 1) {
|
||||
std::cerr << "SQLITE ERROR: sqlite could not find desired cookie" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
*(std::string*)data = argv[0];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the value from the sqlite database or "?" if not set.
|
||||
*/
|
||||
std::string name;
|
||||
/**
|
||||
* The value of the cookie
|
||||
*/
|
||||
std::string value;
|
||||
/**
|
||||
* The length of the value of the cookie
|
||||
*/
|
||||
int len;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - Name of the value from the sqlite database
|
||||
*
|
||||
* Create a cookie with no value and length of 0
|
||||
*/
|
||||
explicit cookie(const std::string& name) {
|
||||
this->name = name;
|
||||
this->value = "";
|
||||
this->len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - Name of the value from the sqlite database
|
||||
* @param cookie - Value of the cookie
|
||||
*
|
||||
* Sets the name and value using the parameters and gets the length from the value
|
||||
*/
|
||||
cookie(const std::string& name, const std::string& cookie) {
|
||||
this->name = name;
|
||||
this->value = cookie;
|
||||
this->len = cookie.size();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cookie - Value of the cookie
|
||||
* @param length - Length of the cookie
|
||||
*
|
||||
* Sets the value and length using the parameters and sets the name as "?"
|
||||
*/
|
||||
cookie(const std::string& cookie, int length) {
|
||||
this->value = cookie;
|
||||
this->name = "?";
|
||||
this->len = length;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - Name of the value from the sqlite database
|
||||
* @param cookie - Value of the cookie
|
||||
* @param length - Length of the cookie
|
||||
*
|
||||
* Sets the name, value, and length using the parameters leaving nothing unset.
|
||||
*/
|
||||
cookie(const std::string& name, const std::string& cookie, int length) {
|
||||
this->name = name;
|
||||
this->value = cookie;
|
||||
this->len = length;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param db - An sqlite3 database
|
||||
* @param sql_query_base - A base without the name search e.g. "FROM cookies" this function would then append the text "SELECT <value>" and "WHERE name='<name>'"
|
||||
* @param value - The name of the value to fill the cookie with
|
||||
*
|
||||
* Retrieve the value of a cookie from the provided sqlite database.
|
||||
*
|
||||
*/
|
||||
void get_value_from_db(sqlite3* db, const std::string& sql_query_base, const std::string& value, bool verbose = false, int (*callback)(void*,int,char**,char**) = sqlite_write_callback);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param password - Default is "peanuts". This works for linux. The password should be keychain password on MacOS
|
||||
* @param salt - Salt is "saltysalt" for both MacOS and Linux
|
||||
* @param length - Length of 16 is standard for both MacOS and Linux
|
||||
* @param iterations - 1 on linux and 1003 on MacOS
|
||||
*
|
||||
* Decrypt chrome cookies and format them to be usable as regular cookies. Currently this has only been tested for the _session and __cf_bm cookies from dropout.tv but I see no reason this would not work for anything else.
|
||||
*/
|
||||
void chrome_decrypt(const std::string& password = "peanuts", int iterations = 1, const std::string& salt = "saltysalt", int length = 16);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Remove url encoded text from a cookie. Currently this only checks for %3D ('=') as that is the only thing I've come across in cookies during the entirety of this project.
|
||||
*/
|
||||
void url_decode();
|
||||
|
||||
/**
|
||||
* Remove the leading version (e.g. "v10") from the cookie
|
||||
*/
|
||||
void format_from_chrome();
|
||||
};
|
||||
}
|
311
src/episode.cpp
311
src/episode.cpp
|
@ -1,186 +1,10 @@
|
|||
//
|
||||
// Created by moss on 9/28/22.
|
||||
//
|
||||
|
||||
#include "episode.h"
|
||||
|
||||
|
||||
namespace dropout_dl {
|
||||
|
||||
// dropout-dl helpers
|
||||
bool substr_is(const std::string& string, int start, const std::string& test_str) {
|
||||
if (test_str.size() != test_str.size())
|
||||
return false;
|
||||
|
||||
for (int i = start, j = 0; i < start + test_str.size(); i++, j++) {
|
||||
if (string[i] != test_str[j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void replace_all(std::string& str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string remove_leading_and_following_whitespace(const std::string& str) {
|
||||
int start = 0;
|
||||
int end = str.length() - 1;
|
||||
|
||||
for (; str[start] == ' ' || str[start] == '\t' || str[start] == '\n'; start++);
|
||||
for (; str[end] == ' ' || str[end] == '\t' || str[end] == '\n'; end--);
|
||||
|
||||
return str.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
std::string replace_html_character_codes(const std::string& str) {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
if (substr_is(str, i, "&#")) {
|
||||
i += 2;
|
||||
char code = 0;
|
||||
|
||||
if (i > str.size() - 4) {
|
||||
if (str[str.size() - 1] == ';') {
|
||||
// Numerical character code length is two at the end of the string
|
||||
|
||||
code = str[str.size() - 2] - '0';
|
||||
code += (str[str.size() - 3] - '0') * 10;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (str[i + 3] == ';') {
|
||||
// Numerical character code length is three
|
||||
code = str[i + 2] - '0';
|
||||
code += (str[i + 1] - '0') * 10;
|
||||
code += (str[i] - '0') * 10;
|
||||
i += 3;
|
||||
}
|
||||
else if (str[i + 2] == ';'){
|
||||
code = str[i + 1] - '0';
|
||||
code += (str[i] - '0') * 10;
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
std::cerr << "HTML CHAR CODE ERROR: Code with numerical length of one used\n";
|
||||
exit(11);
|
||||
}
|
||||
}
|
||||
|
||||
if (code < 32) {
|
||||
std::cerr << "HTML CHAR CODE ERROR: Control Character Decoded. This is not supported and likely an error.\n";
|
||||
exit(11);
|
||||
}
|
||||
|
||||
out += code;
|
||||
}
|
||||
else {
|
||||
out += str[i];
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string remove_escaped_characters(const std::string& str) {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
if (str[i] == '\\') {
|
||||
i++;
|
||||
}
|
||||
out += str[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string format_name_string(const std::string& str) {
|
||||
return remove_escaped_characters(replace_html_character_codes(remove_leading_and_following_whitespace(str)));
|
||||
}
|
||||
|
||||
std::string format_filename(const std::string& str) {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
char c = str[i];
|
||||
|
||||
// Skip these
|
||||
if (c == '?' || c == ':' || c == '\\') {
|
||||
continue;
|
||||
}
|
||||
// Replace these with dashes
|
||||
else if (c == '/') {
|
||||
out += '-';
|
||||
}
|
||||
else {
|
||||
out += c;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include <windows.h>
|
||||
msec_t time_ms(void)
|
||||
{
|
||||
return timeGetTime();
|
||||
}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
long time_ms()
|
||||
{
|
||||
timeval tv{};
|
||||
gettimeofday(&tv, nullptr);
|
||||
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
long current_time;
|
||||
long last_progress_timestamp;
|
||||
|
||||
int curl_progress_func(void* filename, double total_to_download, double downloaded, double total_to_upload, double uploaded) {
|
||||
const double number_chars = 50;
|
||||
const char* full_character = "▓";
|
||||
const char* empty_character = "░";
|
||||
|
||||
current_time = time_ms();
|
||||
if (current_time - 50 > last_progress_timestamp) {
|
||||
// Percent of the file downloadede. Adding one to round up so that when its done it shows as a full bar rather than missing one.
|
||||
double percent_done = ((downloaded / total_to_download) * number_chars) + 1;
|
||||
double percent_done_clone = percent_done;
|
||||
std::cout << *(std::string*)filename << " [";
|
||||
while (percent_done_clone-- > 0) {
|
||||
std::cout << full_character;
|
||||
}
|
||||
while (percent_done++ < number_chars) {
|
||||
std::cout << empty_character;
|
||||
}
|
||||
std::cout << "] " << downloaded / 1048576 << "MiB / " << total_to_download / 1048576 << "MiB ";
|
||||
putchar('\r');
|
||||
last_progress_timestamp = time_ms();
|
||||
std::cout.flush();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
std::string episode::get_meta_data_json(const std::string& html_data) {
|
||||
std::string data_start("window.Page = {");
|
||||
char data_open = '{';
|
||||
|
@ -506,7 +330,7 @@ namespace dropout_dl {
|
|||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dropout_dl::WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, dropout_dl::curl_progress_func);
|
||||
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, dropout_dl::curl_progress_func);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &filename);
|
||||
res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
|
@ -568,137 +392,4 @@ namespace dropout_dl {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Cookie functions
|
||||
|
||||
void cookie::get_value_from_db(sqlite3 *db, const std::string &sql_query_base, const std::string& value, bool verbose, int (*callback)(void*,int,char**,char**)) {
|
||||
#ifdef DROPOUT_DL_SQLITE
|
||||
std::string sql_mod_base = sql_query_base;
|
||||
|
||||
if (sql_mod_base.find("WHERE") == std::string::npos) {
|
||||
sql_mod_base += " WHERE ";
|
||||
}
|
||||
else {
|
||||
sql_mod_base += " AND ";
|
||||
}
|
||||
|
||||
sql_mod_base += "name='" + this->name + "';";
|
||||
|
||||
std::string sql_value_query = "SELECT " + value + ' ' + sql_mod_base;
|
||||
std::string sql_length_query = "SELECT length(" + value + ") " + sql_mod_base;
|
||||
std::string tmp;
|
||||
char *err_code = nullptr;
|
||||
int rc;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << sql_value_query << '\n' << sql_length_query << std::endl;
|
||||
}
|
||||
|
||||
rc = sqlite3_exec(db, sql_length_query.c_str(), callback, &tmp, &err_code);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "SQL error: %s\n", err_code);
|
||||
sqlite3_free(err_code);
|
||||
sqlite3_close(db);
|
||||
exit(3);
|
||||
} else if (verbose) {
|
||||
std::cout << "Got " << this->name << " cookie length\n";
|
||||
}
|
||||
|
||||
if (tmp.empty()) {
|
||||
std::cerr << "COOKIE SQLITE ERROR: No Cookie With Name " << this->name << " Exists\n";
|
||||
exit(0);
|
||||
}
|
||||
this->len = std::stoi(tmp);
|
||||
|
||||
rc = sqlite3_exec(db, sql_value_query.c_str(), callback, &tmp, &err_code);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "SQL error: %s\n", err_code);
|
||||
sqlite3_free(err_code);
|
||||
sqlite3_close(db);
|
||||
exit(3);
|
||||
} else if (verbose) {
|
||||
std::cout << "Got " << this->name << " cookie\n";
|
||||
}
|
||||
|
||||
this->value = tmp;
|
||||
#else
|
||||
std::cerr << "COOKIE ERROR: Attempted to get cookies from sqlite without having sqlite installed\n";
|
||||
exit(12);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cookie::format_from_chrome() {
|
||||
this->value = this->value.substr(3);
|
||||
this->len -= 3;
|
||||
}
|
||||
|
||||
void cookie::chrome_decrypt(const std::string &password, int iterations, const std::string &salt, int length) {
|
||||
|
||||
#ifdef DROPOUT_DL_GCRYPT
|
||||
this->format_from_chrome();
|
||||
|
||||
uint8_t key[32];
|
||||
|
||||
char output[this->len + 2];
|
||||
|
||||
char iv[16];
|
||||
|
||||
for (char& c : iv) {
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
for (char& c : output) {
|
||||
c = 0;
|
||||
}
|
||||
|
||||
gcry_kdf_derive(password.c_str(), password.size(), GCRY_KDF_PBKDF2, GCRY_KDF_ARGON2ID, salt.c_str(), salt.size(), iterations, length, key);
|
||||
|
||||
gcry_cipher_hd_t handle;
|
||||
|
||||
gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
|
||||
|
||||
gcry_cipher_setkey(handle, (const void*) &key, length);
|
||||
|
||||
gcry_cipher_setiv(handle, (const void*)&iv, 16);
|
||||
|
||||
unsigned long err = gcry_cipher_decrypt(handle, output, this->len, this->value.c_str(), this->len);
|
||||
|
||||
if (err) {
|
||||
std::cout << gcry_strerror(err) << std::endl;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
|
||||
this->value = output;
|
||||
|
||||
this->url_decode();
|
||||
|
||||
this->value = this->value.substr(0, this->len - 7);
|
||||
this->len -= 7;
|
||||
#else
|
||||
std::cerr << "CHROME COOKIE ERROR: Attempted to Decrypt Chrome Cookie Without libgcrypt\n";
|
||||
exit(12);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cookie::url_decode() {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < this->value.size() - 3; i++) {
|
||||
if (substr_is(this->value, i, "%3D")) {
|
||||
out += "=";
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
out += this->value[i];
|
||||
}
|
||||
}
|
||||
|
||||
this->value = out;
|
||||
this->len = out.size();
|
||||
}
|
||||
} // dropout_dl
|
||||
|
|
238
src/episode.h
238
src/episode.h
|
@ -9,244 +9,14 @@
|
|||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "color.h"
|
||||
#include "cookie.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#ifdef DROPOUT_DL_GCRYPT
|
||||
#include <gcrypt.h>
|
||||
#endif
|
||||
|
||||
namespace dropout_dl {
|
||||
|
||||
class cookie {
|
||||
public:
|
||||
/**
|
||||
*
|
||||
* @param data - A string to write to return value of the command to
|
||||
* @param argc - The number of results from the command
|
||||
* @param argv - The results of the command
|
||||
* @param azColName - The column names from the command
|
||||
* @return 0 on success or -1 on failure
|
||||
*
|
||||
* Used by sqlite. Write the first result of the sqlite command to the data string. If there are no results print an error and return -1.
|
||||
*/
|
||||
static int sqlite_write_callback(void* data, int argc, char** argv, char** azColName)
|
||||
{
|
||||
if (argc < 1) {
|
||||
std::cerr << "SQLITE ERROR: sqlite could not find desired cookie" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
*(std::string*)data = argv[0];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the value from the sqlite database or "?" if not set.
|
||||
*/
|
||||
std::string name;
|
||||
/**
|
||||
* The value of the cookie
|
||||
*/
|
||||
std::string value;
|
||||
/**
|
||||
* The length of the value of the cookie
|
||||
*/
|
||||
int len;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - Name of the value from the sqlite database
|
||||
*
|
||||
* Create a cookie with no value and length of 0
|
||||
*/
|
||||
explicit cookie(const std::string& name) {
|
||||
this->name = name;
|
||||
this->value = "";
|
||||
this->len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - Name of the value from the sqlite database
|
||||
* @param cookie - Value of the cookie
|
||||
*
|
||||
* Sets the name and value using the parameters and gets the length from the value
|
||||
*/
|
||||
cookie(const std::string& name, const std::string& cookie) {
|
||||
this->name = name;
|
||||
this->value = cookie;
|
||||
this->len = cookie.size();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cookie - Value of the cookie
|
||||
* @param length - Length of the cookie
|
||||
*
|
||||
* Sets the value and length using the parameters and sets the name as "?"
|
||||
*/
|
||||
cookie(const std::string& cookie, int length) {
|
||||
this->value = cookie;
|
||||
this->name = "?";
|
||||
this->len = length;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name - Name of the value from the sqlite database
|
||||
* @param cookie - Value of the cookie
|
||||
* @param length - Length of the cookie
|
||||
*
|
||||
* Sets the name, value, and length using the parameters leaving nothing unset.
|
||||
*/
|
||||
cookie(const std::string& name, const std::string& cookie, int length) {
|
||||
this->name = name;
|
||||
this->value = cookie;
|
||||
this->len = length;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param db - An sqlite3 database
|
||||
* @param sql_query_base - A base without the name search e.g. "FROM cookies" this function would then append the text "SELECT <value>" and "WHERE name='<name>'"
|
||||
* @param value - The name of the value to fill the cookie with
|
||||
*
|
||||
* Retrieve the value of a cookie from the provided sqlite database.
|
||||
*
|
||||
*/
|
||||
void get_value_from_db(sqlite3* db, const std::string& sql_query_base, const std::string& value, bool verbose = false, int (*callback)(void*,int,char**,char**) = sqlite_write_callback);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param password - Default is "peanuts". This works for linux. The password should be keychain password on MacOS
|
||||
* @param salt - Salt is "saltysalt" for both MacOS and Linux
|
||||
* @param length - Length of 16 is standard for both MacOS and Linux
|
||||
* @param iterations - 1 on linux and 1003 on MacOS
|
||||
*
|
||||
* Decrypt chrome cookies and format them to be usable as regular cookies. Currently this has only been tested for the _session and __cf_bm cookies from dropout.tv but I see no reason this would not work for anything else.
|
||||
*/
|
||||
void chrome_decrypt(const std::string& password = "peanuts", int iterations = 1, const std::string& salt = "saltysalt", int length = 16);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Remove url encoded text from a cookie. Currently this only checks for %3D ('=') as that is the only thing I've come across in cookies during the entirety of this project.
|
||||
*/
|
||||
void url_decode();
|
||||
|
||||
/**
|
||||
* Remove the leading version (e.g. "v10") from the cookie
|
||||
*/
|
||||
void format_from_chrome();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string - The string which is being searched
|
||||
* @param start - The starting index of the substring
|
||||
* @param test_str - The string which is being tested
|
||||
* @return whether or not the substring is at the start index
|
||||
*
|
||||
* Checks if <b>test_str</b> is a substring of <b>string</b> at the start index
|
||||
*/
|
||||
bool substr_is(const std::string& string, int start, const std::string& test_str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - The base string which is being modified
|
||||
* @param from - what is being replaced
|
||||
* @param to - what to place it with
|
||||
*
|
||||
* Replaces every instance of the <b>from</b> string with the <b>to</b> string.
|
||||
*/
|
||||
void replace_all(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> with any leading or following whitespace
|
||||
*
|
||||
* Removes leading and following whitespace from a string and returns the modified string
|
||||
*/
|
||||
std::string remove_leading_and_following_whitespace(const std::string& str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> with any html character codes replaced with their ascii equivalent.
|
||||
*
|
||||
* E.G. \' would be replaced with '
|
||||
*/
|
||||
std::string replace_html_character_codes(const std::string& str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> with junk removed or replace
|
||||
*
|
||||
* Removed leading and following whitespace and replaces html character codes
|
||||
*/
|
||||
std::string format_name_string(const std::string& str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> properly formatted to be a filename
|
||||
*
|
||||
* Removes non-alphanumeric characters and spaces
|
||||
*/
|
||||
std::string format_filename(const std::string& str);
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include <windows.h>
|
||||
msec_t time_ms(void);
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
/**
|
||||
*
|
||||
* @return The time in milliseconds
|
||||
*/
|
||||
long time_ms();
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filename - Name of the file that is being downloaded
|
||||
* @param total_to_download - The total amount of bytes that are being downloaded
|
||||
* @param downloaded - The current amount of bytes that have been downloaded
|
||||
* @param total_to_upload - The total amount of bytes that are being uploaded (This project does not upload so this is not used)
|
||||
* @param uploaded - The current amount of bytes that have been uploaded (This project does not upload so this is not used)
|
||||
* @return 0
|
||||
*
|
||||
* Used by curl. Displays the filename followed by a bar which show the percent downloaded followed by the number of Mib downloaded out of the total.
|
||||
* The function takes the upload amount because that is required by curl but they are just ignored for this since we never upload anything.
|
||||
*/
|
||||
static int curl_progress_func(void* filename, double total_to_download, double downloaded, double total_to_upload, double uploaded);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param contents - What we're writing
|
||||
* @param size - The amount that is being written
|
||||
* @param nmemb - The number of bytes per unit of size
|
||||
* @param userp - Where the information in <b>contents</b> is written to
|
||||
* @return size * nmemb
|
||||
*
|
||||
* Used by curl. Writes the information gathered by curl into the userp string. This function was not written by me.
|
||||
*/
|
||||
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url - Url which is being downloaded
|
||||
* @param verbose - Whether or not to be verbose (not recommended)
|
||||
* @return The page data as a string
|
||||
*
|
||||
* This function downloads the provided url and returns it as a string. Does not use cookies. This was ripped directly from a firefox network request for an episode page and modified minimally.
|
||||
*/
|
||||
std::string get_generic_page(const std::string& url, bool verbose = false);
|
||||
|
||||
/**
|
||||
* A class for handling all episode information. This class is wildly overkill if downloading an entire series as it gather the series name and season for every episode. This is not an issue here because all the information it gathers it already available while gathering the video url and the majority of the time taken while parsing an episode is from downloading the three required webpages.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
#include "util.h"
|
||||
|
||||
namespace dropout_dl {
|
||||
// dropout-dl helpers
|
||||
bool substr_is(const std::string& string, int start, const std::string& test_str) {
|
||||
if (test_str.size() != test_str.size())
|
||||
return false;
|
||||
|
||||
for (int i = start, j = 0; i < start + test_str.size(); i++, j++) {
|
||||
if (string[i] != test_str[j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void replace_all(std::string& str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string remove_leading_and_following_whitespace(const std::string& str) {
|
||||
int start = 0;
|
||||
int end = str.length() - 1;
|
||||
|
||||
for (; str[start] == ' ' || str[start] == '\t' || str[start] == '\n'; start++);
|
||||
for (; str[end] == ' ' || str[end] == '\t' || str[end] == '\n'; end--);
|
||||
|
||||
return str.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
std::string replace_html_character_codes(const std::string& str) {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
if (substr_is(str, i, "&#")) {
|
||||
i += 2;
|
||||
char code = 0;
|
||||
|
||||
if (i > str.size() - 4) {
|
||||
if (str[str.size() - 1] == ';') {
|
||||
// Numerical character code length is two at the end of the string
|
||||
|
||||
code = str[str.size() - 2] - '0';
|
||||
code += (str[str.size() - 3] - '0') * 10;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (str[i + 3] == ';') {
|
||||
// Numerical character code length is three
|
||||
code = str[i + 2] - '0';
|
||||
code += (str[i + 1] - '0') * 10;
|
||||
code += (str[i] - '0') * 10;
|
||||
i += 3;
|
||||
}
|
||||
else if (str[i + 2] == ';'){
|
||||
code = str[i + 1] - '0';
|
||||
code += (str[i] - '0') * 10;
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
std::cerr << "HTML CHAR CODE ERROR: Code with numerical length of one used\n";
|
||||
exit(11);
|
||||
}
|
||||
}
|
||||
|
||||
if (code < 32) {
|
||||
std::cerr << "HTML CHAR CODE ERROR: Control Character Decoded. This is not supported and likely an error.\n";
|
||||
exit(11);
|
||||
}
|
||||
|
||||
out += code;
|
||||
}
|
||||
else {
|
||||
out += str[i];
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string remove_escaped_characters(const std::string& str) {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
if (str[i] == '\\') {
|
||||
i++;
|
||||
}
|
||||
out += str[i];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string format_name_string(const std::string& str) {
|
||||
return remove_escaped_characters(replace_html_character_codes(remove_leading_and_following_whitespace(str)));
|
||||
}
|
||||
|
||||
std::string format_filename(const std::string& str) {
|
||||
std::string out;
|
||||
|
||||
for (int i = 0; i < str.size(); i++) {
|
||||
char c = str[i];
|
||||
|
||||
// Skip these
|
||||
if (c == '?' || c == ':' || c == '\\') {
|
||||
continue;
|
||||
}
|
||||
// Replace these with dashes
|
||||
else if (c == '/') {
|
||||
out += '-';
|
||||
}
|
||||
else {
|
||||
out += c;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include <windows.h>
|
||||
msec_t time_ms(void)
|
||||
{
|
||||
return timeGetTime();
|
||||
}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
long time_ms()
|
||||
{
|
||||
timeval tv{};
|
||||
gettimeofday(&tv, nullptr);
|
||||
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
long current_time;
|
||||
long last_progress_timestamp;
|
||||
|
||||
int curl_progress_func(void* filename, curl_off_t total_to_download, curl_off_t downloaded, curl_off_t total_to_upload, curl_off_t uploaded) {
|
||||
const double number_chars = 50;
|
||||
const char* full_character = "▓";
|
||||
const char* empty_character = "░";
|
||||
|
||||
current_time = time_ms();
|
||||
if (current_time - 50 > last_progress_timestamp) {
|
||||
// Percent of the file downloaded. Adding one to round up so that when its done it shows as a full bar rather than missing one.
|
||||
double percent_done = (((double)downloaded / (double)total_to_download) * number_chars) + 1;
|
||||
double percent_done_clone = percent_done;
|
||||
std::cout << *(std::string*)filename << " [";
|
||||
while (percent_done_clone-- > 0) {
|
||||
std::cout << full_character;
|
||||
}
|
||||
while (percent_done++ < number_chars) {
|
||||
std::cout << empty_character;
|
||||
}
|
||||
std::cout << "] " << downloaded / 1048576 << "MiB / " << total_to_download / 1048576 << "MiB ";
|
||||
putchar('\r');
|
||||
last_progress_timestamp = time_ms();
|
||||
std::cout.flush();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#ifdef DROPOUT_DL_GCRYPT
|
||||
#include <gcrypt.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace dropout_dl {
|
||||
/**
|
||||
*
|
||||
* @param string - The string which is being searched
|
||||
* @param start - The starting index of the substring
|
||||
* @param test_str - The string which is being tested
|
||||
* @return whether or not the substring is at the start index
|
||||
*
|
||||
* Checks if <b>test_str</b> is a substring of <b>string</b> at the start index
|
||||
*/
|
||||
bool substr_is(const std::string& string, int start, const std::string& test_str);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - The base string which is being modified
|
||||
* @param from - what is being replaced
|
||||
* @param to - what to place it with
|
||||
*
|
||||
* Replaces every instance of the <b>from</b> string with the <b>to</b> string.
|
||||
*/
|
||||
void replace_all(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> with any leading or following whitespace
|
||||
*
|
||||
* Removes leading and following whitespace from a string and returns the modified string
|
||||
*/
|
||||
std::string remove_leading_and_following_whitespace(const std::string& str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> with any html character codes replaced with their ascii equivalent.
|
||||
*
|
||||
* E.G. \' would be replaced with '
|
||||
*/
|
||||
std::string replace_html_character_codes(const std::string& str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> with junk removed or replace
|
||||
*
|
||||
* Removed leading and following whitespace and replaces html character codes
|
||||
*/
|
||||
std::string format_name_string(const std::string& str);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param str - A string
|
||||
* @return <b>str</b> properly formatted to be a filename
|
||||
*
|
||||
* Removes non-alphanumeric characters and spaces
|
||||
*/
|
||||
std::string format_filename(const std::string& str);
|
||||
|
||||
#if defined(__WIN32__)
|
||||
#include <windows.h>
|
||||
msec_t time_ms(void);
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
/**
|
||||
*
|
||||
* @return The time in milliseconds
|
||||
*/
|
||||
long time_ms();
|
||||
#endif
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filename - Name of the file that is being downloaded
|
||||
* @param total_to_download - The total amount of bytes that are being downloaded
|
||||
* @param downloaded - The current amount of bytes that have been downloaded
|
||||
* @param total_to_upload - The total amount of bytes that are being uploaded (This project does not upload so this is not used)
|
||||
* @param uploaded - The current amount of bytes that have been uploaded (This project does not upload so this is not used)
|
||||
* @return 0
|
||||
*
|
||||
* Used by curl. Displays the filename followed by a bar which show the percent downloaded followed by the number of Mib downloaded out of the total.
|
||||
* The function takes the upload amount because that is required by curl but they are just ignored for this since we never upload anything.
|
||||
*/
|
||||
int curl_progress_func(void* filename, curl_off_t total_to_download, curl_off_t downloaded, curl_off_t total_to_upload, curl_off_t uploaded);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param contents - What we're writing
|
||||
* @param size - The amount that is being written
|
||||
* @param nmemb - The number of bytes per unit of size
|
||||
* @param userp - Where the information in <b>contents</b> is written to
|
||||
* @return size * nmemb
|
||||
*
|
||||
* Used by curl. Writes the information gathered by curl into the userp string. This function was not written by me.
|
||||
*/
|
||||
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url - Url which is being downloaded
|
||||
* @param verbose - Whether or not to be verbose (not recommended)
|
||||
* @return The page data as a string
|
||||
*
|
||||
* This function downloads the provided url and returns it as a string. Does not use cookies. This was ripped directly from a firefox network request for an episode page and modified minimally.
|
||||
*/
|
||||
std::string get_generic_page(const std::string& url, bool verbose = false);
|
||||
|
||||
}
|
Loading…
Reference in New Issue