Compare commits

...

3 Commits

38 changed files with 1396 additions and 17 deletions

View File

@@ -5,6 +5,7 @@ project(TessesFramework VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17)
list(APPEND TESSESFRAMEWORK_SOURCE
src/Random.cpp
src/Date/Date.cpp
src/Http/FileServer.cpp
src/Http/MountableServer.cpp
@@ -22,6 +23,7 @@ src/Mail/Smtp.cpp
src/Serialization/Json.cpp
src/Serialization/SQLite.cpp
src/Serialization/BitConverter.cpp
src/Serialization/Bencode.cpp
src/Platform/Environment.cpp
src/Platform/Process.cpp
src/Streams/FileStream.cpp
@@ -58,6 +60,8 @@ src/Crypto/MbedTLS/Crypto.cpp
src/Args.cpp
src/TF_Init.cpp
src/HiddenField.cpp
src/BitTorrent/TorrentFile.cpp
src/BitTorrent/TorrentStream.cpp
)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
@@ -384,6 +388,26 @@ install(TARGETS ttime DESTINATION "${CMAKE_INSTALL_BINDIR}")
add_executable(tshell apps/tshell.cpp)
target_link_libraries(tshell PUBLIC tessesframework)
install(TARGETS tshell DESTINATION "${CMAKE_INSTALL_BINDIR}")
add_executable(tprinttorrent apps/printtorrent.cpp)
target_link_libraries(tprinttorrent PUBLIC tessesframework)
install(TARGETS tprinttorrent DESTINATION "${CMAKE_INSTALL_BINDIR}")
add_executable(ttorrentcreate apps/ttorrentcreate.cpp)
target_link_libraries(ttorrentcreate PUBLIC tessesframework)
install(TARGETS ttorrentcreate DESTINATION "${CMAKE_INSTALL_BINDIR}")
add_executable(tclearwebseed apps/tclearwebseed.cpp)
target_link_libraries(tclearwebseed PUBLIC tessesframework)
install(TARGETS tclearwebseed DESTINATION "${CMAKE_INSTALL_BINDIR}")
add_executable(trng apps/trng.cpp)
target_link_libraries(trng PUBLIC tessesframework)
install(TARGETS trng DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif()
include(InstallRequiredSystemLibraries)

26
apps/printtorrent.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include "TessesFramework/TessesFramework.hpp"
using namespace Tesses::Framework;
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::Filesystem;
using namespace Tesses::Framework::Serialization::Bencode;
using namespace Tesses::Framework::TextStreams;
using namespace Tesses::Framework::BitTorrent;
int main(int argc, char** argv)
{
TF_Init();
if(argc < 2)
{
printf("USAGE: %s /path/to/torrent/file\n",argv[0]);
return 1;
}
auto strm = LocalFS->OpenFile((std::string)argv[1],"rb");
auto bencode = Bencode::Load(strm);
if(std::holds_alternative<BeDictionary>(bencode))
{
TorrentFile file(std::get<BeDictionary>(bencode));
file.Print(std::make_shared<ConsoleWriter>());
}
return 0;
}

22
apps/tclearwebseed.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "TessesFramework/TessesFramework.hpp"
using namespace Tesses::Framework;
using namespace Tesses::Framework::BitTorrent;
using namespace Tesses::Framework::Serialization::Bencode;
using namespace Tesses::Framework::Filesystem;
int main(int argc, char** argv)
{
TF_Init();
if(argc < 2)
{
printf("USAGE: %s torrent_file\n",argv[0]);
return 1;
}
auto strm = LocalFS->OpenFile((std::string)argv[1],"rb");
auto data = Bencode::Load(strm);
if(std::holds_alternative<BeDictionary>(data)){
std::get<BeDictionary>(data).SetValue("url-list",BeUndefined());
}
strm = LocalFS->OpenFile((std::string)argv[1],"wb");
Bencode::Save(strm,data);
return 0;
}

36
apps/trng.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include "TessesFramework/TessesFramework.hpp"
int main(int argc, char** argv)
{
if(argc < 2)
{
std::cout << "USAGE: " << argv[0] << " times" << std::endl;
std::cout << "USAGE: " << argv[0] << " times max" << std::endl;
std::cout << "USAGE: " << argv[0] << " times min max" << std::endl;
return 0;
}
Tesses::Framework::Random random;
int times=std::atoi(argv[1]);
if(argc > 2)
{
int32_t min = 0;
int32_t max = std::stoi(argv[2]);
if(argc > 3)
{
min = max;
max = std::stoi(argv[3]);
}
for(int i = 0; i < times; i++)
{
std::cout << random.Next(min,max) << std::endl;
}
}
else {
for(int i = 0; i < times; i++)
{
std::cout << random.Next() << std::endl;
}
}
}

66
apps/ttorrentcreate.cpp Normal file
View File

@@ -0,0 +1,66 @@
#include "TessesFramework/TessesFramework.hpp"
using namespace Tesses::Framework;
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::Filesystem;
using namespace Tesses::Framework::Serialization::Bencode;
using namespace Tesses::Framework::TextStreams;
using namespace Tesses::Framework::BitTorrent;
void usage(Args& args)
{
std::cout << "USAGE: " << args.filename << " [options] torrent_contents torrent_file.torrent" << std::endl << std::endl;
std::cout << "OPTIONS:" << std::endl;
std::cout << "tracker: torrent tracker url (require at least one)" << std::endl;
std::cout << "webseed: url for web seeding" << std::endl;
std::cout << "comment: set the comment" << std::endl;
std::cout << "created_by: set the created by field, defaults to TessesFrameworkTorrent" << std::endl;
std::cout << "piece_length: set the piece length, defaults to " + std::to_string(DEFAULT_PIECE_LENGTH) << std::endl << std::endl;
std::cout << "FLAGS:" << std::endl;
std::cout << "help: bring this help message up" << std::endl;
std::cout << "private: enable private tracker flag" << std::endl;
exit(1);
}
int main(int argc, char** argv)
{
TF_Init();
std::vector<BeString> webseeds;
std::vector<BeString> trackers;
bool isPrivate=false;
int64_t pieceLength = DEFAULT_PIECE_LENGTH;
std::string comment="";
std::string created_by = "TessesFrameworkTorrent";
Args args(argc, argv);
if(args.positional.size() < 2)
usage(args);
for(auto& opt : args.options)
{
if(opt.first == "tracker")
trackers.push_back(opt.second);
else if(opt.first == "webseed")
webseeds.push_back(opt.second);
else if(opt.first == "comment")
comment = opt.second;
else if(opt.first == "created_by")
created_by = opt.second;
else if(opt.first == "piece_length")
pieceLength = std::stoll(opt.second);
}
for(auto& flag : args.flags)
{
if(flag == "help")
usage(args);
if(flag == "private")
isPrivate = true;
}
if(trackers.empty())
usage(args);
{
auto torrent_file_stream = LocalFS->OpenFile(args.positional[1],"wb");
TorrentFile::CreateTorrent(torrent_file_stream,trackers,webseeds,LocalFS, args.positional[0], isPrivate, pieceLength, comment, created_by);
}
return 0;
}

View File

@@ -2,6 +2,7 @@
int main(int argc, char** argv)
{
Tesses::Framework::TF_Init();
std::cout << "Basic auth test" << std::endl;
std::string theEncoded="dXNlcjpwYXNz";
Tesses::Framework::Http::ServerContext ctx(nullptr);
ctx.requestHeaders.SetValue("Authorization","Basic " + theEncoded);
@@ -10,4 +11,5 @@ int main(int argc, char** argv)
std::cout << Tesses::Framework::Http::BasicAuthServer::GetCreds(ctx,user,pass) << std::endl;
std::cout << user << std::endl;
std::cout << pass << std::endl;
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include "../Serialization/Bencode.hpp"
#include "TorrentStream.hpp"
namespace Tesses::Framework::BitTorrent
{
constexpr int DEFAULT_PIECE_LENGTH = 524288;
class TorrentFileInfo {
Serialization::Bencode::BeDictionary info;
public:
TorrentFileInfo();
TorrentFileInfo(const Serialization::Bencode::BeDictionary& tkn);
int64_t piece_length=DEFAULT_PIECE_LENGTH;
Serialization::Bencode::BeString pieces;
int64_t isPrivate=0;
Serialization::Bencode::BeString name;
std::variant<int64_t, std::vector<TorrentFileEntry>> fileListOrLength;
int64_t GetTorrentFileSize();
/// @brief Generate the info
/// @return the dictionary containing the info
Serialization::Bencode::BeDictionary& GenerateInfoDictionary();
/// @brief Get the info hash for info (if you are filling out the fields, you need to call GenerateInfoDictionary first)
/// @return the 20 byte info hash
Serialization::Bencode::BeString GetInfoHash();
/// @brief Get the info (does not regenerate info from the fields)
/// @return the dictionary containing the info
Serialization::Bencode::BeDictionary& GetInfoDictionary();
/// @brief Used to load a dictionary in, will overwrite all the fields
/// @param dict the dictionary to load
void Load(const Serialization::Bencode::BeDictionary& dict);
/// @brief Gets a read write based on this info object
/// @param vfs the vfs object
/// @param path file or directory inside vfs
/// @return The read write at object
std::shared_ptr<ReadWriteAt> GetStreamFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path);
/// @brief Create info from file/directory
/// @param vfs the vfs object
/// @param path file or directory inside vfs
/// @param isPrivate whether you use private trackers
/// @param pieceLength length of pieces
void CreateFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate=false, int64_t pieceLength=DEFAULT_PIECE_LENGTH);
};
class TorrentFile {
public:
TorrentFile();
TorrentFile(const Serialization::Bencode::BeDictionary& tkn);
Serialization::Bencode::BeString announce;
std::vector<Serialization::Bencode::BeString> announce_list;
std::vector<Serialization::Bencode::BeString> url_list;
Serialization::Bencode::BeString comment;
Serialization::Bencode::BeString created_by;
int64_t creation_date=0;
TorrentFileInfo info;
Serialization::Bencode::BeDictionary ToDictionary();
void Print(std::shared_ptr<TextStreams::TextWriter> writer);
static void CreateTorrent(std::shared_ptr<Tesses::Framework::Streams::Stream> torrent_file_stream,const std::vector<Serialization::Bencode::BeString>& trackers,const std::vector<Serialization::Bencode::BeString>& webseeds, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate=false, int64_t pieceLength=DEFAULT_PIECE_LENGTH, Serialization::Bencode::BeString comment="", Serialization::Bencode::BeString created_by = "TessesFrameworkTorrent");
};
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "TessesFramework/Http/HttpClient.hpp"
#include "TessesFramework/Streams/NetworkStream.hpp"
namespace Tesses::Framework::BitTorrent
{
class TorrentFileEntry {
public:
TorrentFileEntry();
TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, int64_t length);
Tesses::Framework::Filesystem::VFSPath path;
int64_t length=0;
};
class ReadWriteAt {
public:
virtual bool ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)=0;
virtual void WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)=0;
virtual ~ReadWriteAt();
};
class TorrentFileStream : public ReadWriteAt
{
Tesses::Framework::Threading::Mutex mtx;
public:
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs;
Tesses::Framework::Filesystem::VFSPath path;
uint64_t length;
TorrentFileStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path, uint64_t length);
bool ReadBlockAt(uint64_t offset, uint8_t* data, size_t len);
void WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len);
};
class TorrentDirectoryStream : public ReadWriteAt
{
std::vector<Tesses::Framework::Threading::Mutex> mtxs;
public:
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs;
Tesses::Framework::Filesystem::VFSPath path;
std::vector<TorrentFileEntry> entries;
TorrentDirectoryStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,std::vector<TorrentFileEntry> entries);
bool ReadBlockAt(uint64_t offset, uint8_t* data, size_t len);
void WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len);
};
}

