More CMake changes - BROKEN BUILD
This commit is contained in:
@@ -25,8 +25,15 @@ if (MSVC)
|
||||
URL https://github.com/curl/curl/archive/curl-7_53_1.tar.gz
|
||||
PREFIX ${CMAKE_BINARY_DIR}/external/builds/curl
|
||||
CMAKE_ARGS -DCURL_STATICLIB=ON -DBUILD_TESTING=OFF -DBUILD_CURL_EXE=OFF -DCURL_DISABLE_LDAP=ON -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/)
|
||||
add_dependencies(siadrive.api curl_project)
|
||||
set(3RD_PARTY_INCLUDES ${3RD_PARTY_INCLUDES} ${CMAKE_BINARY_DIR}/external/include)
|
||||
|
||||
ExternalProject_Add(sqlitecpp_project
|
||||
URL https://github.com/SRombauts/SQLiteCpp/archive/2.0.0.tar.gz
|
||||
PREFIX ${CMAKE_BINARY_DIR}/external/builds/sqlitecpp
|
||||
INSTALL_COMMAND cmake -E echo "Skipping install step.")
|
||||
|
||||
add_dependencies(siadrive.api curl_project sqlitecpp_project)
|
||||
|
||||
set(3RD_PARTY_INCLUDES ${3RD_PARTY_INCLUDES} ${CMAKE_BINARY_DIR}/external/include ${CMAKE_BINARY_DIR}/external/builds/sqlitecpp/src/sqlitecpp_project/include/)
|
||||
target_link_libraries(siadrive.api ${CMAKE_BINARY_DIR}/external/lib/libcurl.lib)
|
||||
endif()
|
||||
|
||||
|
57
include/siadrive_api/eventsystem.h
Normal file
57
include/siadrive_api/eventsystem.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#include <siacommon.h>
|
||||
|
||||
NS_BEGIN(Sia)
|
||||
NS_BEGIN(Api)
|
||||
|
||||
class SIADRIVE_EXPORTABLE CEvent
|
||||
{
|
||||
public:
|
||||
virtual ~CEvent() {}
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const = 0;
|
||||
virtual std::shared_ptr<CEvent> Clone() const = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<CEvent> CEventPtr;
|
||||
|
||||
#define CreateSystemEvent(E) CEventPtr(new E)
|
||||
#define CreateSystemEventConsumer(E) [=](const CEvent&) -> void { E(e); }
|
||||
|
||||
// Singleton
|
||||
class SIADRIVE_EXPORTABLE CEventSystem
|
||||
{
|
||||
private:
|
||||
CEventSystem();
|
||||
|
||||
private:
|
||||
~CEventSystem();
|
||||
|
||||
public:
|
||||
// Singleton setup
|
||||
CEventSystem(const CEventSystem&) = delete;
|
||||
CEventSystem(CEventSystem&&) = delete;
|
||||
CEventSystem& operator=(CEventSystem&&) = delete;
|
||||
CEventSystem& operator=(const CEventSystem&) = delete;
|
||||
|
||||
private:
|
||||
HANDLE _stopEvent;
|
||||
std::deque<CEventPtr> _eventQueue;
|
||||
std::deque<std::function<void(const CEvent&)>> _eventConsumers;
|
||||
std::mutex _eventMutex;
|
||||
std::unique_ptr<std::thread> _processThread;
|
||||
|
||||
public:
|
||||
static CEventSystem EventSystem;
|
||||
|
||||
private:
|
||||
void ProcessEvents();
|
||||
|
||||
public:
|
||||
void AddEventConsumer(std::function<void(const CEvent&)> consumer);
|
||||
void NotifyEvent(CEventPtr eventData);
|
||||
void Start();
|
||||
void Stop();
|
||||
};
|
||||
NS_END(2)
|
@@ -46,6 +46,7 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <deque>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
880
include/siadrive_api/uploadmanager.h
Normal file
880
include/siadrive_api/uploadmanager.h
Normal file
@@ -0,0 +1,880 @@
|
||||
#ifndef _UPLOADMANAGER_H
|
||||
#define _UPLOADMANAGER_H
|
||||
|
||||
#include <autothread.h>
|
||||
#include <SQLiteCpp/Database.h>
|
||||
#include <deque>
|
||||
#include <EventSystem.h>
|
||||
|
||||
NS_BEGIN(Sia)
|
||||
NS_BEGIN(Api)
|
||||
|
||||
class SIADRIVE_EXPORTABLE CUploadManager :
|
||||
public CAutoThread
|
||||
{
|
||||
public:
|
||||
enum class _UploadStatus : unsigned
|
||||
{
|
||||
NotFound,
|
||||
Copying,
|
||||
Queued,
|
||||
Modified,
|
||||
Uploading,
|
||||
Remove,
|
||||
Complete,
|
||||
Error
|
||||
};
|
||||
|
||||
enum class _UploadError
|
||||
{
|
||||
Success,
|
||||
SourceFileNotFound,
|
||||
DatabaseError
|
||||
};
|
||||
|
||||
private:
|
||||
typedef struct
|
||||
{
|
||||
std::uint64_t Id;
|
||||
SString SiaPath;
|
||||
SString FilePath;
|
||||
SString TempPath;
|
||||
_UploadStatus Status;
|
||||
} UploadData;
|
||||
|
||||
public:
|
||||
CUploadManager(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig);
|
||||
|
||||
public:
|
||||
virtual ~CUploadManager();
|
||||
|
||||
private:
|
||||
SQLite::Database _uploadDatabase;
|
||||
std::mutex _uploadMutex;
|
||||
CAutoThread _fileThread;
|
||||
std::mutex _fileQueueMutex;
|
||||
std::mutex _fileActionMutex;
|
||||
SString _activeSiaPath;
|
||||
std::deque<std::function<void()>> _fileQueue;
|
||||
|
||||
private:
|
||||
void FileAction(const CSiaCurl& siaCurl, const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath, const bool& remove);
|
||||
void FileThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig);
|
||||
void HandleFileRemove(const CSiaCurl& siaCurl, const SString& siaPath, const SString& siaDriveFilePath);
|
||||
void HandleAddFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath);
|
||||
void UpdateFileQueueOnStartup();
|
||||
void DeleteFilesRemovedFromSia(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig, const bool& isStartup = false);
|
||||
|
||||
protected:
|
||||
virtual void AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) override;
|
||||
|
||||
public:
|
||||
static SString UploadStatusToString(const _UploadStatus& uploadStatus);
|
||||
|
||||
public:
|
||||
_UploadStatus GetUploadStatus(const SString& siaPath);
|
||||
_UploadError AddOrUpdate(const SString& siaPath, SString filePath);
|
||||
_UploadError Remove(const SString& siaPath);
|
||||
};
|
||||
|
||||
typedef Sia::Api::CUploadManager::_UploadStatus UploadStatus;
|
||||
typedef Sia::Api::CUploadManager::_UploadError UploadError;
|
||||
|
||||
// Event Notifications
|
||||
class CreatingTemporarySiaDriveFile :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
CreatingTemporarySiaDriveFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CreatingTemporarySiaDriveFile()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"CreatingTemporarySiaDriveFile|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new CreatingTemporarySiaDriveFile(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadAddedToQueue :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadAddedToQueue(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadAddedToQueue()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadAddedToQueue|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadAddedToQueue(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class ExternallyRemovedFileDetected :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
ExternallyRemovedFileDetected(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~ExternallyRemovedFileDetected()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"ExternallyRemovedFileDetected|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new ExternallyRemovedFileDetected(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class ModifiedUploadQueued :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
ModifiedUploadQueued(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~ModifiedUploadQueued()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"ModifiedUploadQueued|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new ModifiedUploadQueued(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadToSiaStarted :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadToSiaStarted(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadToSiaStarted()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadToSiaStarted|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadToSiaStarted(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadComplete :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadComplete(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadComplete()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadComplete|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadComplete(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class FileRemoved :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
FileRemoved(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~FileRemoved()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"FileRemoved|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new FileRemoved(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadStatusSetToModified :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadStatusSetToModified(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadStatusSetToModified()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadStatusSetToModified|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadStatusSetToModified(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class UploadStatusSetToRemoved :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
UploadStatusSetToRemoved(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~UploadStatusSetToRemoved()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"UploadStatusSetToRemoved|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new UploadStatusSetToRemoved(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class FailedToDeleteFromSia :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
FailedToDeleteFromSia(const SString& siaPath, const SString& filePath, const SiaCurlError& curlError) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_curlError(curlError)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~FailedToDeleteFromSia()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SiaCurlError _curlError = SiaCurlError::Success;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"FailedToDeleteFromSia|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new FailedToDeleteFromSia(_siaPath, _filePath, _curlError));
|
||||
}
|
||||
};
|
||||
|
||||
class ModifyUploadStatusFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
ModifyUploadStatusFailed(const SString& siaPath, const SString& filePath, const UploadStatus& uploadStatus, const SString& errorMsg) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_uploadStatus(uploadStatus),
|
||||
_errorMsg(errorMsg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~ModifyUploadStatusFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const UploadStatus _uploadStatus;
|
||||
const SString _errorMsg;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"ModifyUploadStatusFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|ST|" + CUploadManager::UploadStatusToString(_uploadStatus) + L"|MSG|" + _errorMsg;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new ModifyUploadStatusFailed(_siaPath, _filePath, _uploadStatus, _errorMsg));
|
||||
}
|
||||
};
|
||||
|
||||
class CreatingTemporarySiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
CreatingTemporarySiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CreatingTemporarySiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"CreatingTemporarySiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new CreatingTemporarySiaDriveFileFailed(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class DeleteSiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DeleteSiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DeleteSiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DeleteSiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DeleteSiaDriveFileFailed(_siaPath, _filePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class DeleteTemporarySiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DeleteTemporarySiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DeleteTemporarySiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DeleteTemporarySiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DeleteTemporarySiaDriveFileFailed(_siaPath, _filePath, _tempSourcePath));
|
||||
}
|
||||
};
|
||||
|
||||
class RenamingTemporarySiaDriveFile :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RenamingTemporarySiaDriveFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RenamingTemporarySiaDriveFile()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RenamingTemporarySiaDriveFile|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RenamingTemporarySiaDriveFile(_siaPath, _filePath, _tempSourcePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class RenamingTemporarySiaDriveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RenamingTemporarySiaDriveFileFailed(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_tempSourcePath(tempSourcePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RenamingTemporarySiaDriveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _tempSourcePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RenamingTemporarySiaDriveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|TSP|" + _tempSourcePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RenamingTemporarySiaDriveFileFailed(_siaPath, _filePath, _tempSourcePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class DatabaseInsertFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DatabaseInsertFailed(const SString& siaPath, const SString& filePath, const SString& errorMessage) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_errorMessage(errorMessage)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DatabaseInsertFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _errorMessage;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DatabaseInsertFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|MSG|" + _errorMessage;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DatabaseInsertFailed(_siaPath, _filePath, _errorMessage));
|
||||
}
|
||||
};
|
||||
|
||||
class DatabaseDeleteFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DatabaseDeleteFailed(const SString& siaPath, const SString& filePath, const SString& errorMessage) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_errorMessage(errorMessage)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DatabaseDeleteFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _errorMessage;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DatabaseDeleteFailed|SP|" + _siaPath + L"|FP|" + _filePath + L"|MSG|" + _errorMessage;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DatabaseDeleteFailed(_siaPath, _filePath, _errorMessage));
|
||||
}
|
||||
};
|
||||
|
||||
class RemoveFileFailed :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
RemoveFileFailed(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~RemoveFileFailed()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"RemoveFileFailed|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new RemoveFileFailed(_siaPath, _filePath));
|
||||
}
|
||||
};
|
||||
|
||||
class ExistingUploadFound :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
ExistingUploadFound(const SString& siaPath, const SString& filePath, const UploadStatus& uploadStatus) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_uploadStatus(uploadStatus)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~ExistingUploadFound()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const UploadStatus _uploadStatus;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"ExistingUploadFound|SP|" + _siaPath + L"|FP|" + _filePath + L"|ST|" + CUploadManager::UploadStatusToString(_uploadStatus);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new ExistingUploadFound(_siaPath, _filePath, _uploadStatus));
|
||||
}
|
||||
};
|
||||
|
||||
class NewFileAdded :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
NewFileAdded(const SString& siaPath, const SString& filePath, const SString& siaDriveFilePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath),
|
||||
_siaDriveFilePath(siaDriveFilePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~NewFileAdded()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
const SString _siaDriveFilePath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"NewFileAdded|SP|" + _siaPath + L"|FP|" + _filePath + L"|SDP|" + _siaDriveFilePath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new NewFileAdded(_siaPath, _filePath, _siaDriveFilePath));
|
||||
}
|
||||
};
|
||||
|
||||
class FileRemoveAdded :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
FileRemoveAdded(const SString& siaPath) :
|
||||
_siaPath(siaPath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~FileRemoveAdded()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
|
||||
public:
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"FileRemoveAdded|SP|" + _siaPath;
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new FileRemoveAdded(_siaPath));
|
||||
}
|
||||
};
|
||||
|
||||
class SourceFileNotFound :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
SourceFileNotFound(const SString& siaPath, const SString& filePath) :
|
||||
_siaPath(siaPath),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~SourceFileNotFound()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SString _siaPath;
|
||||
const SString _filePath;
|
||||
|
||||
public:
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new SourceFileNotFound(_siaPath, _filePath));
|
||||
}
|
||||
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"SourceFileNotFound|SP|" + _siaPath + L"|FP|" + _filePath;
|
||||
}
|
||||
};
|
||||
|
||||
class DatabaseExceptionOccurred :
|
||||
public CEvent
|
||||
{
|
||||
public:
|
||||
DatabaseExceptionOccurred(const SQLite::Exception& exception) :
|
||||
_exception(exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~DatabaseExceptionOccurred()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
const SQLite::Exception _exception;
|
||||
|
||||
public:
|
||||
virtual std::shared_ptr<CEvent> Clone() const override
|
||||
{
|
||||
return std::shared_ptr<CEvent>(new DatabaseExceptionOccurred(_exception));
|
||||
}
|
||||
|
||||
virtual SString GetSingleLineMessage() const override
|
||||
{
|
||||
return L"DatabaseExceptionOccurred|MSG|" + SString(CA2W(_exception.getErrorStr()).m_psz);
|
||||
}
|
||||
};
|
||||
|
||||
NS_END(2)
|
||||
|
||||
#endif //_UPLOADMANAGER_H
|
83
src/siadrive_api/eventsystem.cpp
Normal file
83
src/siadrive_api/eventsystem.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <eventsystem.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
CEventSystem CEventSystem::EventSystem;
|
||||
|
||||
CEventSystem::CEventSystem() :
|
||||
_stopEvent(INVALID_HANDLE_VALUE)
|
||||
{
|
||||
}
|
||||
|
||||
CEventSystem::~CEventSystem()
|
||||
{
|
||||
Stop();
|
||||
::CloseHandle(_stopEvent);
|
||||
}
|
||||
|
||||
void CEventSystem::ProcessEvents()
|
||||
{
|
||||
while (::WaitForSingleObject(_stopEvent, 10) == WAIT_TIMEOUT)
|
||||
{
|
||||
CEventPtr eventData;
|
||||
do
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_eventMutex);
|
||||
if (_eventQueue.size())
|
||||
{
|
||||
eventData = _eventQueue.front();
|
||||
_eventQueue.pop_front();
|
||||
}
|
||||
else
|
||||
{
|
||||
eventData.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (eventData)
|
||||
{
|
||||
for (auto& v : _eventConsumers)
|
||||
{
|
||||
v(*eventData);
|
||||
}
|
||||
}
|
||||
} while (eventData);
|
||||
}
|
||||
}
|
||||
|
||||
void CEventSystem::NotifyEvent(CEventPtr eventData)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_eventMutex);
|
||||
if (_eventConsumers.size())
|
||||
{
|
||||
_eventQueue.push_back(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
void CEventSystem::AddEventConsumer(std::function<void(const CEvent&)> consumer)
|
||||
{
|
||||
if (!_processThread)
|
||||
{
|
||||
_eventConsumers.push_back(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
void CEventSystem::Start()
|
||||
{
|
||||
if (!_processThread)
|
||||
{
|
||||
_stopEvent = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
_processThread.reset(new std::thread([this]() {ProcessEvents(); }));
|
||||
}
|
||||
}
|
||||
|
||||
void CEventSystem::Stop()
|
||||
{
|
||||
if (_processThread)
|
||||
{
|
||||
::SetEvent(_stopEvent);
|
||||
_processThread->join();
|
||||
_processThread.reset();
|
||||
}
|
||||
}
|
35
src/siadrive_api/siaconsensus.cpp
Normal file
35
src/siadrive_api/siaconsensus.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <siaapi.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
CSiaApi::_CSiaConsensus::_CSiaConsensus(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) :
|
||||
CSiaBase(siaCurl, siaDriveConfig),
|
||||
CAutoThread(siaCurl, siaDriveConfig),
|
||||
_Height(0),
|
||||
_Synced(false),
|
||||
_CurrentBlock(L"")
|
||||
{
|
||||
StartAutoThread();
|
||||
}
|
||||
|
||||
CSiaApi::_CSiaConsensus::~_CSiaConsensus()
|
||||
{
|
||||
StopAutoThread();
|
||||
}
|
||||
|
||||
void CSiaApi::_CSiaConsensus::AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig)
|
||||
{
|
||||
json result;
|
||||
if (ApiSuccess(siaCurl.Get(L"/consensus", result)))
|
||||
{
|
||||
SetHeight(result["height"].get<std::uint64_t>());
|
||||
SetSynced(result["synced"].get<bool>());
|
||||
SetCurrentBlock(result["currentblock"].get<std::string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHeight(0);
|
||||
SetSynced(false);
|
||||
SetCurrentBlock(L"");
|
||||
}
|
||||
}
|
21
src/siadrive_api/siafile.cpp
Normal file
21
src/siadrive_api/siafile.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include <siaapi.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
CSiaApi::_CSiaFile::_CSiaFile(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig, const json& fileJson) :
|
||||
CSiaBase(siaCurl, siaDriveConfig),
|
||||
_SiaPath(fileJson["siapath"].get<std::string>()),
|
||||
_FileSize(fileJson["filesize"].get<std::uint64_t>()),
|
||||
_Available(fileJson["available"].get<bool>()),
|
||||
_Renewing(fileJson["renewing"].get<bool>()),
|
||||
_Redundancy(fileJson["redundancy"].get<std::uint32_t>()),
|
||||
_UploadProgress(fileJson["uploadprogress"].get<std::uint32_t>()),
|
||||
_Expiration(fileJson["expiration"].get<std::uint32_t>())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CSiaApi::_CSiaFile::~_CSiaFile()
|
||||
{
|
||||
|
||||
}
|
109
src/siadrive_api/siafiletree.cpp
Normal file
109
src/siadrive_api/siafiletree.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <siaapi.h>
|
||||
#include <regex>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
CSiaApi::_CSiaFileTree::_CSiaFileTree(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) :
|
||||
CSiaBase(siaCurl, siaDriveConfig)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CSiaApi::_CSiaFileTree::~_CSiaFileTree()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CSiaApi::_CSiaFileTree::BuildTree(const json& result)
|
||||
{
|
||||
_fileList.clear();
|
||||
for (const auto& file : result["files"])
|
||||
{
|
||||
_fileList.push_back(CSiaFilePtr(new CSiaFile(GetSiaCurl(), &GetSiaDriveConfig(), file)));
|
||||
}
|
||||
}
|
||||
|
||||
bool CSiaApi::_CSiaFileTree::FileExists(const SString& siaPath) const
|
||||
{
|
||||
auto result = std::find_if(_fileList.begin(), _fileList.end(), [&](const CSiaFilePtr& item)->bool
|
||||
{
|
||||
return (item->GetSiaPath() == siaPath);
|
||||
});
|
||||
|
||||
return (result != _fileList.end());
|
||||
}
|
||||
|
||||
CSiaFileCollection CSiaApi::_CSiaFileTree::GetFileList() const
|
||||
{
|
||||
return _fileList;
|
||||
}
|
||||
|
||||
CSiaFilePtr CSiaApi::_CSiaFileTree::GetFile(const SString& siaPath) const
|
||||
{
|
||||
auto result = std::find_if(_fileList.begin(), _fileList.end(), [&](const CSiaFilePtr& item)->bool
|
||||
{
|
||||
return (item->GetSiaPath() == siaPath);
|
||||
});
|
||||
|
||||
return ((result != _fileList.end()) ? *result : nullptr);
|
||||
}
|
||||
|
||||
CSiaFileCollection CSiaApi::_CSiaFileTree::Query(SString query) const
|
||||
{
|
||||
query = CSiaApi::FormatToSiaPath(query);
|
||||
query.Replace(".", "\\.").Replace("*", "[^/]+").Replace("?", "[^/]?");
|
||||
std::wregex r(query.str());
|
||||
|
||||
CSiaFileCollection ret;
|
||||
std::copy_if(_fileList.begin(), _fileList.end(), std::back_inserter(ret), [&](const CSiaFilePtr& v) -> bool
|
||||
{
|
||||
return std::regex_match(v->GetSiaPath().str(), r);
|
||||
});
|
||||
|
||||
return std::move(ret);
|
||||
}
|
||||
|
||||
std::vector<SString> CSiaApi::_CSiaFileTree::QueryDirectories(SString rootFolder) const
|
||||
{
|
||||
CSiaFileCollection col;
|
||||
rootFolder.Replace("/", "\\");
|
||||
if (rootFolder[0] == '\\')
|
||||
{
|
||||
rootFolder = rootFolder.SubString(1);
|
||||
}
|
||||
|
||||
std::vector<SString> ret;
|
||||
std::for_each(_fileList.begin(), _fileList.end(), [&](const CSiaFilePtr& v)
|
||||
{
|
||||
SString dir = v->GetSiaPath();
|
||||
dir.Replace("/", "\\");
|
||||
::PathRemoveFileSpec(&dir[0]);
|
||||
::PathRemoveBackslash(&dir[0]);
|
||||
::PathRemoveBackslash(&rootFolder[0]);
|
||||
dir = dir.str().c_str();
|
||||
rootFolder = rootFolder.str().c_str();
|
||||
if ((dir.Length() > rootFolder.Length()) && (dir.SubString(0, rootFolder.Length()) == rootFolder))
|
||||
{
|
||||
SString subFolder = dir.SubString(rootFolder.Length());
|
||||
int idx = subFolder.str().find('\\');
|
||||
if (idx == 0)
|
||||
{
|
||||
subFolder = subFolder.SubString(1);
|
||||
idx = subFolder.str().find('\\');
|
||||
}
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
subFolder = subFolder.SubString(0, idx);
|
||||
}
|
||||
|
||||
auto it = std::find(ret.begin(), ret.end(), subFolder);
|
||||
if (it == ret.end())
|
||||
{
|
||||
ret.push_back(subFolder);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return std::move(ret);
|
||||
}
|
155
src/siadrive_api/siarenter.cpp
Normal file
155
src/siadrive_api/siarenter.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <siaapi.h>
|
||||
#include <SQLiteCpp/Database.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
/*{
|
||||
// Settings that control the behavior of the renter.
|
||||
"settings": {
|
||||
// Allowance dictates how much the renter is allowed to spend in a given
|
||||
// period. Note that funds are spent on both storage and bandwidth.
|
||||
"allowance": {
|
||||
// Amount of money allocated for contracts. Funds are spent on both
|
||||
// storage and bandwidth.
|
||||
"funds": "1234", // hastings
|
||||
|
||||
// Number of hosts that contracts will be formed with.
|
||||
"hosts":24,
|
||||
|
||||
// Duration of contracts formed, in number of blocks.
|
||||
"period": 6048, // blocks
|
||||
|
||||
// If the current blockheight + the renew window >= the height the
|
||||
// contract is scheduled to end, the contract is renewed automatically.
|
||||
// Is always nonzero.
|
||||
"renewwindow": 3024 // blocks
|
||||
}
|
||||
},
|
||||
|
||||
// Metrics about how much the Renter has spent on storage, uploads, and
|
||||
// downloads.
|
||||
"financialmetrics": {
|
||||
// How much money, in hastings, the Renter has spent on file contracts,
|
||||
// including fees.
|
||||
"contractspending": "1234", // hastings
|
||||
|
||||
// Amount of money spent on downloads.
|
||||
"downloadspending": "5678", // hastings
|
||||
|
||||
// Amount of money spend on storage.
|
||||
"storagespending": "1234", // hastings
|
||||
|
||||
// Amount of money spent on uploads.
|
||||
"uploadspending": "5678", // hastings
|
||||
|
||||
// Amount of money in the allowance that has not been spent.
|
||||
"unspent": "1234" // hastings
|
||||
}
|
||||
}*/
|
||||
CSiaApi::_CSiaRenter::_CSiaRenter(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) :
|
||||
CSiaBase(siaCurl, siaDriveConfig),
|
||||
CAutoThread(siaCurl, siaDriveConfig),
|
||||
_Funds(0),
|
||||
_Hosts(0),
|
||||
_Unspent(0),
|
||||
_TotalUsedBytes(0),
|
||||
_TotalUploadProgress(100)
|
||||
{
|
||||
StartAutoThread();
|
||||
}
|
||||
|
||||
CSiaApi::_CSiaRenter::~_CSiaRenter()
|
||||
{
|
||||
StopAutoThread();
|
||||
}
|
||||
|
||||
void CSiaApi::_CSiaRenter::AutoThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig)
|
||||
{
|
||||
json result;
|
||||
if (ApiSuccess(siaCurl.Get(L"/renter", result)))
|
||||
{
|
||||
SiaCurrency funds = HastingsStringToSiaCurrency(result["settings"]["allowance"]["funds"].get<std::string>());
|
||||
SiaCurrency unspent = HastingsStringToSiaCurrency(result["financialmetrics"]["unspent"].get<std::string>());
|
||||
std::uint64_t hosts = result["settings"]["allowance"]["hosts"].get<std::uint64_t>();
|
||||
SetFunds(funds);
|
||||
SetHosts(hosts);
|
||||
SetUnspent(unspent);
|
||||
|
||||
CSiaFileTreePtr fileTree(new CSiaFileTree(siaCurl, siaDriveConfig));
|
||||
if (ApiSuccess(siaCurl.Get(L"/renter/files", result)))
|
||||
{
|
||||
fileTree->BuildTree(result);
|
||||
|
||||
auto fileList = fileTree->GetFileList();
|
||||
if (fileList.size())
|
||||
{
|
||||
std::uint64_t total = std::accumulate(std::next(fileList.begin()), fileList.end(), fileList[0]->GetFileSize(), [](const std::uint64_t& sz, const CSiaFilePtr& file)
|
||||
{
|
||||
return sz + file->GetFileSize();
|
||||
});
|
||||
|
||||
std::uint32_t totalProgress = std::accumulate(std::next(fileList.begin()), fileList.end(), fileList[0]->GetUploadProgress(), [](const std::uint32_t& progress, const CSiaFilePtr& file)
|
||||
{
|
||||
return progress + min(100, file->GetUploadProgress());
|
||||
}) / fileList.size();
|
||||
|
||||
SetTotalUsedBytes(total);
|
||||
SetTotalUploadProgress(totalProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTotalUsedBytes(0);
|
||||
SetTotalUploadProgress(100);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTotalUsedBytes(0);
|
||||
SetTotalUploadProgress(100);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFunds(0);
|
||||
SetHosts(0);
|
||||
SetUnspent(0);
|
||||
SetTotalUsedBytes(0);
|
||||
SetTotalUploadProgress(100);
|
||||
}
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaRenter::FileExists(const SString& siaPath, bool& exists) const
|
||||
{
|
||||
CSiaFileTreePtr siaFileTree;
|
||||
SiaApiError ret = GetFileTree(siaFileTree);
|
||||
if (ApiSuccess(ret))
|
||||
{
|
||||
exists = siaFileTree->FileExists(siaPath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaRenter::DownloadFile(const SString& siaPath, const SString& location) const
|
||||
{
|
||||
SiaApiError ret = SiaApiError::RequestError;
|
||||
json result;
|
||||
if (ApiSuccess(GetSiaCurl().Get(L"/renter/download/" + siaPath, { { L"destination", location } }, result)))
|
||||
{
|
||||
ret = SiaApiError::Success;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaRenter::GetFileTree(CSiaFileTreePtr& siaFileTree) const
|
||||
{
|
||||
SiaApiError ret = SiaApiError::RequestError;
|
||||
siaFileTree.reset(new CSiaFileTree(GetSiaCurl(), &GetSiaDriveConfig()));
|
||||
json result;
|
||||
if (ApiSuccess(GetSiaCurl().Get(L"/renter/files", result)))
|
||||
{
|
||||
siaFileTree->BuildTree(result);
|
||||
ret = SiaApiError::Success;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
178
src/siadrive_api/siawallet.cpp
Normal file
178
src/siadrive_api/siawallet.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include <siaapi.h>
|
||||
|
||||
using namespace Sia::Api;
|
||||
|
||||
static SString SeedLangToString(const SiaSeedLanguage& lang)
|
||||
{
|
||||
switch (lang)
|
||||
{
|
||||
case SiaSeedLanguage::English:
|
||||
return L"english";
|
||||
|
||||
case SiaSeedLanguage::German:
|
||||
return L"german";
|
||||
|
||||
case SiaSeedLanguage::Japanese:
|
||||
return L"japanese";
|
||||
|
||||
default:
|
||||
throw std::exception("Seed language not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
CSiaApi::_CSiaWallet::_CSiaWallet(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) :
|
||||
CSiaBase(siaCurl, siaDriveConfig),
|
||||
_Created(false),
|
||||
_Locked(false)
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
CSiaApi::_CSiaWallet::~_CSiaWallet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CSiaApi::_CSiaWallet::Refresh()
|
||||
{
|
||||
json result;
|
||||
SiaCurlError error = GetSiaCurl().Get(L"/wallet", result);
|
||||
if (ApiSuccess(error))
|
||||
{
|
||||
SetCreated(result["encrypted"].get<bool>());
|
||||
SetLocked(!result["unlocked"].get<bool>());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaWallet::Create(const SiaSeedLanguage& seedLanguage, SString& seed)
|
||||
{
|
||||
SiaApiError error = SiaApiError::RequestError;
|
||||
if (Refresh())
|
||||
{
|
||||
error = SiaApiError::WalletExists;
|
||||
if (!GetCreated())
|
||||
{
|
||||
error = SiaApiError::RequestError;
|
||||
json result;
|
||||
SiaCurlError cerror = GetSiaCurl().Post(L"/wallet/init", { {L"dictionary", SeedLangToString(seedLanguage)} }, result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
error = SiaApiError::Success;
|
||||
seed = result["primaryseed"].get<std::string>();
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaWallet::Restore(const SString& seed)
|
||||
{
|
||||
SiaApiError error = SiaApiError::NotImplemented;
|
||||
// TODO Future enhancement
|
||||
return error;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaWallet::Lock()
|
||||
{
|
||||
SiaApiError error = GetCreated() ? (GetLocked() ? SiaApiError::WalletLocked : SiaApiError::Success) : SiaApiError::WalletNotCreated;
|
||||
if (ApiSuccess(error))
|
||||
{
|
||||
error = SiaApiError::RequestError;
|
||||
|
||||
json result;
|
||||
SiaCurlError cerror = GetSiaCurl().Post(L"/wallet/lock", {}, result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
Refresh();
|
||||
error = SiaApiError::Success;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaWallet::Unlock(const SString& password)
|
||||
{
|
||||
SiaApiError error = GetCreated() ? (GetLocked() ? SiaApiError::Success : SiaApiError::WalletUnlocked) : SiaApiError::WalletNotCreated;
|
||||
if (ApiSuccess(error))
|
||||
{
|
||||
error = SiaApiError::RequestError;
|
||||
|
||||
json result;
|
||||
SiaCurlError cerror = GetSiaCurl().Post(L"/wallet/unlock", { {L"encryptionpassword", password} }, result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
Refresh();
|
||||
error = SiaApiError::Success;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*{
|
||||
"encrypted": true,
|
||||
"unlocked": true,
|
||||
|
||||
"confirmedsiacoinbalance": "123456", // hastings, big int
|
||||
"unconfirmedoutgoingsiacoins": "0", // hastings, big int
|
||||
"unconfirmedincomingsiacoins": "789", // hastings, big int
|
||||
|
||||
"siafundbalance": "1", // siafunds, big int
|
||||
"siacoinclaimbalance": "9001", // hastings, big int
|
||||
}*/
|
||||
SiaApiError CSiaApi::_CSiaWallet::GetConfirmedBalance(SiaCurrency& balance) const
|
||||
{
|
||||
SiaApiError ret = SiaApiError::RequestError;
|
||||
balance = 0;
|
||||
|
||||
json result;
|
||||
SiaCurlError cerror = GetSiaCurl().Get(L"/wallet", result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
balance = HastingsStringToSiaCurrency(result["confirmedsiacoinbalance"].get<std::string>());
|
||||
ret = SiaApiError::Success;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaWallet::GetUnonfirmedBalance(SiaCurrency& balance) const
|
||||
{
|
||||
SiaApiError ret = SiaApiError::RequestError;
|
||||
balance = 0;
|
||||
|
||||
json result;
|
||||
SiaCurlError cerror = GetSiaCurl().Get(L"/wallet", result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
SiaCurrency sc1 = HastingsStringToSiaCurrency(result["unconfirmedincomingsiacoins"].get<std::string>());
|
||||
SiaCurrency sc2 = HastingsStringToSiaCurrency(result["unconfirmedoutgoingsiacoins"].get<std::string>());
|
||||
balance = sc1 - sc2;
|
||||
ret = SiaApiError::Success;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SiaApiError CSiaApi::_CSiaWallet::GetAddress(SString& address) const
|
||||
{
|
||||
SiaApiError ret = SiaApiError::RequestError;
|
||||
address = L"";
|
||||
|
||||
json result;
|
||||
SiaCurlError cerror = GetSiaCurl().Get(L"/wallet/address", result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
address = result["address"].get<std::string>();
|
||||
ret = SiaApiError::Success;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
703
src/siadrive_api/uploadmanager.cpp
Normal file
703
src/siadrive_api/uploadmanager.cpp
Normal file
@@ -0,0 +1,703 @@
|
||||
#include <uploadmanager.h>
|
||||
#include <siaapi.h>
|
||||
#include <eventsystem.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, sd_file_path text unique not null, status integer not null"
|
||||
#define QUERY_STATUS "select id, sia_path, file_path, sd_file_path, status from upload_table where sia_path=@sia_path order by id desc limit 1;"
|
||||
#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, file_path, sd_file_path, status from upload_table where sia_path=@sia_path and (status=@status1 or status=@status2) order by id desc limit 1;"
|
||||
#define UPDATE_STATUS "update upload_table set status=@status where sia_path=@sia_path;"
|
||||
#define INSERT_UPLOAD "insert into upload_table (sia_path, status, file_path, sd_file_path) values (@sia_path, @status, @file_path, @sd_file_path);"
|
||||
#define DELETE_UPLOAD "delete from upload_table where sia_path=@sia_path;"
|
||||
|
||||
#define SET_STATUS(status, success_event, fail_event)\
|
||||
bool statusUpdated = false;\
|
||||
try\
|
||||
{\
|
||||
SQLite::Statement update(_uploadDatabase, UPDATE_STATUS);\
|
||||
update.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);\
|
||||
update.bind("@status", static_cast<unsigned>(status));\
|
||||
if (update.exec() != 1)\
|
||||
{\
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(fail_event(siaPath, filePath, status, CA2W(update.getErrorMsg()).m_psz)));\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(success_event(siaPath, filePath)));\
|
||||
statusUpdated = true;\
|
||||
}\
|
||||
}\
|
||||
catch (SQLite::Exception e)\
|
||||
{\
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));\
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
SString fmt(const SString &fmt, Ts... vs)
|
||||
{
|
||||
size_t required = _sntprintf(nullptr, 0, fmt.c_str(), vs...);
|
||||
|
||||
SString ret;
|
||||
ret.resize(required);
|
||||
_sntprintf(&ret[0], required, fmt.c_str(), vs...);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void CreateTableIfNotFound(SQLite::Database* database, const SString& tableName, const SString& columns)
|
||||
{
|
||||
SString sqlCreate = fmt(TABLE_CREATE, &tableName[0], &columns[0]);
|
||||
database->exec(CW2A(sqlCreate.c_str()));
|
||||
}
|
||||
|
||||
SString CUploadManager::UploadStatusToString(const UploadStatus& uploadStatus)
|
||||
{
|
||||
switch (uploadStatus)
|
||||
{
|
||||
case UploadStatus::Complete:
|
||||
return L"Complete";
|
||||
|
||||
case UploadStatus::Copying:
|
||||
return L"Copying";
|
||||
|
||||
case UploadStatus::Error:
|
||||
return L"Error";
|
||||
|
||||
case UploadStatus::Modified:
|
||||
return L"Modified";
|
||||
|
||||
case UploadStatus::NotFound:
|
||||
return L"Not Found";
|
||||
|
||||
case UploadStatus::Queued:
|
||||
return L"Queued";
|
||||
|
||||
case UploadStatus::Remove:
|
||||
return L"Remove";
|
||||
|
||||
case UploadStatus::Uploading:
|
||||
return L"Uploading";
|
||||
|
||||
default:
|
||||
return L"!!Not Defined!!";
|
||||
}
|
||||
}
|
||||
|
||||
CUploadManager::CUploadManager(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) :
|
||||
CAutoThread(siaCurl, siaDriveConfig),
|
||||
_uploadDatabase(siaDriveConfig->GetRenter_UploadDbFilePath(), SQLite::OPEN_CREATE | SQLite::OPEN_READWRITE),
|
||||
_fileThread(siaCurl, siaDriveConfig, [this](const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig) { this->FileThreadCallback(siaCurl, siaDriveConfig); })
|
||||
{
|
||||
CreateTableIfNotFound(&_uploadDatabase, UPLOAD_TABLE, UPLOAD_TABLE_COLUMNS);
|
||||
|
||||
// Clean-up cache folder
|
||||
if (!RecurDeleteFilesByExtentsion(CA2W(siaDriveConfig->GetCacheFolder().c_str()).m_psz, L".siadrive"))
|
||||
{
|
||||
throw StartupException(L"Failed to remove '.siadrive' files");
|
||||
}
|
||||
|
||||
if (!RecurDeleteFilesByExtentsion(CA2W(siaDriveConfig->GetCacheFolder().c_str()).m_psz, L".siadrive.temp"))
|
||||
{
|
||||
throw StartupException(L"Failed to remove '.siadrive.temp' files");
|
||||
}
|
||||
|
||||
// Re-add files to file action queue
|
||||
UpdateFileQueueOnStartup();
|
||||
|
||||
// Detect files that have been removed since last startup
|
||||
DeleteFilesRemovedFromSia(siaCurl, siaDriveConfig, true);
|
||||
|
||||
// Begin normal processing
|
||||
StartAutoThread();
|
||||
_fileThread.StartAutoThread();
|
||||
}
|
||||
|
||||
CUploadManager::~CUploadManager()
|
||||
{
|
||||
// Stop all processing
|
||||
_fileThread.StopAutoThread();
|
||||
StopAutoThread();
|
||||
}
|
||||
|
||||
void CUploadManager::UpdateFileQueueOnStartup()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Re-add Copying and Remove
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_UPLOADS_BY_SIA_PATH_AND_2_STATUS);
|
||||
query.bind("@status1", static_cast<unsigned>(UploadStatus::Copying));
|
||||
query.bind("@status1", static_cast<unsigned>(UploadStatus::Remove));
|
||||
while (query.executeStep())
|
||||
{
|
||||
SString siaPath = CA2W(query.getColumn(query.getColumnIndex("sia_path"))).m_psz;
|
||||
SString filePath = CA2W(query.getColumn(query.getColumnIndex("file_path"))).m_psz;
|
||||
SString siaDriveFilePath = CA2W(query.getColumn(query.getColumnIndex("sd_file_path"))).m_psz;
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(query.getColumn(query.getColumnIndex("status")).getUInt());
|
||||
|
||||
SString temp = filePath;
|
||||
::PathRemoveFileSpec(&temp[0]);
|
||||
SString rootPath = temp;
|
||||
|
||||
// Strip drive specification (i.e. C:\)
|
||||
// TODO If mount to folder is ever enabled, this will need to change
|
||||
SString siaDriveFileName = GenerateSha256(&filePath[3]) + L".siadrive";
|
||||
|
||||
SString tempSourcePath;
|
||||
tempSourcePath.resize(MAX_PATH + 1);
|
||||
PathCombine(&tempSourcePath[0], rootPath.c_str(), (siaDriveFileName + L".temp").c_str());
|
||||
|
||||
std::lock_guard<std::mutex> l(_fileQueueMutex);
|
||||
if (uploadStatus == UploadStatus::Remove)
|
||||
{
|
||||
_fileQueue.push_back([=]() { this->FileAction(CSiaCurl(GetHostConfig()), siaPath, filePath, nullptr, siaDriveFilePath, true); });
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileRemoveAdded(siaPath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_fileQueue.push_back([=]() { this->FileAction(CSiaCurl(GetHostConfig()), siaPath, filePath, tempSourcePath, siaDriveFilePath, false); });
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(NewFileAdded(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
throw StartupException(e.getErrorStr());
|
||||
}
|
||||
}
|
||||
|
||||
void CUploadManager::DeleteFilesRemovedFromSia(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig, const bool& isStartup)
|
||||
{
|
||||
CSiaFileTreePtr fileTree(new CSiaFileTree(siaCurl, siaDriveConfig));
|
||||
json result;
|
||||
SiaCurlError cerror = siaCurl.Get(L"/renter/files", result);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
fileTree->BuildTree(result);
|
||||
auto fileList = fileTree->GetFileList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isStartup)
|
||||
{
|
||||
throw StartupException(L"Failed to get Sia files");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CUploadManager::FileThreadCallback(const CSiaCurl& siaCurl, CSiaDriveConfig* siaDriveConfig)
|
||||
{
|
||||
std::function<void()> nextFile = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_fileQueueMutex);
|
||||
if (_fileQueue.size())
|
||||
{
|
||||
nextFile = _fileQueue.front();
|
||||
_fileQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
if (nextFile)
|
||||
{
|
||||
nextFile();
|
||||
}
|
||||
}
|
||||
|
||||
void CUploadManager::HandleFileRemove(const CSiaCurl& siaCurl, const SString& siaPath, const SString& siaDriveFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_STATUS);
|
||||
query.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
||||
if (query.executeStep())
|
||||
{
|
||||
SString removeFilePath = CA2W(query.getColumn(query.getColumnIndex("file_path"))).m_psz;
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(query.getColumnIndex("status"))));
|
||||
// Make sure status is still remove
|
||||
if (uploadStatus == UploadStatus::Remove)
|
||||
{
|
||||
bool deleteFromDb = true;
|
||||
if (::PathFileExists(removeFilePath.c_str()))
|
||||
{
|
||||
if (RetryDeleteFileIfExists(removeFilePath.c_str()))
|
||||
{
|
||||
if (!RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, removeFilePath, siaDriveFilePath)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(RemoveFileFailed(siaPath, removeFilePath)));
|
||||
deleteFromDb = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleteFromDb)
|
||||
{
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/delete") + siaPath, {}, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
// TODO validate response
|
||||
|
||||
SQLite::Statement del(_uploadDatabase, DELETE_UPLOAD);
|
||||
del.bind("@sia_path", CW2A(siaPath.c_str()));
|
||||
if (del.exec() == 1)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileRemoved(siaPath, removeFilePath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseDeleteFailed(siaPath, removeFilePath, CA2W(del.getErrorMsg()).m_psz)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FailedToDeleteFromSia(siaPath, removeFilePath, cerror)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
}
|
||||
}
|
||||
|
||||
void CUploadManager::HandleAddFile(const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath)
|
||||
{
|
||||
// Check for retry condition
|
||||
if (!::PathFileExists(tempSourcePath.c_str()) && ::PathFileExists(siaDriveFilePath.c_str()))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_STATUS);
|
||||
query.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
||||
if (query.executeStep())
|
||||
{
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(query.getColumnIndex("status"))));
|
||||
if (uploadStatus == UploadStatus::Copying)
|
||||
{
|
||||
if (RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
SET_STATUS(UploadStatus::Queued, UploadAddedToQueue, ModifyUploadStatusFailed)
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(CreatingTemporarySiaDriveFile(siaPath, filePath, tempSourcePath)));
|
||||
if (RetryableAction(::CopyFile(filePath.c_str(), tempSourcePath.c_str(), FALSE), DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY_MS))
|
||||
{
|
||||
// Delete existing '.siadrive' file, if found
|
||||
// !!Should never come here. If so, there was a problem with startup clean-up
|
||||
if (!RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
|
||||
// Rename '.siadrive.temp' to '.siadrive'
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(RenamingTemporarySiaDriveFile(siaPath, filePath, tempSourcePath, siaDriveFilePath)));
|
||||
if (RetryableAction(::MoveFile(tempSourcePath.c_str(), siaDriveFilePath.c_str()), DEFAULT_RETRY_COUNT, DEFAULT_RETRY_DELAY_MS))
|
||||
{
|
||||
if (!RetryDeleteFileIfExists(tempSourcePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_STATUS);
|
||||
query.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
||||
if (query.executeStep())
|
||||
{
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(query.getColumnIndex("status"))));
|
||||
if (uploadStatus == UploadStatus::Copying)
|
||||
{
|
||||
SET_STATUS(UploadStatus::Queued, UploadAddedToQueue, ModifyUploadStatusFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(RenamingTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath, siaDriveFilePath)));
|
||||
if (!RetryDeleteFileIfExists(tempSourcePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
}
|
||||
|
||||
if (!RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
|
||||
// Requeued
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(CreatingTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
|
||||
// If temp copy fails, try to delete
|
||||
// If partial copy and file is unable to be deleted, log warning
|
||||
if (!RetryDeleteFileIfExists(tempSourcePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteTemporarySiaDriveFileFailed(siaPath, filePath, tempSourcePath)));
|
||||
}
|
||||
|
||||
// Requeued
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CUploadManager::FileAction(const CSiaCurl& siaCurl, const SString& siaPath, const SString& filePath, const SString& tempSourcePath, const SString& siaDriveFilePath, const bool& remove)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_fileActionMutex);
|
||||
_activeSiaPath = siaPath;
|
||||
}
|
||||
|
||||
if (remove)
|
||||
{
|
||||
HandleFileRemove(siaCurl, siaPath, siaDriveFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleAddFile(siaPath, filePath, tempSourcePath, siaDriveFilePath);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_fileActionMutex);
|
||||
_activeSiaPath.empty();
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
SString siaPath = CA2W(query.getColumn(query.getColumnIndex("sia_path"))).m_psz;
|
||||
SString filePath = CA2W(query.getColumn(query.getColumnIndex("file_path"))).m_psz;
|
||||
SString siaDriveFilePath = CA2W(query.getColumn(query.getColumnIndex("sd_file_path"))).m_psz;
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(query.getColumn(query.getColumnIndex("status")).getUInt());
|
||||
|
||||
auto fileList = fileTree->GetFileList();
|
||||
auto it = std::find_if(fileList.begin(), fileList.end(), [&](const CSiaFilePtr& ptr)
|
||||
{
|
||||
return ptr->GetSiaPath() == siaPath;
|
||||
});
|
||||
|
||||
// Removed by another client
|
||||
if (it == fileList.end())
|
||||
{
|
||||
SET_STATUS(UploadStatus::Remove, ExternallyRemovedFileDetected, ModifyUploadStatusFailed)
|
||||
if (statusUpdated)
|
||||
{
|
||||
std::lock_guard<std::mutex> l2(_fileQueueMutex);
|
||||
_fileQueue.push_back([=]() { this->FileAction(CSiaCurl(GetHostConfig()), siaPath, filePath, nullptr, siaDriveFilePath, true); });
|
||||
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileRemoveAdded(siaPath)));
|
||||
}
|
||||
}
|
||||
// Changed file detected
|
||||
else if (uploadStatus == UploadStatus::Modified)
|
||||
{
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/delete") + siaPath, {}, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
// TODO validate response
|
||||
SET_STATUS(UploadStatus::Queued, ModifiedUploadQueued, ModifyUploadStatusFailed)
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FailedToDeleteFromSia(siaPath, filePath, cerror)));
|
||||
}
|
||||
}
|
||||
// Upload is complete
|
||||
else if ((*it)->GetUploadProgress() >= 100)
|
||||
{
|
||||
SET_STATUS(UploadStatus::Complete, UploadComplete, ModifyUploadStatusFailed)
|
||||
if (!RetryDeleteFileIfExists(siaDriveFilePath))
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DeleteSiaDriveFileFailed(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
}
|
||||
// Upload still active, don't process another file
|
||||
else
|
||||
{
|
||||
processNext = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// error condition - host down?
|
||||
processNext = false;
|
||||
}
|
||||
}
|
||||
catch (const SQLite::Exception& e)
|
||||
{
|
||||
// error condition - database not initialized (i.e. no table)?
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
processNext = false;
|
||||
}
|
||||
|
||||
if (processNext)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
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())
|
||||
{
|
||||
SString siaPath = CA2W(query.getColumn(query.getColumnIndex("sia_path"))).m_psz;
|
||||
SString filePath = CA2W(query.getColumn(query.getColumnIndex("file_path"))).m_psz;
|
||||
|
||||
// TODO Validate response
|
||||
json response;
|
||||
SiaCurlError cerror = siaCurl.Post(SString(L"/renter/upload") + siaPath, { {L"source", filePath} }, response);
|
||||
if (ApiSuccess(cerror))
|
||||
{
|
||||
SET_STATUS(UploadStatus::Uploading, UploadToSiaStarted, ModifyUploadStatusFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const SQLite::Exception& e)
|
||||
{
|
||||
// error condition
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UploadStatus CUploadManager::GetUploadStatus(const SString& 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(query.getColumnIndex("status"))));
|
||||
}
|
||||
|
||||
return uploadStatus;
|
||||
}
|
||||
|
||||
// The real source file is copied to a hidden file. The hidden filename is constructed by generating an SHA25 hash of the
|
||||
// source path (all lowercase). '.siadrive.temp' will be used as the extension. After copy is successful, the extension
|
||||
// is renamed to '.siadrive'. '.siadrive' files will be hidden/system and used by the Dokan API for file i/o until Sia upload
|
||||
// is complete. If a change occurs, the file will be deleted from Sia (cancelling the in-progress upload) and renamed to the
|
||||
// real source path. The process will then start over again as if the file was new.
|
||||
// If the file has been fully uploaded, the hidden file should not exist, so rename will not occur; however, the file
|
||||
// will be deleted from Sia and treated as new.
|
||||
// Uploads will always use the real file. User i/o will occur against the hidden file temporarily. Since upload will take
|
||||
// longer to handle than a normal file copy, this seems to be the best compromise for performance.
|
||||
//
|
||||
// Error Scenarios:
|
||||
// Crash before db update to status copying - file will be re-uploaded automatically, if complete; otherwise, deleted
|
||||
// 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
|
||||
UploadError CUploadManager::AddOrUpdate(const SString& siaPath, SString filePath)
|
||||
{
|
||||
UploadError ret = UploadError::Success;
|
||||
|
||||
// Relative to absolute and grab parent folder of source
|
||||
SString rootPath;
|
||||
{
|
||||
SString temp;
|
||||
if (::PathIsRelative(filePath.c_str()))
|
||||
{
|
||||
temp.resize(MAX_PATH + 1);
|
||||
filePath = _wfullpath(&temp[0], filePath.c_str(), MAX_PATH);
|
||||
}
|
||||
|
||||
temp = filePath;
|
||||
::PathRemoveFileSpec(&temp[0]);
|
||||
rootPath = temp;
|
||||
}
|
||||
|
||||
if (::PathFileExists(filePath.c_str()))
|
||||
{
|
||||
// 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);
|
||||
|
||||
try
|
||||
{
|
||||
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())
|
||||
{
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(query.getColumnIndex("status"))));
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(ExistingUploadFound(siaPath, filePath, uploadStatus)));
|
||||
if (uploadStatus == UploadStatus::Uploading)
|
||||
{
|
||||
SET_STATUS(UploadStatus::Modified, UploadStatusSetToModified, ModifyUploadStatusFailed)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Strip drive specification (i.e. C:\)
|
||||
// TODO If mount to folder is ever enabled, this will need to change
|
||||
// TODO Case sensative file names? Going to be a bit of an issue.
|
||||
SString siaDriveFileName = GenerateSha256(&filePath[3]) + L".siadrive";
|
||||
|
||||
SString siaDriveFilePath;
|
||||
siaDriveFilePath.resize(MAX_PATH + 1);
|
||||
PathCombine(&siaDriveFilePath[0], rootPath.c_str(), siaDriveFileName.c_str());
|
||||
|
||||
SString tempSourcePath;
|
||||
tempSourcePath.resize(MAX_PATH + 1);
|
||||
PathCombine(&tempSourcePath[0], rootPath.c_str(), (siaDriveFileName + L".temp").c_str());
|
||||
|
||||
// Add to db
|
||||
try
|
||||
{
|
||||
SQLite::Statement insert(_uploadDatabase, INSERT_UPLOAD);
|
||||
insert.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
||||
insert.bind("@file_path", CW2A(filePath.c_str()).m_psz);
|
||||
insert.bind("@sd_file_path", CW2A(siaDriveFileName.c_str()).m_psz);
|
||||
insert.bind("@status", static_cast<unsigned>(UploadStatus::Copying));
|
||||
if (insert.exec() == 1)
|
||||
{
|
||||
// Queue file upload operation
|
||||
std::lock_guard<std::mutex> l2(_fileQueueMutex);
|
||||
_fileQueue.push_back([=]() { this->FileAction(CSiaCurl(GetHostConfig()), siaPath, filePath, tempSourcePath, siaDriveFilePath, false); });
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(NewFileAdded(siaPath, filePath, siaDriveFilePath)));
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseInsertFailed(siaPath, filePath, CA2W(insert.getErrorMsg()).m_psz)));
|
||||
ret = UploadError::DatabaseError;
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
ret = UploadError::DatabaseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
ret = UploadError::DatabaseError;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(SourceFileNotFound(siaPath, filePath)));
|
||||
ret = UploadError::SourceFileNotFound;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UploadError CUploadManager::Remove(const SString& siaPath)
|
||||
{
|
||||
UploadError ret = UploadError::Success;
|
||||
std::lock_guard<std::mutex> l(_uploadMutex);
|
||||
try
|
||||
{
|
||||
bool remove = false;
|
||||
|
||||
SQLite::Statement query(_uploadDatabase, QUERY_STATUS);
|
||||
query.bind("@sia_path", CW2A(siaPath.c_str()).m_psz);
|
||||
if (query.executeStep())
|
||||
{
|
||||
SString filePath = CA2W(query.getColumn(query.getColumnIndex("file_path"))).m_psz;
|
||||
SString siaDriveFilePath = CA2W(query.getColumn(query.getColumnIndex("sd_file_path"))).m_psz;
|
||||
UploadStatus uploadStatus = static_cast<UploadStatus>(static_cast<unsigned>(query.getColumn(query.getColumnIndex("status"))));
|
||||
switch (uploadStatus)
|
||||
{
|
||||
case UploadStatus::Complete:
|
||||
case UploadStatus::Queued:
|
||||
case UploadStatus::Modified:
|
||||
case UploadStatus::Copying:
|
||||
case UploadStatus::Uploading:
|
||||
{
|
||||
SET_STATUS(UploadStatus::Remove, UploadStatusSetToRemoved, ModifyUploadStatusFailed)
|
||||
remove = statusUpdated;
|
||||
if (!statusUpdated)
|
||||
{
|
||||
ret = UploadError::DatabaseError;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
remove = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (remove)
|
||||
{
|
||||
std::lock_guard<std::mutex> l2(_fileQueueMutex);
|
||||
_fileQueue.push_back([=]() { this->FileAction(CSiaCurl(GetHostConfig()), siaPath, filePath, nullptr, siaDriveFilePath, true); });
|
||||
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(FileRemoveAdded(siaPath)));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLite::Exception e)
|
||||
{
|
||||
CEventSystem::EventSystem.NotifyEvent(CreateSystemEvent(DatabaseExceptionOccurred(e)));
|
||||
ret = UploadError::DatabaseError;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user