Skip to content

Commit

Permalink
TgBot++: socket: Make uploadfile contain in-out paths
Browse files Browse the repository at this point in the history
  • Loading branch information
Royna2544 committed Jun 12, 2024
1 parent 4921b1e commit 69b3983
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 65 deletions.
35 changes: 19 additions & 16 deletions src/socket/TgBotSocketClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,18 @@ std::string_view AckTypeToStr(callback::AckType type) {

} // namespace

// TODO: WTF is this
FileDataHelper::DataFromFileParam p;

struct ClientParser : TgBotSocketParser {
explicit ClientParser(SocketInterfaceBase* interface)
: TgBotSocketParser(interface) {}
void handle_CommandPacket(SocketConnContext context, Packet pkt) override {
using callback::AckType;
using callback::GenericAck;

callback::GetUptimeCallback callbackData = {};
callback::GenericAck result{};
std::string resultText;

switch (pkt.header.cmd) {
case Command::CMD_GET_UPTIME_CALLBACK: {
callback::GetUptimeCallback callbackData = {};
pkt.data.assignTo(callbackData);
LOG(INFO) << "Server replied: " << callbackData.uptime.data();
break;
Expand All @@ -109,31 +105,39 @@ struct ClientParser : TgBotSocketParser {
break;
}
case Command::CMD_UPLOAD_FILE_DRY_CALLBACK: {
pkt.data.assignTo(result);
callback::UploadFileDryCallback callbackData{};
pkt.data.assignTo(callbackData);
LOG(INFO) << "Response from server: "
<< AckTypeToStr(result.result);
if (result.result != AckType::SUCCESS) {
LOG(ERROR) << "Reason: " << result.error_msg.data();
<< AckTypeToStr(callbackData.result);
if (callbackData.result != AckType::SUCCESS) {
LOG(ERROR) << "Reason: " << callbackData.error_msg.data();
} else {
interface->closeSocketHandle(context);
context = interface->createClientSocket().value();
LOG(INFO) << "Recreated client socket";
auto params_in = callbackData.requestdata;
FileDataHelper::DataFromFileParam param;
param.destfilepath = params_in.destfilepath.data();
param.filepath = params_in.srcfilepath.data();
param.options = params_in.options;
auto newPkt = FileDataHelper::DataFromFile<
FileDataHelper::UPLOAD_FILE>(p);
FileDataHelper::UPLOAD_FILE>(param);
LOG(INFO) << "Sending the actual file content again...";
interface->writeToSocket(context, newPkt->toSocketData());
onNewBuffer(context);
}
break;
}
case Command::CMD_GENERIC_ACK:
pkt.data.assignTo(result);
case Command::CMD_GENERIC_ACK: {
callback::GenericAck callbackData{};
pkt.data.assignTo(callbackData);
LOG(INFO) << "Response from server: "
<< AckTypeToStr(result.result);
if (result.result != AckType::SUCCESS) {
LOG(ERROR) << "Reason: " << result.error_msg.data();
<< AckTypeToStr(callbackData.result);
if (callbackData.result != AckType::SUCCESS) {
LOG(ERROR) << "Reason: " << callbackData.error_msg.data();
}
break;
}
default:
LOG(ERROR) << "Unhandled callback of command: "
<< static_cast<int>(pkt.header.cmd);
Expand Down Expand Up @@ -246,7 +250,6 @@ int main(int argc, char** argv) {
params.options.overwrite = true;
pkt = FileDataHelper::DataFromFile<FileDataHelper::UPLOAD_FILE_DRY>(
params);
p = params;
break;
}
case Command::CMD_DOWNLOAD_FILE: {
Expand Down
55 changes: 36 additions & 19 deletions src/socket/include/TgBotSocket_Export.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ struct TGSOCKET_ATTR_PACKED PacketHeader {
// 3: Uploadfile has a sha256sum check, std::array conversions
// 4: Move CMD_UPLOAD_FILE_DRY to internal namespace
// 5: Use the packed attribute for structs
constexpr static int DATA_VERSION = 5;
// 6: Make CMD_UPLOAD_FILE_DRY_CALLBACK return sperate callback, and add srcpath to UploadFile
constexpr static int DATA_VERSION = 6;
constexpr static int64_t MAGIC_VALUE = MAGIC_VALUE_BASE + DATA_VERSION;

int64_t magic = MAGIC_VALUE; ///< Magic value to verify the packet
Expand Down Expand Up @@ -125,6 +126,10 @@ struct Packet {
}
};

using PathStringArray = std::array<char, MAX_PATH_SIZE>;
using MessageStringArray = std::array<char, MAX_MSG_SIZE>;
using SHA256StringArray = std::array<unsigned char, SHA256_DIGEST_LENGTH>;

namespace data {

enum class FileType {
Expand All @@ -146,7 +151,7 @@ enum class CtrlSpamBlock {

struct TGSOCKET_ATTR_PACKED WriteMsgToChatId {
ChatId chat; // destination chatid
std::array<char, MAX_MSG_SIZE> message; // Msg to send
MessageStringArray message; // Msg to send
};

struct TGSOCKET_ATTR_PACKED ObserveChatId {
Expand All @@ -158,7 +163,7 @@ struct TGSOCKET_ATTR_PACKED ObserveChatId {
struct TGSOCKET_ATTR_PACKED SendFileToChatId {
ChatId chat; // Destination ChatId
FileType fileType; // File type for file
std::array<char, MAX_PATH_SIZE> filePath; // Path to file (local)
PathStringArray filePath; // Path to file (local)
};

struct TGSOCKET_ATTR_PACKED ObserveAllChats {
Expand All @@ -170,28 +175,35 @@ struct TGSOCKET_ATTR_PACKED DeleteControllerById {
int controller_id;
};

struct TGSOCKET_ATTR_PACKED UploadFile {
std::array<char, MAX_PATH_SIZE> filepath{}; // Destination file name
std::array<unsigned char, SHA256_DIGEST_LENGTH>
sha256_hash{}; // SHA256 hash of the file
struct TGSOCKET_ATTR_PACKED UploadFileDry {
PathStringArray destfilepath{}; // Destination file name
PathStringArray srcfilepath{}; // Source file name (This is not used on the remote, used if dry=true)
SHA256StringArray sha256_hash{}; // SHA256 hash of the file

// Returns AckType::ERROR_COMMAND_IGNORED on options failure
struct TGSOCKET_ATTR_PACKED Options {
bool overwrite = false; // Overwrite existing file if exists
bool hash_ignore =
false; // Ignore hash, always upload file even if the same file
// exists and the hash matches. Depends on overwrite flag
// for actual overwriting
bool dry_run =
false; // If true, just check the hashes and return result.
// Overwrite existing file if exists
bool overwrite = false;

// Ignore hash, always upload file even if the same file
// exists and the hash matches. Depends on overwrite flag
// for actual overwriting
bool hash_ignore = false;

// If true, just check the hashes and return result.
bool dry_run = false;
} options;
};

struct TGSOCKET_ATTR_PACKED UploadFile : public UploadFileDry {
using Options = UploadFileDry::Options;
uint8_t buf[]; // Buffer
};

struct TGSOCKET_ATTR_PACKED DownloadFile {
std::array<char, MAX_PATH_SIZE> filepath{}; // Path to file (in remote)
std::array<char, MAX_PATH_SIZE> destfilename{}; // Destination file name
uint8_t buf[]; // Buffer
PathStringArray filepath{}; // Path to file (in remote)
PathStringArray destfilename{}; // Destination file name
uint8_t buf[]; // Buffer
};
} // namespace data

Expand All @@ -213,7 +225,7 @@ enum class AckType {
struct TGSOCKET_ATTR_PACKED GenericAck {
AckType result; // result type
// Error message, only valid when result type is not SUCCESS
std::array<char, MAX_MSG_SIZE> error_msg{};
MessageStringArray error_msg{};

// Create a new instance of the Generic Ack, error.
explicit GenericAck(AckType result, const std::string& errorMsg)
Expand All @@ -227,6 +239,10 @@ struct TGSOCKET_ATTR_PACKED GenericAck {
static GenericAck ok() { return GenericAck(AckType::SUCCESS, "OK"); }
};

struct TGSOCKET_ATTR_PACKED UploadFileDryCallback : public GenericAck {
data::UploadFileDry requestdata;
};

} // namespace callback
} // namespace TgBotSocket

Expand All @@ -242,14 +258,15 @@ ASSERT_SIZE(ObserveChatId, 9);
ASSERT_SIZE(SendFileToChatId, 268);
ASSERT_SIZE(ObserveAllChats, 1);
ASSERT_SIZE(DeleteControllerById, 4);
ASSERT_SIZE(UploadFile, 291);
ASSERT_SIZE(UploadFile, 547);
ASSERT_SIZE(DownloadFile, 512);
ASSERT_SIZE(PacketHeader, 24);
} // namespace TgBotSocket::data

namespace TgBotSocket::callback {
ASSERT_SIZE(GetUptimeCallback, 21);
ASSERT_SIZE(GenericAck, 260);
ASSERT_SIZE(UploadFileDryCallback, 807);
} // namespace TgBotSocket::callback

#undef ASSERT_SIZE
Expand Down
45 changes: 24 additions & 21 deletions src/socket/interface/impl/bot/SocketDataHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,25 +196,29 @@ GenericAck SocketInterfaceTgBot::handle_DeleteControllerById(const void* ptr) {
}

GenericAck SocketInterfaceTgBot::handle_UploadFile(
const void* ptr, TgBotSocket::PacketHeader::length_type len, bool dry) {
const void* ptr, TgBotSocket::PacketHeader::length_type len) {
if (!FileDataHelper::DataToFile<FileDataHelper::UPLOAD_FILE>(ptr, len)) {
return GenericAck(AckType::ERROR_RUNTIME_ERROR, "Failed to write file");
}
return GenericAck::ok();
}

UploadFileDryCallback SocketInterfaceTgBot::handle_UploadFileDry(
const void* ptr, TgBotSocket::PacketHeader::length_type len) {
bool ret = false;
const auto f = static_cast<const UploadFileDry *>(ptr);
UploadFileDryCallback callback;
callback.requestdata = *f;

if (dry) {
ret = FileDataHelper::DataToFile<FileDataHelper::UPLOAD_FILE_DRY>(ptr,
len);
if (!ret) {
return GenericAck(AckType::ERROR_COMMAND_IGNORED,
"Options verification failed");
}
ret = FileDataHelper::DataToFile<FileDataHelper::UPLOAD_FILE_DRY>(ptr, len);
if (!ret) {
copyTo(callback.error_msg, "Options verification failed");
callback.result = AckType::ERROR_COMMAND_IGNORED;
} else {
ret = FileDataHelper::DataToFile<FileDataHelper::UPLOAD_FILE>(ptr, len);
if (!ret) {
return GenericAck(AckType::ERROR_RUNTIME_ERROR,
"Failed to write file");
}
copyTo(callback.error_msg, "OK");
callback.result = AckType::SUCCESS;
}

return GenericAck::ok();
return callback;
}

bool SocketInterfaceTgBot::handle_DownloadFile(SocketConnContext ctx,
Expand Down Expand Up @@ -252,7 +256,7 @@ bool SocketInterfaceTgBot::handle_GetUptime(SocketConnContext ctx,
void SocketInterfaceTgBot::handle_CommandPacket(SocketConnContext ctx,
TgBotSocket::Packet pkt) {
const void* ptr = pkt.data.get();
std::variant<GenericAck, bool> ret;
std::variant<GenericAck, UploadFileDryCallback, bool> ret;

switch (pkt.header.cmd) {
case Command::CMD_WRITE_MSG_TO_CHAT_ID:
Expand All @@ -277,10 +281,10 @@ void SocketInterfaceTgBot::handle_CommandPacket(SocketConnContext ctx,
ret = handle_GetUptime(ctx, ptr);
break;
case Command::CMD_UPLOAD_FILE:
ret = handle_UploadFile(ptr, pkt.header.data_size, false);
ret = handle_UploadFile(ptr, pkt.header.data_size);
break;
case Command::CMD_UPLOAD_FILE_DRY:
ret = handle_UploadFile(ptr, pkt.header.data_size, true);
ret = handle_UploadFileDry(ptr, pkt.header.data_size);
break;
case Command::CMD_DOWNLOAD_FILE:
ret = handle_DownloadFile(ctx, ptr);
Expand All @@ -305,10 +309,9 @@ void SocketInterfaceTgBot::handle_CommandPacket(SocketConnContext ctx,
break;
}
case Command::CMD_UPLOAD_FILE_DRY: {
// Need a special cmd here
GenericAck result = std::get<GenericAck>(ret);
const auto result = std::get<UploadFileDryCallback>(ret);
Packet ackpkt(Command::CMD_UPLOAD_FILE_DRY_CALLBACK, &result,
sizeof(GenericAck));
sizeof(UploadFileDryCallback));
LOG(INFO) << "Sending CMD_UPLOAD_FILE_DRY ack: " << std::boolalpha
<< (result.result == AckType::SUCCESS);
interface->writeToSocket(ctx, ackpkt.toSocketData());
Expand Down
25 changes: 17 additions & 8 deletions src/socket/interface/impl/bot/TgBotSocketFileHelper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <openssl/sha.h>

#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <ios>
Expand All @@ -18,6 +19,10 @@ template <size_t size>
inline void copyTo(std::array<char, size>& arr_in, const char* buf) {
strncpy(arr_in.data(), buf, size);
}
template <size_t size, size_t size2>
inline void copyTo(std::array<char, size>& arr_in, std::array<char, size2> buf) {
copyTo(arr_in, buf.data());
}

namespace FileDataHelper {
using len_t = TgBotSocket::PacketHeader::length_type;
Expand Down Expand Up @@ -81,6 +86,7 @@ std::optional<TgBotSocket::Packet> DataFromFile<DOWNLOAD_FILE>(
// Implementation starts
using TgBotSocket::data::DownloadFile;
using TgBotSocket::data::UploadFile;
using TgBotSocket::data::UploadFileDry;
struct HashContainer {
std::array<unsigned char, SHA256_DIGEST_LENGTH> m_data;
};
Expand Down Expand Up @@ -151,8 +157,8 @@ inline std::ostream& operator<<(std::ostream& self, const HashContainer& data) {
template <>
bool FileDataHelper::DataToFile<FileDataHelper::UPLOAD_FILE_DRY>(
const void* ptr, len_t len) {
const auto* data = static_cast<const UploadFile*>(ptr);
const char* filename = data->filepath.data();
const auto* data = static_cast<const UploadFileDry*>(ptr);
const char* filename = data->destfilepath.data();
std::error_code errc;
bool exists = false;

Expand Down Expand Up @@ -202,7 +208,7 @@ bool FileDataHelper::DataToFile<FileDataHelper::UPLOAD_FILE>(const void* ptr,
len_t len) {
const auto* data = static_cast<const UploadFile*>(ptr);

return writeFileCommon(data->filepath.data(), &data->buf[0],
return writeFileCommon(data->destfilepath.data(), &data->buf[0],
len - sizeof(UploadFile));
}

Expand Down Expand Up @@ -231,7 +237,7 @@ FileDataHelper::DataFromFile<FileDataHelper::UPLOAD_FILE>(
// The front bytes of the buffer is UploadFile, hence cast it
auto* uploadFile = static_cast<UploadFile*>(resultPointer.get());
// Copy destination file name info to the buffer
copyTo(uploadFile->filepath, params.destfilepath.string().c_str());
copyTo(uploadFile->destfilepath, params.destfilepath.string().c_str());
// Copy source file data to the buffer
memcpy(&uploadFile->buf[0], result->data.get(), result->size);
// Calculate SHA256 hash
Expand Down Expand Up @@ -261,11 +267,14 @@ FileDataHelper::DataFromFile<FileDataHelper::UPLOAD_FILE_DRY>(
return std::nullopt;
}
// Create result packet buffer
auto resultPointer = createMemory(sizeof(UploadFile));
auto resultPointer = createMemory(sizeof(UploadFileDry));
// The front bytes of the buffer is UploadFile, hence cast it
auto* uploadFile = static_cast<UploadFile*>(resultPointer.get());
auto* uploadFile = static_cast<UploadFileDry*>(resultPointer.get());
// Copy destination file name info to the buffer
copyTo(uploadFile->filepath, params.destfilepath.string().c_str());
copyTo(uploadFile->destfilepath, params.destfilepath.string().c_str());
// Copy source file name to the buffer
copyTo(uploadFile->srcfilepath, params.filepath.string().c_str());

// Calculate SHA256 hash
SHA256(static_cast<unsigned char*>(result->data.get()), result->size,
hash.m_data.data());
Expand All @@ -277,7 +286,7 @@ FileDataHelper::DataFromFile<FileDataHelper::UPLOAD_FILE_DRY>(
uploadFile->options.dry_run = true;

return TgBotSocket::Packet{TgBotSocket::Command::CMD_UPLOAD_FILE_DRY,
resultPointer.get(), sizeof(UploadFile)};
resultPointer.get(), sizeof(UploadFileDry)};
}

// Server ->
Expand Down
6 changes: 5 additions & 1 deletion src/socket/interface/impl/bot/TgBotSocketInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#include <utility>

#include "TgBotPacketParser.hpp"
#include "TgBotSocket_Export.hpp"

using TgBotSocket::callback::GenericAck;
using TgBotSocket::callback::UploadFileDryCallback;

#ifdef WINDOWS_BUILD
#include "impl/SocketWindows.hpp"
Expand Down Expand Up @@ -59,7 +61,9 @@ struct SocketInterfaceTgBot : SingleThreadCtrlRunnable,
static GenericAck handle_ObserveAllChats(const void* ptr);
static GenericAck handle_DeleteControllerById(const void* ptr);
static GenericAck handle_UploadFile(
const void* ptr, TgBotSocket::PacketHeader::length_type len, bool dry);
const void* ptr, TgBotSocket::PacketHeader::length_type len);
static UploadFileDryCallback handle_UploadFileDry(
const void* ptr, TgBotSocket::PacketHeader::length_type len);

// These have their own ack handlers
bool handle_GetUptime(SocketConnContext ctx, const void* ptr);
Expand Down

0 comments on commit 69b3983

Please sign in to comment.