View File

@@ -9,6 +9,7 @@
#include <vector>
#include <functional>
#include "Threading/Mutex.hpp"
#include <optional>
namespace Tesses::Framework
{
@@ -88,8 +89,10 @@ namespace Tesses::Framework
};
extern EventList<uint64_t> OnItteraton;
std::optional<std::string> TF_GetCommandName();
void TF_Init();
void TF_InitWithConsole();
void TF_AllowPortable(std::string argv0);
void TF_ConnectToSelf(uint16_t port);
void TF_InitEventLoop();
void TF_RunEventLoop();
@@ -101,6 +104,7 @@ namespace Tesses::Framework
void TF_SetConsoleEventsEnabled(bool flag);
void TF_InitConsole();
void TF_Invoke(std::function<void()> cb);
std::string TF_FileSize(uint64_t bytes, bool usesBin=true);
#if defined(TESSESFRAMEWORK_LOGTOFILE)
void TF_Log(std::string dataToLog);

View File

@@ -39,6 +39,9 @@ namespace Tesses::Framework::Filesystem
void Chmod(VFSPath path, uint32_t mode);
void Lock(VFSPath path);
void Unlock(VFSPath path);
};
extern std::shared_ptr<LocalFilesystem> LocalFS;
}

View File

@@ -57,5 +57,7 @@ namespace Tesses::Framework::Filesystem
void Chmod(VFSPath path, uint32_t mode);
void Lock(VFSPath path);
void Unlock(VFSPath path);
};
}

View File

@@ -41,5 +41,7 @@ namespace Tesses::Framework::Filesystem
void Chmod(VFSPath path, uint32_t mode);
void Lock(VFSPath path);
void Unlock(VFSPath path);
};
}

View File

@@ -46,6 +46,9 @@ namespace Tesses::Framework::Filesystem
void Chmod(VFSPath path, uint32_t mode);
void Close();
void Lock(VFSPath path);
void Unlock(VFSPath path);
~TempFS();
};
}

View File

