<commit_before>#include "poddlthread.h"

#include <cinttypes>
#include <curl/curl.h>
#include <iostream>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#include "config.h"
#include "logger.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

static size_t my_write_data(void* buffer, size_t size, size_t nmemb,
	void* userp);
static int progress_callback(void* clientp,
	double dltotal,
	double dlnow,
	double ultotal,
	double ulnow);

PodDlThread::PodDlThread(Download* dl_, newsboat::ConfigContainer* c)
	: dl(dl_)
	, f(new std::ofstream())
	, bytecount(0)
	, cfg(c)
{
	static const timeval zero = {0, 0};
	tv1 = zero;
	tv2 = zero;
}

PodDlThread::~PodDlThread() {}

void PodDlThread::operator()()
{
	run();
}

void PodDlThread::run()
{
	// are we resuming previous download?
	bool resumed_download = false;

	gettimeofday(&tv1, nullptr);
	++bytecount;

	CURL* easyhandle = curl_easy_init();
	utils::set_common_curl_options(easyhandle, cfg);

	curl_easy_setopt(easyhandle, CURLOPT_URL, dl->url().c_str());
	curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, 0);
	// set up write functions:
	curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, my_write_data);
	curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, this);

	// set up progress notification:
	curl_easy_setopt(easyhandle, CURLOPT_NOPROGRESS, 0);
	curl_easy_setopt(
		easyhandle, CURLOPT_PROGRESSFUNCTION, progress_callback);
	curl_easy_setopt(easyhandle, CURLOPT_PROGRESSDATA, this);

	// set up max download speed
	int max_dl_speed = cfg->get_configvalue_as_int("max-download-speed");
	if (max_dl_speed > 0) {
		curl_easy_setopt(easyhandle,
			CURLOPT_MAX_RECV_SPEED_LARGE,
			(curl_off_t)(max_dl_speed * 1024));
	}

	struct stat sb;
	std::string filename =
		dl->filename() + newsboat::ConfigContainer::PARTIAL_FILE_SUFFIX;

	if (stat(filename.c_str(), &sb) == -1) {
		LOG(Level::INFO,
			"PodDlThread::run: stat failed: starting normal "
			"download");

		// Have to copy the string into a vector in order to be able to
		// get a char* pointer. std::string::c_str() won't do because it
		// returns const char*, whereas ::dirname() needs non-const.
		std::vector<char> directory(filename.begin(), filename.end());
		utils::mkdir_parents(dirname(&directory[0]));

		f->open(filename, std::fstream::out);
		dl->set_offset(0);
		resumed_download = false;
	} else {
		LOG(Level::INFO,
			"PodDlThread::run: stat ok: starting download from %" PRIi64,
			// That field is `long int`, which is at least 32 bits. On x86_64,
			// it's 64 bits. Thus, this cast is either a no-op, or an up-cast
			// which are always safe.
			static_cast<int64_t>(sb.st_size));
		curl_easy_setopt(easyhandle, CURLOPT_RESUME_FROM, sb.st_size);
		dl->set_offset(sb.st_size);
		f->open(filename, std::fstream::out | std::fstream::app);
		resumed_download = true;<commit_msg>Add explicit null-termination to poddl directory string<commit_after>#include "poddlthread.h"

#include <cinttypes>
#include <curl/curl.h>
#include <iostream>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#include "config.h"
#include "logger.h"
#include "utils.h"

using namespace newsboat;

namespace podboat {

static size_t my_write_data(void* buffer, size_t size, size_t nmemb,
	void* userp);
static int progress_callback(void* clientp,
	double dltotal,
	double dlnow,
	double ultotal,
	double ulnow);

PodDlThread::PodDlThread(Download* dl_, newsboat::ConfigContainer* c)
	: dl(dl_)
	, f(new std::ofstream())
	, bytecount(0)
	, cfg(c)
{
	static const timeval zero = {0, 0};
	tv1 = zero;
	tv2 = zero;
}

PodDlThread::~PodDlThread() {}

void PodDlThread::operator()()
{
	run();
}

void PodDlThread::run()
{
	// are we resuming previous download?
	bool resumed_download = false;

	gettimeofday(&tv1, nullptr);
	++bytecount;

	CURL* easyhandle = curl_easy_init();
	utils::set_common_curl_options(easyhandle, cfg);

	curl_easy_setopt(easyhandle, CURLOPT_URL, dl->url().c_str());
	curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, 0);
	// set up write functions:
	curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, my_write_data);
	curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, this);

	// set up progress notification:
	curl_easy_setopt(easyhandle, CURLOPT_NOPROGRESS, 0);
	curl_easy_setopt(
		easyhandle, CURLOPT_PROGRESSFUNCTION, progress_callback);
	curl_easy_setopt(easyhandle, CURLOPT_PROGRESSDATA, this);

	// set up max download speed
	int max_dl_speed = cfg->get_configvalue_as_int("max-download-speed");
	if (max_dl_speed > 0) {
		curl_easy_setopt(easyhandle,
			CURLOPT_MAX_RECV_SPEED_LARGE,
			(curl_off_t)(max_dl_speed * 1024));
	}

	struct stat sb;
	std::string filename =
		dl->filename() + newsboat::ConfigContainer::PARTIAL_FILE_SUFFIX;

	if (stat(filename.c_str(), &sb) == -1) {
		LOG(Level::INFO,
			"PodDlThread::run: stat failed: starting normal "
			"download");

		// Have to copy the string into a vector in order to be able to
		// get a char* pointer. std::string::c_str() won't do because it
		// returns const char*, whereas ::dirname() needs non-const.
		std::vector<char> directory(filename.begin(), filename.end());
		directory.push_back('\0');
		utils::mkdir_parents(dirname(&directory[0]));

		f->open(filename, std::fstream::out);
		dl->set_offset(0);
		resumed_download = false;
	} else {
		LOG(Level::INFO,
			"PodDlThread::run: stat ok: starting download from %" PRIi64,
			// That field is `long int`, which is at least 32 bits. On x86_64,
			// it's 64 bits. Thus, this cast is either a no-op, or an up-cast
			// which are always safe.
			static_cast<int64_t>(sb.st_size));
		curl_easy_setopt(easyhandle, CURLOPT_RESUME_FROM, sb.st_size);
		dl->set_offset(sb.st_size);
		f->open(filename, std::fstream::out | std::fstream::app);
		resumed_download = true;
{'fraction_non_alphanumeric': 0.13892215568862276, 'fraction_numerical': 0.009751924721984603, 'mean_word_length': 5.292787944025834, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6917669', 'n_tokens_mistral': 2372, 'n_tokens_neox': 2147, 'n_words': 492}