225 lines
7.0 KiB
C++
225 lines
7.0 KiB
C++
#include "stdafx.h"
|
|
#include "UploadManager.h"
|
|
#include "SiaDriveConfig.h"
|
|
#include "SiaApi.h"
|
|
|
|
using namespace Sia::Api;
|
|
|
|
#define TABLE_CREATE L"create table if not exists %s (%s);"
|
|
#define UPLOAD_TABLE L"upload_table"
|
|
#define UPLOAD_TABLE_COLUMNS L"id integer primary key autoincrement, sia_path text unique not null, file_path text unique not null, temp_path text unique not null, status integer not null"
|
|
#define QUERY_UPLOADS_BY_STATUS "select id, sia_path, status from upload_table where status=@status order by id desc limit 1;"
|
|
#define QUERY_UPLOADS_BY_2_STATUS "select id, sia_path, status from upload_table where (status=@status1 or status=@status2) order by id desc limit 1;"
|
|
#define QUERY_UPLOADS_BY_SIA_PATH "select id, sia_path, status from upload_table where sia_path=@sia_path order by id desc limit 1;"
|
|
#define QUERY_UPLOADS_BY_SIA_PATH_AND_2_STATUS "select id, sia_path, status from upload_table where sia_path=@sia_path and (status=@status1 or status=@status2) order by id desc limit 1;"
|
|
|
|
template <typename... Ts>
|
|
String fmt(const String &fmt, Ts... vs)
|
|
{
|
|
size_t required = _sntprintf(nullptr, 0, fmt.c_str(), vs...);
|
|
|
|
String ret;
|
|
ret.resize(required);
|
|
_sntprintf(&ret[0], required, fmt.c_str(), vs...);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void CreateTableIfNotFound(SQLite::Database* database, const String& tableName, const String& columns)
|
|
{
|
|
String sqlCreate = fmt(TABLE_CREATE, &tableName[0], &columns[0]);
|
|
database->exec(CW2A(sqlCreate.c_str()));
|
|
}
|
|
|
|
CUploadManager::CUploadManager(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) :
|
|
CAutoThread(siaCurl, siaDriveConfig),
|
|
_uploadDatabase(siaDriveConfig->GetRenter_UploadDbFilePath(), SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE),
|
|
_siaDriveConfig(siaDriveConfig)
|
|
{
|
|
CreateTableIfNotFound(&_uploadDatabase, UPLOAD_TABLE, UPLOAD_TABLE_COLUMNS);
|
|
StartAutoThread();
|
|
}
|
|
|
|
CUploadManager::~CUploadManager()
|
|
{
|
|
StopAutoThread();
|
|
}
|
|
|
|
void CUploadManager::AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig)
|
|
{
|
|
bool processNext = true;
|
|
|
|
try
|
|
{
|
|
CSiaFileTreePtr fileTree(new CSiaFileTree(siaCurl, siaDriveConfig));
|
|
json result;
|
|
if (ApiSuccess(siaCurl.Get(L"/renter/files", result)))
|
|
{
|
|
// Lock here - if file is modified again before a prior upload is complete, delete it and
|
|
// start again later
|
|
std::lock_guard<std::mutex> l(_uploadMutex);
|
|
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_2_STATUS);
|
|
query.bind("@status1", static_cast<unsigned>(UploadStatus::Uploading));
|
|
query.bind("@status2", static_cast<unsigned>(UploadStatus::Modified));
|
|
|
|
fileTree->BuildTree(result);
|
|
if (query.executeStep())
|
|
{
|
|
std::string tempSiaPath = query.getColumn(1);
|
|
String siaPath = CA2W(tempSiaPath.c_str()).m_psz;
|
|
UploadStatus uploadStatus = static_cast<UploadStatus>(query.getColumn(2).getUInt());
|
|
|
|
auto fileList = fileTree->GetFileList();
|
|
auto it = std::find_if(fileList.begin(), fileList.end(), [&](const CSiaFilePtr& ptr)
|
|
{
|
|
return ptr->GetSiaPath() == siaPath;
|
|
});
|
|
|
|
if (it == fileList.end())
|
|
{
|
|
// error condition - should always exist. delete from db and log warning, but continue processing
|
|
}
|
|
else if (uploadStatus == UploadStatus::Modified)
|
|
{
|
|
// delete existing, change status to queued
|
|
}
|
|
else if ((*it)->GetUploadProgress() >= 100)
|
|
{
|
|
// upload complete - change status
|
|
}
|
|
else
|
|
{
|
|
// upload still active
|
|
processNext = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// error condition - host down?
|
|
processNext = false;
|
|
}
|
|
}
|
|
catch (const SQLite::Exception& e)
|
|
{
|
|
// error condition - database not initialized (i.e. no table)?
|
|
std::string msg = e.what();
|
|
processNext = false;
|
|
}
|
|
|
|
if (processNext)
|
|
{
|
|
try
|
|
{
|
|
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_STATUS);
|
|
query.bind("@status", static_cast<unsigned>(UploadStatus::Queued));
|
|
|
|
// Lock here - if file is modified again before a prior upload is complete, delete it and
|
|
// start again later
|
|
if (query.executeStep())
|
|
{
|
|
String siaPath = CA2W(query.getColumn(1)).m_psz;
|
|
String tempFilePath;
|
|
|
|
json response;
|
|
SiaCurlError cerror = siaCurl.Post(String(L"/renter/upload/") + siaPath, { {L"source", tempFilePath} }, response);
|
|
if (ApiSuccess(cerror))
|
|
{
|
|
// TODO Update status in DB
|
|
}
|
|
}
|
|
}
|
|
catch (const SQLite::Exception& e)
|
|
{
|
|
// error condition
|
|
std::string msg = e.what();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UploadStatus CUploadManager::GetUploadStatus(const String& siaPath)
|
|
{
|
|
UploadStatus uploadStatus = UploadStatus::NotFound;
|
|
|
|
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_SIA_PATH);
|
|
query.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
|
if (query.executeStep())
|
|
{
|
|
uploadStatus = static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(2)));
|
|
}
|
|
|
|
return uploadStatus;
|
|
}
|
|
|
|
void CUploadManager::AddOrUpdate(const String& siaPath, const String& filePath)
|
|
{
|
|
// Lock here - if file is modified again before a prior upload is complete, delete it and
|
|
// start again later
|
|
std::lock_guard<std::mutex> l(_uploadMutex);
|
|
|
|
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_SIA_PATH_AND_2_STATUS);
|
|
query.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
|
query.bind("@status1", static_cast<unsigned>(UploadStatus::Uploading));
|
|
query.bind("@status2", static_cast<unsigned>(UploadStatus::Modified));
|
|
// Check copying
|
|
if (query.executeStep())
|
|
{
|
|
if (static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(2))) == UploadStatus::Uploading)
|
|
{
|
|
// set to modified
|
|
// update file path
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*SQLite::Statement addOrUpdate(_uploadDatabase, ADD_UPDATE_UPLOAD);
|
|
addOrUpdate.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
|
addOrUpdate.bind("@file_path", CW2A(filePath.c_str()).m_psz);
|
|
addOrUpdate.bind("@temp_path", CW2A(tempPath.c_str()).m_psz);
|
|
addOrUpdate.bind("@status", static_cast<unsigned>(UploadStatus::Queued));*/
|
|
|
|
// While copy is active, point to normal file.
|
|
// After copy, use temp file as real file until upload is complete
|
|
// This allows modifications to the file to occur in a more timely manner.
|
|
// Error Scenarios:
|
|
// Crash before db update to status copying - file will be re-uploaded automatically, if complete; otherwise, deleted
|
|
// Need to keep track of files as being copied and then there status
|
|
// Crash Scenarios:
|
|
// Crash before copy begins - on startup, check for copying status with no .siadrive
|
|
// Crash during copy - on startup, check for copying status and delete .siadrive
|
|
// Crash after copy but before db update - on startup, check for copying status and delete .siadrive
|
|
String tempPath;
|
|
tempPath.resize(MAX_PATH + 1);
|
|
PathCombine(&tempPath[0], CA2W(_siaDriveConfig->GetTempFolder().c_str()), (GenerateSha256(filePath) + L".siadrive").c_str());
|
|
|
|
// Queue this
|
|
if (::CopyFile(filePath.c_str(), tempPath.c_str(), FALSE))
|
|
{
|
|
tempPath = L"";
|
|
}
|
|
else
|
|
{
|
|
if (!::DeleteFile(tempPath.c_str()))
|
|
{
|
|
|
|
}
|
|
// error condition
|
|
}
|
|
}
|
|
}
|
|
|
|
void CUploadManager::PurgeCompleteStatus()
|
|
{
|
|
|
|
}
|
|
|
|
void CUploadManager::PurgeErrorStatus()
|
|
{
|
|
|
|
}
|
|
|
|
void CUploadManager::Remove(const String& siaPath)
|
|
{
|
|
|
|
} |