@@ -153,6 +153,9 @@ namespace Tesses::Framework::Filesystem
virtual void Chmod(VFSPath path, uint32_t mode);
virtual void Lock(VFSPath path);
virtual void Unlock(VFSPath path);
virtual ~VFS();
virtual void Close();

View File

@@ -3,4 +3,8 @@
#undef DeleteFile
#undef MoveFile
#undef MoveDirectory
/*
Just in case
*/
#undef Lock
#undef Unlock

View File

@@ -145,6 +145,10 @@ struct CaseInsensitiveLess {
public:
static char NibbleToHex(uint8_t nibble);
static uint8_t HexToNibble(char c);
static std::string BytesToHex(const std::vector<uint8_t>& data);
static void BytesToHex(std::string& text,const std::vector<uint8_t>& data);
static std::vector<uint8_t> HexToBytes(const std::string& text);
static void HexToBytes(std::vector<uint8_t>& data,const std::string& text);
static std::string MimeType(std::filesystem::path p);
static bool Invalid(char c);
static std::string Sanitise(std::string text);

View File

@@ -0,0 +1,15 @@
#pragma once
#include "Common.hpp"
namespace Tesses::Framework {
class Random {
uint64_t num;
public:
Random();
Random(uint64_t seed);
uint64_t Next();
uint32_t Next(uint32_t max);
int32_t Next(int32_t min,int32_t max);
uint8_t NextByte();
};
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include <cstdint>
#include <vector>
#include <variant>
#include <string>
#include "../Streams/Stream.hpp"
#include "../TextStreams/TextWriter.hpp"
#include "Json.hpp"
namespace Tesses::Framework::Serialization::Bencode {
class BeArray;
class BeDictionary;
class BeString;
using BeUndefined = std::monostate;
using BeToken = std::variant<BeUndefined,BeArray,BeDictionary,BeString,int64_t>;
class BeArray {
public:
std::vector<BeToken> tokens;
};
class BeDictionary {
public:
std::vector<std::pair<BeString,BeToken>> tokens;
BeToken GetValue(BeString key) const;
void SetValue(BeString key, BeToken value);
};
class BeString {
public:
BeString();
BeString(const std::string& text);
BeString(const char* text);
BeString(const std::vector<uint8_t>& data);
std::vector<uint8_t> data;
operator std::string() const;
};
bool operator==(const BeString& lStr, const BeString& rStr);
bool operator==(const BeString& lStr, const std::string& rStr);
bool operator==(const std::string& lStr, const BeString& rStr);
bool operator==(const BeString& lStr, const char* rStr);
bool operator==(const char* lStr, const BeString& rStr);
bool operator!=(const BeString& lStr, const BeString& rStr);
bool operator!=(const BeString& lStr, const std::string& rStr);
bool operator!=(const std::string& lStr, const BeString& rStr);
bool operator!=(const BeString& lStr, const char* rStr);
bool operator!=(const char* lStr, const BeString& rStr);
class Bencode {
public:
static void Save(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,const BeToken& value);
static BeToken Load(std::shared_ptr<Tesses::Framework::Streams::Stream> strm);
//This cannot be converted back to torrent, this may (probably will) be out of order
static Json::JToken ToJson(const BeToken& tkn);
//This may (probably will) be out of order
static void Print(std::shared_ptr<Tesses::Framework::TextStreams::TextWriter> writer, BeToken tkn);
};
}

View File

@@ -9,9 +9,7 @@ namespace Tesses::Framework::Serialization::Json
{
class JArray;
class JObject;
class JUndefined {
public:
};
using JUndefined = std::monostate;
using JToken = std::variant<JUndefined,std::nullptr_t,bool,int64_t,double,std::string, JArray, JObject>;

View File

@@ -34,6 +34,7 @@ namespace Tesses::Framework::Streams
bool success;
bool endOfStream;
public:
bool DataAvailable(int timeout=0);
bool EndOfStream();
bool CanRead();
bool CanWrite();

View File

@@ -39,7 +39,10 @@
#include "HiddenField.hpp"
#include "Serialization/Json.hpp"
#include "Serialization/SQLite.hpp"
#include "Serialization/Bencode.hpp"
#include "Platform/Environment.hpp"
#include "Platform/Process.hpp"
#include "Serialization/BitConverter.hpp"
#include "Args.hpp"
#include "Args.hpp"
#include "BitTorrent/TorrentFile.hpp"
#include "Random.hpp"

View File

@@ -5,12 +5,14 @@ namespace Tesses::Framework::TextStreams
{
class TextReader
{
bool eof=false;
public:
virtual bool Rewind();
virtual bool ReadBlock(std::string& str,size_t sz)=0;
int32_t ReadChar();
std::string ReadLine();
bool ReadLine(std::string& str);
bool ReadLineHttp(std::string& str);
void ReadAllLines(std::vector<std::string>& lines);
std::string ReadToEnd();
void ReadToEnd(std::string& str);

View File

@@ -0,0 +1,379 @@
#include "TessesFramework/BitTorrent/TorrentFile.hpp"
#include "TessesFramework/Streams/MemoryStream.hpp"
#include "TessesFramework/Crypto/Crypto.hpp"
#include "TessesFramework/Http/HttpUtils.hpp"
namespace Tesses::Framework::BitTorrent
{
TorrentFileInfo::TorrentFileInfo()
{
this->fileListOrLength = 0;
}
TorrentFileInfo::TorrentFileInfo(const Serialization::Bencode::BeDictionary& tkn)
{
Load(tkn);
}
int64_t TorrentFileInfo::GetTorrentFileSize()
{
if(std::holds_alternative<int64_t>(this->fileListOrLength))
return std::get<int64_t>(fileListOrLength);
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength)) {
auto& files= std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength);
int64_t no=0;
for(auto itm : files)
{
no += itm.length;
}
return no;
}
return 0;
}
Serialization::Bencode::BeDictionary& TorrentFileInfo::GenerateInfoDictionary()
{
this->info = {};
this->info.tokens.emplace_back("name",this->name);
this->info.tokens.emplace_back("piece length",this->piece_length);
this->info.tokens.emplace_back("pieces",this->pieces);
if(this->isPrivate)
this->info.tokens.emplace_back("private",1);
if(std::holds_alternative<int64_t>(this->fileListOrLength))
{
this->info.tokens.emplace_back("length",std::get<int64_t>(this->fileListOrLength));
}
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength))
{
Serialization::Bencode::BeArray a;
for(auto& item : std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength))
{
Serialization::Bencode::BeDictionary dict;
dict.tokens.emplace_back("length",item.length);
Serialization::Bencode::BeArray path;
for(auto& p : item.path.path)
path.tokens.push_back(p);
dict.tokens.emplace_back("path",path);
a.tokens.push_back(dict);
}
this->info.tokens.emplace_back("files", a);
}
return this->info;
}
Serialization::Bencode::BeDictionary& TorrentFileInfo::GetInfoDictionary()
{
return this->info;
}
void TorrentFileInfo::Load(const Serialization::Bencode::BeDictionary& dict)
{
this->info = dict;
auto o=dict.GetValue("name");
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
this->name = std::get<Serialization::Bencode::BeString>(o);
else
this->name = {};
o=dict.GetValue("piece length");
if(std::holds_alternative<int64_t>(o))
this->piece_length = std::get<int64_t>(o);
else
this->piece_length = DEFAULT_PIECE_LENGTH;
o=dict.GetValue("private");
if(std::holds_alternative<int64_t>(o))
this->isPrivate = std::get<int64_t>(o);
else
this->isPrivate = 0;
o=dict.GetValue("pieces");
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
this->pieces = std::get<Serialization::Bencode::BeString>(o);
else
this->pieces = {};
o=dict.GetValue("files");
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
{
std::vector<TorrentFileEntry> ents;
for(auto& item : std::get<Serialization::Bencode::BeArray>(o).tokens)
{
if(std::holds_alternative<Serialization::Bencode::BeDictionary>(item))
{
TorrentFileEntry fe;
fe.path.relative=true;
auto& d2=std::get<Serialization::Bencode::BeDictionary>(item);
auto o2=d2.GetValue("length");
if(std::holds_alternative<int64_t>(o2))
{
fe.length = std::get<int64_t>(o2);
}
o2=d2.GetValue("path");
if(std::holds_alternative<Serialization::Bencode::BeArray>(o2))
{
auto& arr=std::get<Serialization::Bencode::BeArray>(o2);
for(auto& itm : arr.tokens)
{
if(std::holds_alternative<Serialization::Bencode::BeString>(itm))
{
fe.path.path.push_back(std::get<Serialization::Bencode::BeString>(itm));
}
}
}
ents.push_back(fe);
}
}
this->fileListOrLength = ents;
}
else {
o = dict.GetValue("length");
if(std::holds_alternative<int64_t>(o))
{
this->fileListOrLength = std::get<int64_t>(o);
}
else {
this->fileListOrLength = (int64_t)0;
}
}
}
Serialization::Bencode::BeString TorrentFileInfo::GetInfoHash()
{
auto strm = std::make_shared<Streams::MemoryStream>(true);
Serialization::Bencode::Bencode::Save(strm,this->info);
strm->Seek(0L,Streams::SeekOrigin::Begin);
return Crypto::Sha1::ComputeHash(strm);
}
TorrentFile::TorrentFile()
{
this->created_by = "TessesFrameworkTorrent";
this->creation_date = (int64_t)time(NULL);
}
TorrentFile::TorrentFile(const Serialization::Bencode::BeDictionary& tkn)
{
auto o=tkn.GetValue("info");
if(std::holds_alternative<Serialization::Bencode::BeDictionary>(o))
{
this->info.Load(std::get<Serialization::Bencode::BeDictionary>(o));
}
o=tkn.GetValue("announce");
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
{
this->announce = std::get<Serialization::Bencode::BeString>(o);
}
o=tkn.GetValue("announce-list");
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
{
auto& ls = std::get<Serialization::Bencode::BeArray>(o);
for(auto& item : ls.tokens)
{
if(std::holds_alternative<Serialization::Bencode::BeArray>(item))
{
auto ls2 = std::get<Serialization::Bencode::BeArray>(item);
auto item2=ls2.tokens.at(0);
if(std::holds_alternative<Serialization::Bencode::BeString>(item2))
{
this->announce_list.push_back(std::get<Serialization::Bencode::BeString>(item2));
}
}
}
}
o=tkn.GetValue("creation date");
if(std::holds_alternative<int64_t>(o))
{
this->creation_date = std::get<int64_t>(o);
}
o=tkn.GetValue("comment");
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
{
this->comment = std::get<Serialization::Bencode::BeString>(o);
}
o=tkn.GetValue("created by");
if(std::holds_alternative<Serialization::Bencode::BeString>(o))
{
this->created_by = std::get<Serialization::Bencode::BeString>(o);
}
o=tkn.GetValue("url-list");
if(std::holds_alternative<Serialization::Bencode::BeArray>(o))
{
auto& li =std::get<Serialization::Bencode::BeArray>(o);
for(auto& itm : li.tokens)
{
if(std::holds_alternative<Serialization::Bencode::BeString>(itm))
this->url_list.push_back(std::get<Serialization::Bencode::BeString>(itm));
}
}
}
Serialization::Bencode::BeDictionary TorrentFile::ToDictionary()
{
Serialization::Bencode::BeDictionary dict;
dict.SetValue("info",this->info.GetInfoDictionary());
dict.SetValue("announce",this->announce);
if(!this->announce_list.empty())
{
Serialization::Bencode::BeArray a;
for(auto& item : this->announce_list)
{
Serialization::Bencode::BeArray a2;
a2.tokens.push_back(item);
a.tokens.push_back(a2);
}
dict.SetValue("announce-list",a);
}
if(!this->url_list.empty())
{
Serialization::Bencode::BeArray ls;
for(auto& itm : this->url_list)
{
ls.tokens.push_back(itm);
}
dict.SetValue("url-list",ls);
}
dict.SetValue("created by", this->created_by);
dict.SetValue("creation date", this->creation_date);
dict.SetValue("comment",this->comment);
return dict;
}
void TorrentFile::Print(std::shared_ptr<TextStreams::TextWriter> writer)
{
writer->WriteLine("Announce: " + (std::string)this->announce);
writer->WriteLine("Announce List:");
for(auto& item : this->announce_list)
{
writer->WriteLine("\t" + (std::string)item);
}
writer->WriteLine("Comment: " + (std::string)this->comment);
writer->WriteLine("Created By: " + (std::string)this->created_by);
Date::DateTime dt(this->creation_date);
dt.SetToLocal();
writer->WriteLine("Creation Date: " + dt.ToString());
writer->WriteLine("Info Hash: " + Http::HttpUtils::BytesToHex(this->info.GetInfoHash().data));
writer->WriteLine("Info:");
writer->WriteLine("\tName: " + (std::string)this->info.name);
writer->WriteLine(this->info.isPrivate ? "\tPrivate: true" : "\tPrivate: false");
writer->WriteLine("\tPiece Length: " + TF_FileSize(this->info.piece_length));
if(std::holds_alternative<int64_t>(this->info.fileListOrLength))
{
writer->WriteLine("\tIs Single File: true");
writer->WriteLine("\tFile length: " + TF_FileSize((uint64_t)std::get<int64_t>(this->info.fileListOrLength)));
}
else if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->info.fileListOrLength))
{
writer->WriteLine("\tIs Single File: false");
writer->WriteLine("\tFiles:");
auto& files = std::get<std::vector<TorrentFileEntry>>(this->info.fileListOrLength);
for(auto& file : files)
{
writer->WriteLine("\t\tPath: " + file.path.ToString());
writer->WriteLine("\t\tLength: " + TF_FileSize(file.length));
writer->WriteLine();
}
}
}
std::shared_ptr<ReadWriteAt> TorrentFileInfo::GetStreamFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path)
{
if(std::holds_alternative<std::vector<TorrentFileEntry>>(this->fileListOrLength))
{
return std::make_shared<TorrentDirectoryStream>(vfs,path / this->name, std::get<std::vector<TorrentFileEntry>>(this->fileListOrLength));
}
else if(std::holds_alternative<int64_t>(this->fileListOrLength)) {
return std::make_shared<TorrentFileStream>(vfs,path / this->name, (uint64_t)std::get<int64_t>(this->fileListOrLength));
}
return nullptr;
}
static void ParsePieces(TorrentFileInfo* fi,std::shared_ptr<ReadWriteAt> rwa, int64_t flength)
{
int64_t pieces = fi->pieces.data.size()/20;
std::vector<uint8_t> buffer;
buffer.resize((size_t)fi->piece_length);
size_t lastPieceSize = (size_t)(flength % (int64_t)buffer.size());
for(int64_t i = 0; i < pieces; i++)
{
size_t len = buffer.size();
if(i == pieces-1 && lastPieceSize != 0) len = std::min(len,lastPieceSize);
rwa->ReadBlockAt(i*buffer.size(), buffer.data(), len);
auto hsh=Crypto::Sha1::ComputeHash(buffer.data(),len);
std::copy(hsh.begin(),hsh.end(),fi->pieces.data.begin()+(i*20));
}
}
void TorrentFileInfo::CreateFromFilesystem(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength)
{
this->isPrivate=isPrivate;
this->piece_length = pieceLength;
this->pieces.data.clear();
this->name = path.GetFileName();
int64_t len=0;
if(vfs->FileExists(path))
{
auto strm = vfs->OpenFile(path,"rb");
len = strm->GetLength();
this->fileListOrLength= len;
int64_t pieces = len / piece_length;
if((len % piece_length) != 0) pieces++;
this->pieces.data.resize(pieces*20);
}
else if(vfs->DirectoryExists(path))
{
std::vector<TorrentFileEntry> ents;
std::function<void(Tesses::Framework::Filesystem::VFSPath,Tesses::Framework::Filesystem::VFSPath)> crawl;
crawl= [&](Tesses::Framework::Filesystem::VFSPath inFS, Tesses::Framework::Filesystem::VFSPath inTorrent)->void{
for(auto ent : vfs->EnumeratePaths(inFS))
{
if(vfs->FileExists(ent))
{
auto strm = vfs->OpenFile(ent,"rb");
auto flen = strm->GetLength();
ents.emplace_back(inTorrent / ent.GetFileName(),flen);
len += flen;
}
else if(vfs->DirectoryExists(ent))
{
crawl(ent,inTorrent / ent.GetFileName());
}
}
};
Tesses::Framework::Filesystem::VFSPath p2;
p2.relative=true;
crawl(path, p2);
this->fileListOrLength = ents;
int64_t pieces = len / piece_length;
if((len % piece_length) != 0) pieces++;
this->pieces.data.resize(pieces*20);
}
else {
throw std::runtime_error("File or directory does not exist");
}
ParsePieces(this,this->GetStreamFromFilesystem(vfs,path.GetParent()),len);
this->GenerateInfoDictionary();
}
void TorrentFile::CreateTorrent(std::shared_ptr<Tesses::Framework::Streams::Stream> torrent_file_stream,const std::vector<Serialization::Bencode::BeString>& trackers,const std::vector<Serialization::Bencode::BeString>& webseeds, std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, const Tesses::Framework::Filesystem::VFSPath& path, bool isPrivate, int64_t pieceLength, Serialization::Bencode::BeString comment, Serialization::Bencode::BeString created_by)
{
TorrentFile file;
file.announce = trackers.at(0);
file.announce_list = trackers;
file.url_list = webseeds;
file.comment = comment;
file.created_by = created_by;
file.info.CreateFromFilesystem(vfs,path,isPrivate,pieceLength);
auto dict=file.ToDictionary();
Serialization::Bencode::Bencode::Save(torrent_file_stream,dict);
}
}

View File

@@ -0,0 +1,122 @@
#include "TessesFramework/BitTorrent/TorrentStream.hpp"
namespace Tesses::Framework::BitTorrent
{
TorrentFileEntry::TorrentFileEntry()
{
}
TorrentFileEntry::TorrentFileEntry(Tesses::Framework::Filesystem::VFSPath path, int64_t length): path(path), length(length)
{}
ReadWriteAt::~ReadWriteAt(){}
TorrentFileStream::TorrentFileStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,uint64_t length) : vfs(vfs), path(path), length(length)
{
}
bool TorrentFileStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)
{
if(!vfs->FileExists(path)) return false;
auto strm = vfs->OpenFile(path,"rb");
strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin);
strm->ReadBlock(data,len);
return true;
}
void TorrentFileStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)
{
len = (size_t)std::min((int64_t)len, (int64_t)this->length-(int64_t)offset);
if(len == 0) return;
mtx.Lock();
auto strm = vfs->OpenFile(path,"wb");
strm->Seek((int64_t)offset,Streams::SeekOrigin::Begin);
strm->WriteBlock(data,len);
mtx.Unlock();
}
TorrentDirectoryStream::TorrentDirectoryStream(std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs, Tesses::Framework::Filesystem::VFSPath path,std::vector<TorrentFileEntry> entries) : vfs(vfs), path(path), entries(entries)
{
if(!vfs->DirectoryExists(path))
this->vfs->CreateDirectory(path);
this->mtxs.resize(entries.size());
}
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo
bool TorrentDirectoryStream::ReadBlockAt(uint64_t offset, uint8_t* data, size_t len)
{
uint64_t currentOffset = 0;
uint64_t end = offset + len;
for(size_t i = 0; i < this->entries.size(); i++)
{
if(offset < currentOffset && end < currentOffset) {
currentOffset += this->entries[i].length;
continue;
}
if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){
currentOffset += this->entries[i].length;
continue;
}
auto path = this->path / this->entries[i].path;
if(!vfs->FileExists(path)) return false;
int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset);
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length);
int flength = (int)(fend - fstart);
int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset));
auto strm = vfs->OpenFile(path,"rb");
strm->Seek(fstart, Streams::SeekOrigin::Begin);
strm->ReadBlock(data+bstart,flength);
currentOffset += this->entries[i].length;
}
return true;
}
// From https://www.seanjoflynn.com/research/bittorrent.html , which is licensed under MIT based on code repo
void TorrentDirectoryStream::WriteBlockAt(uint64_t offset, const uint8_t* data, size_t len)
{
uint64_t currentOffset = 0;
uint64_t end = offset + len;
for(size_t i = 0; i < this->entries.size(); i++)
{
if(offset < currentOffset && end < currentOffset) {
currentOffset += this->entries[i].length;
continue;
}
if(offset > currentOffset + this->entries[i].length && end > currentOffset + this->entries[i].length ){
currentOffset += this->entries[i].length;
continue;
}
auto path = this->path / this->entries[i].path;
auto parent = path.GetParent();
if(!vfs->DirectoryExists(parent))
vfs->CreateDirectory(parent);
int64_t fstart = std::max((int64_t)0,(int64_t)offset - (int64_t)currentOffset);
int64_t fend = std::min((int64_t)end - (int64_t)currentOffset, (int64_t)this->entries[i].length);
int flength = (int)(fend - fstart);
int bstart = std::max((int)0,(int)((int64_t)currentOffset - (int64_t)offset));
this->mtxs[i].Lock();
{
auto strm = vfs->OpenFile(path,"wb");
strm->Seek(fstart, Streams::SeekOrigin::Begin);
strm->WriteBlock(data+bstart,flength);
}
this->mtxs[i].Unlock();
currentOffset += this->entries[i].length;
}
}
}

View File

@@ -230,7 +230,27 @@ namespace Tesses::Framework::Filesystem
#endif
}
std::shared_ptr<LocalFilesystem> LocalFS;
void LocalFilesystem::Lock(VFSPath path)
{
auto p2 = VFSPathToSystem(path);
const char* fopenPath = p2.c_str();
while(true)
{
FILE* fp = fopen(fopenPath,"wx");
if(fp) {
fclose(fp);
break;
}
}
}
void LocalFilesystem::Unlock(VFSPath path)
{
std::error_code error;
std::filesystem::remove(VFSPathToSystem(path),error);
}
std::shared_ptr<LocalFilesystem> LocalFS = std::make_shared<LocalFilesystem>();
}
// C:/Users/Jim/Joel

View File

@@ -311,6 +311,34 @@ namespace Tesses::Framework::Filesystem
if(vfs != nullptr)
vfs->DeleteFile(destPath);
}
void MountableFilesystem::Lock(VFSPath path)
{
path = path.CollapseRelativeParents();
VFSPath destRoot;
VFSPath destPath = path;
std::shared_ptr<VFS> vfs = root;
GetFS(path, destRoot, destPath, vfs);
if(vfs != nullptr)
vfs->Lock(destPath);
}
void MountableFilesystem::Unlock(VFSPath path)
{
path = path.CollapseRelativeParents();
VFSPath destRoot;
VFSPath destPath = path;
std::shared_ptr<VFS> vfs = root;
GetFS(path, destRoot, destPath, vfs);
if(vfs != nullptr)
vfs->Unlock(destPath);
}
void MountableFilesystem::GetDate(VFSPath path, Date::DateTime& lastWrite, Date::DateTime& lastAccess)
{

View File

@@ -87,6 +87,15 @@ namespace Tesses::Framework::Filesystem
{
this->parent->DeleteFile(ToParent(path));
}
void SubdirFilesystem::Lock(VFSPath path)
{
this->parent->Lock(ToParent(path));
}
void SubdirFilesystem::Unlock(VFSPath path)
{
this->parent->Unlock(ToParent(path));
}
void SubdirFilesystem::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
{
this->parent->CreateSymlink(ToParent(existingFile),ToParent(symlinkFile));

View File

@@ -109,6 +109,16 @@ namespace Tesses::Framework::Filesystem {
if(this->vfs == nullptr) return;
this->vfs->DeleteFile(path);
}
void TempFS::Lock(VFSPath path)
{
if(this->vfs == nullptr) return;
this->vfs->Lock(path);
}
void TempFS::Unlock(VFSPath path)
{
if(this->vfs == nullptr) return;
this->vfs->Unlock(path);
}
void TempFS::CreateSymlink(VFSPath existingFile, VFSPath symlinkFile)
{
if(this->vfs == nullptr) return;
@@ -202,4 +212,5 @@ namespace Tesses::Framework::Filesystem {
{
Close();
}
}

View File

@@ -519,5 +519,13 @@ namespace Tesses::Framework::Filesystem
}
void VFS::Close() {
}
void VFS::Lock(VFSPath path)
{
}
void VFS::Unlock(VFSPath path)
{
}
}

View File

@@ -104,7 +104,7 @@ namespace Tesses::Framework::Http {
auto stout = p.GetStdoutStream();
Tesses::Framework::TextStreams::StreamReader reader(stout);
std::string line;
while(reader.ReadLine(line))
while(reader.ReadLineHttp(line))
{
auto v = HttpUtils::SplitString(line,": ", 2);
if(v.size() == 2)

View File

@@ -159,7 +159,7 @@ namespace Tesses::Framework::Http
this->handleStrm=nullptr;
StreamReader reader(strm);
std::string statusLine;
if(!reader.ReadLine(statusLine)) return;
if(!reader.ReadLineHttp(statusLine)) return;
auto statusLinesPart = HttpUtils::SplitString(statusLine," ",3);
if(statusLinesPart.size() >= 2)
{
@@ -167,7 +167,7 @@ namespace Tesses::Framework::Http
this->statusCode = (StatusCode)std::stoi(statusLinesPart[1]);
}
std::string line;
while(reader.ReadLine(line))
while(reader.ReadLineHttp(line))
{
if(line.empty()) break;
auto v = HttpUtils::SplitString(line,": ", 2);
@@ -225,7 +225,7 @@ namespace Tesses::Framework::Http
StreamReader reader(strm);
std::string statusLine;
if(!reader.ReadLine(statusLine)) break;
if(!reader.ReadLineHttp(statusLine)) break;
auto statusLinesPart = HttpUtils::SplitString(statusLine," ",3);
if(statusLinesPart.size() >= 2)
{
@@ -233,7 +233,7 @@ namespace Tesses::Framework::Http
this->statusCode = (StatusCode)std::stoi(statusLinesPart[1]);
}
std::string line;
while(reader.ReadLine(line))
while(reader.ReadLineHttp(line))
{
if(line.empty()) break;
auto v = HttpUtils::SplitString(line,": ", 2);

View File

@@ -468,7 +468,7 @@ namespace Tesses::Framework::Http
HttpDictionary req;
StreamReader reader(ctx->GetStream());
std::string line;
while(reader.ReadLine(line))
while(reader.ReadLineHttp(line))
{
auto v = HttpUtils::SplitString(line,": ", 2);
if(v.size() == 2)
@@ -944,7 +944,7 @@ namespace Tesses::Framework::Http
try{
bool firstLine = true;
std::string line;
while(reader.ReadLine(line))
while(reader.ReadLineHttp(line))
{
if(firstLine)
{

View File

@@ -965,5 +965,43 @@ namespace Tesses::Framework::Http {
if(!TryGetFirst(key,val)) return false;
return val == "true" || val == "on";
}
std::string HttpUtils::BytesToHex(const std::vector<uint8_t>& data)
{
std::string text;
BytesToHex(text,data);
return text;
}
void HttpUtils::BytesToHex(std::string& text,const std::vector<uint8_t>& data)
{
if(data.empty()) {
text.clear();
return;
}
text.resize(data.size()*2);
for(size_t i = 0; i < data.size(); i++)
{
text[i*2] = NibbleToHex(data[i] >> 4);
text[i*2+1] += NibbleToHex(data[i]);
}
}
std::vector<uint8_t> HttpUtils::HexToBytes(const std::string& text)
{
std::vector<uint8_t> data;
HexToBytes(data,text);
return data;
}
void HttpUtils::HexToBytes(std::vector<uint8_t>& data,const std::string& text)
{
if(text.empty()) { data.clear(); return;}
if(text.size() % 2 != 0) throw std::runtime_error("Text length is not even");
data.resize(text.size()/2);
for(size_t i = 0; i < text.size(); i+=2)
{
uint8_t b = HexToNibble(text[i]) << 4;
b |= HexToNibble(text[i+1]);
data[i/2] = b;
}
}
}

31
src/Random.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "TessesFramework/Random.hpp"
namespace Tesses::Framework {
Random::Random() : Random((uint64_t)time(NULL))
{
}
Random::Random(uint64_t seed) : num(seed)
{
}
uint32_t Random::Next(uint32_t max)
{
return (uint32_t)Next(0,(int32_t)max);
}
int32_t Random::Next(int32_t min, int32_t max)
{
uint32_t number = (uint32_t)(Next() >> 31);
int32_t range = max-min;
return (uint32_t)((((double)number / (double)0xFFFFFFFF) * (double)range)+min);
}
uint64_t Random::Next()
{
return num = 6364136223846793005 * num + 1;
}
uint8_t Random::NextByte()
{
return (uint8_t)Next(0,256);
}
}

View File

@@ -0,0 +1,253 @@
#include "TessesFramework/Serialization/Bencode.hpp"
namespace Tesses::Framework::Serialization::Bencode {
BeToken BeDictionary::GetValue(BeString key) const
{
for(auto item : this->tokens)
if(item.first == key) return item.second;
return BeUndefined();
}
void BeDictionary::SetValue(BeString key, BeToken value)
{
if(std::holds_alternative<BeUndefined>(value))
{
for(auto idx = this->tokens.begin(); idx != this->tokens.end(); idx++)
{
if(idx->first == key) {
this->tokens.erase(idx);
break;
}
}
}
else
{
for(auto& item : this->tokens)
{
if(item.first == key)
{
item.second = value;
return;
}
}
this->tokens.emplace_back(key,value);
}
}
BeString::BeString()
{
}
BeString::BeString(const std::string& text)
{
this->data.insert(this->data.end(),text.cbegin(),text.cend());
}
BeString::BeString(const char* text)
{
size_t len = strlen(text);
this->data.insert(this->data.end(),text,text+len);
}
BeString::BeString(const std::vector<uint8_t>& data) : data(data)
{
}
BeString::operator std::string() const
{
return std::string(data.cbegin(),data.cend());
}
bool operator==(const BeString& lStr, const BeString& rStr)
{
return lStr.data == rStr.data;
}
bool operator==(const BeString& lStr, const std::string& rStr)
{
if(lStr.data.size() != rStr.size()) return false;
return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr.cbegin());
}
bool operator==(const std::string& lStr, const BeString& rStr)
{
if(lStr.size() != rStr.data.size()) return false;
return std::equal(lStr.cbegin(),lStr.cend(),rStr.data.cbegin());
}
bool operator==(const BeString& lStr, const char* rStr)
{
size_t len = strlen(rStr);
if(lStr.data.size() != len) return false;
return std::equal(lStr.data.cbegin(),lStr.data.cend(),rStr);
}
bool operator==(const char* lStr, const BeString& rStr)
{
size_t len = strlen(lStr);
if(rStr.data.size() != len) return false;
return std::equal(lStr,lStr+len,rStr.data.cbegin());
}
bool operator!=(const BeString& lStr, const BeString& rStr)
{
return !(lStr == rStr);
}
bool operator!=(const BeString& lStr, const std::string& rStr)
{
return !(lStr == rStr);
}
bool operator!=(const std::string& lStr, const BeString& rStr)
{
return !(lStr == rStr);
}
bool operator!=(const BeString& lStr, const char* rStr)
{
return !(lStr == rStr);
}
bool operator!=(const char* lStr, const BeString& rStr)
{
return !(lStr == rStr);
}
void Bencode::Save(std::shared_ptr<Tesses::Framework::Streams::Stream> strm,const BeToken& value)
{
if(std::holds_alternative<BeArray>(value))
{
auto& array = std::get<BeArray>(value);
strm->WriteByte((uint8_t)'l');
for(auto& item : array.tokens)
{
Save(strm,item);
}
strm->WriteByte((uint8_t)'e');
}
else if(std::holds_alternative<BeDictionary>(value))
{
auto& dict = std::get<BeDictionary>(value);
strm->WriteByte((uint8_t)'d');
for(auto& item : dict.tokens)
{
Save(strm,item.first);
Save(strm,item.second);
}
strm->WriteByte((uint8_t)'e');
}
else if(std::holds_alternative<BeString>(value))
{
auto& str = std::get<BeString>(value);
std::string prefix = std::to_string(str.data.size()) + ":";
strm->WriteBlock((const uint8_t*)prefix.data(),prefix.size());
strm->WriteBlock(str.data.data(),str.data.size());
}
else if(std::holds_alternative<int64_t>(value))
{
int64_t val = std::get<int64_t>(value);
std::string str = "i" + std::to_string(val) + "e";
strm->WriteBlock((const uint8_t*)str.data(),str.size());
}
}
BeToken Bencode::Load(std::shared_ptr<Tesses::Framework::Streams::Stream> strm)
{
auto chr = strm->ReadByte();
switch(chr)
{
case 'i':
{
std::string no;
while(true) {
chr = strm->ReadByte();
if(chr == -1) throw std::out_of_range("End of file");
if(chr == 'e') break;
no.push_back((char)chr);
}
return std::stoll(no);
}
break;
case 'd':
{
BeDictionary dict;
while(true)
{
auto key = Load(strm);
if(std::holds_alternative<BeUndefined>(key)) break;
if(!std::holds_alternative<BeString>(key)) throw std::runtime_error("Key must be a string");
auto value = Load(strm);
if(std::holds_alternative<BeUndefined>(key)) throw std::runtime_error("Incomplete dictionary entry");
dict.tokens.emplace_back(std::get<BeString>(key),value);
}
return dict;
}
break;
case 'l':
{
BeArray array;
while(true)
{
auto tkn = Load(strm);
if(std::holds_alternative<BeUndefined>(tkn)) break;
array.tokens.push_back(tkn);
}
return array;
}
break;
case 'e':
return BeUndefined();
case -1:
throw std::out_of_range("End of file");
default:
{
std::string no({(char)chr});
while(true) {
chr = strm->ReadByte();
if(chr == -1) throw std::out_of_range("End of file");
if(chr == ':') break;
no.push_back((char)chr);
}
auto len = std::stoll(no);
if(len < 0) throw std::out_of_range("Less than zero byte string");
BeString str;
str.data.resize((size_t)len);
size_t result = strm->ReadBlock(str.data.data(),str.data.size());
if(result != str.data.size() || result != (size_t)len) throw std::out_of_range("Didn't read entire string");
return str;
}
break;
}
}
Json::JToken Bencode::ToJson(const BeToken& tkn)
{
if(std::holds_alternative<BeDictionary>(tkn))
{
auto& dict = std::get<BeDictionary>(tkn);
Json::JObject o;
for(auto& itm : dict.tokens)
{
o.SetValue(itm.first,ToJson(itm.second));
}
return o;
}
if(std::holds_alternative<BeArray>(tkn))
{
auto& array = std::get<BeArray>(tkn);
Json::JArray a;
for(auto& itm : array.tokens)
{
a.Add(ToJson(itm));
}
return a;
}
if(std::holds_alternative<BeString>(tkn))
{
return (std::string)std::get<BeString>(tkn);
}
if(std::holds_alternative<int64_t>(tkn))
{
return std::get<int64_t>(tkn);
}
return Json::JUndefined();
}
void Bencode::Print(std::shared_ptr<Tesses::Framework::TextStreams::TextWriter> writer, BeToken tkn)
{
writer->WriteLine(Json::Json::Encode(ToJson(tkn),true));
}
}

View File

@@ -15,6 +15,7 @@ using HttpUtils = Tesses::Framework::Http::HttpUtils;
#include <network.h>
#define NETWORK_GETSOCKNAME net_getsockname
#define NETWORK_RECV net_recv
#define NETWORK_POLL net_poll
#define sockaddr_storage sockaddr_in
#error "Not supported yet"
#else
@@ -46,6 +47,7 @@ extern "C" {
#if defined(AF_UNIX) && !defined(GEKKO) && !defined(__SWITCH__) && !defined(__PS2__)
#include <sys/un.h>
#endif
#include <sys/poll.h>
}
#endif
#if defined(GEKKO)
@@ -68,10 +70,13 @@ extern "C" uint32_t if_config( char *local_ip, char *netmask, char *gateway,bool
#define NETWORK_GETADDRINFO getaddrinfo
#define NETWORK_FREEADDRINFO freeaddrinfo
#define NETWORK_GETSOCKNAME getsockname
#if defined(_WIN32)
#define NETWORK_CLOSE closesocket
#define NETWORK_POLL WSAPoll
#else
#define NETWORK_CLOSE close
#define NETWORK_POLL poll
#endif
#endif
@@ -288,6 +293,28 @@ namespace Tesses::Framework::Streams {
uint32_t addr;
uint8_t addr_parts[4];
} my_addr_t;
bool NetworkStream::DataAvailable(int timeout)
{
pollfd fd;
fd.events = POLLIN;
fd.fd = this->sock;
int res= NETWORK_POLL(&fd,1,timeout);
if (res == -1) {
if(errno == EINTR) return false;
throw std::runtime_error("poll() failed");
}
else if(res == 0) return false;
else if(fd.revents & POLLIN)
return true;
else if(fd.revents & (POLLERR | POLLNVAL))
{
this->endOfStream=true;
return false;
}
return false;
}
NetworkStream::NetworkStream(std::string unixPath,bool isServer)
{
this->endOfStream=false;

View File

@@ -182,7 +182,6 @@ namespace Tesses::Framework
}
void TF_Init()
{
Tesses::Framework::Filesystem::LocalFS = std::make_shared<Tesses::Framework::Filesystem::LocalFilesystem>();
#if defined(TESSESFRAMEWORK_ENABLE_SQLITE)
sqlite3_initialize();
#if defined(GEKKO) || defined(__SWITCH__) || defined(__PS2__)
@@ -303,4 +302,41 @@ if (iResult != 0) {
#endif
}
#endif
static std::string TF_FileSizeBin(uint64_t bytes)
{
if(bytes == 1) return "1 Byte";
if(bytes < 1024ULL) return std::to_string(bytes) + " Bytes";
if(bytes < 1024ULL*1024ULL) return std::to_string(bytes / 1024ULL) + " kiB";
if(bytes < 1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL)) + " MiB";
if(bytes < 1024ULL*1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL*1024ULL)) + " GiB";
if(bytes < 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL) return std::to_string(bytes / (1024ULL*1024ULL*1024ULL*1024ULL)) + " TiB";
return std::to_string(bytes / (1024ULL*1024ULL*1024ULL*1024ULL*1024ULL)) + " PiB";
}
static std::string TF_FileSizeDec(uint64_t bytes)
{
if(bytes == 1) return "1 Byte";
if(bytes < 1000ULL) return std::to_string(bytes) + " Bytes";
if(bytes < 1000ULL*1000ULL) return std::to_string(bytes / 1000ULL) + " kB";
if(bytes < 1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL)) + " MB";
if(bytes < 1000ULL*1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL*1000ULL)) + " GB";
if(bytes < 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) return std::to_string(bytes / (1000ULL*1000ULL*1000ULL*1000ULL)) + " TB";
return std::to_string(bytes / (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL)) + " PB";
}
std::string TF_FileSize(uint64_t bytes, bool usesBin)
{
return usesBin ? TF_FileSizeBin(bytes) : TF_FileSizeDec(bytes);
}
std::optional<std::string> _argv0=std::nullopt;
void TF_AllowPortable(std::string argv0)
{
_argv0 = argv0;
}
std::optional<std::string> TF_GetCommandName()
{
return _argv0;
}
}

View File

@@ -10,7 +10,7 @@ namespace Tesses::Framework::TextStreams
{
std::string txt;
this->ReadBlock(txt,1);
if(txt.empty()) return -1;
if(txt.empty()) { eof=true; return -1;}
return (uint8_t)txt[0];
}
std::string TextReader::ReadLine()
@@ -19,13 +19,14 @@ namespace Tesses::Framework::TextStreams
ReadLine(str);
return str;
}
bool TextReader::ReadLine(std::string& str)
bool TextReader::ReadLineHttp(std::string& str)
{
if(eof) return false;
bool ret = false;
int32_t r = -1;
do {
r = ReadChar();
if(r == -1) break;
if(r == -1) {break;}
if(r == '\r') continue;
if(r == '\n') break;
str.push_back((char)(uint8_t)r);
@@ -33,8 +34,25 @@ namespace Tesses::Framework::TextStreams
} while(r != -1);
return ret;
}
bool TextReader::ReadLine(std::string& str)
{
if(eof) return false;
bool ret = false;
int32_t r = -1;
do {
r = ReadChar();
if(r == -1) break;
if(r == '\r') continue;
if(r == '\n') return true;
str.push_back((char)(uint8_t)r);
ret = true;
} while(r != -1);
return ret;
}
void TextReader::ReadAllLines(std::vector<std::string>& lines)
{
if(eof) return;
int32_t r = -1;
std::string builder;
do {
@@ -60,10 +78,14 @@ namespace Tesses::Framework::TextStreams
void TextReader::ReadToEnd(std::string& str)
{
if(eof) return;
while(ReadBlock(str,1024));
}
void TextReader::CopyTo(TextWriter& writer, size_t buffSz)
{
if(eof) return;
std::string str = {};
while(ReadBlock(str,buffSz))
{