File transfer WIP

This commit is contained in:
Perttu Ahola 2014-09-18 23:47:05 +03:00
parent ab83f7e5ca
commit 5f66d2eeb6
20 changed files with 599 additions and 32 deletions

8
3rdparty/smallsha1/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 2.6)
project(smallsha1)
set(SRCS
smallsha1.cpp
)
add_library(smallsha1 STATIC ${SRCS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x -g -O0 -Wall")

185
3rdparty/smallsha1/smallsha1.cpp vendored Normal file
View File

@ -0,0 +1,185 @@
/*
Copyright (c) 2011, Micael Hildenborg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Micael Hildenborg nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Contributors:
Gustav
Several members in the gamedev.se forum.
Gregory Petrosyan
*/
#include "smallsha1.h"
namespace smallsha1
{
namespace // local
{
// Rotate an integer value to left.
inline const unsigned int rol(const unsigned int value,
const unsigned int steps)
{
return ((value << steps) | (value >> (32 - steps)));
}
// Sets the first 16 integers in the buffert to zero.
// Used for clearing the W buffert.
inline void clearWBuffert(unsigned int* buffert)
{
for (int pos = 16; --pos >= 0;)
{
buffert[pos] = 0;
}
}
void innerHash(unsigned int* result, unsigned int* w)
{
unsigned int a = result[0];
unsigned int b = result[1];
unsigned int c = result[2];
unsigned int d = result[3];
unsigned int e = result[4];
int round = 0;
#define sha1macro(func,val) \
{ \
const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
e = d; \
d = c; \
c = rol(b, 30); \
b = a; \
a = t; \
}
while (round < 16)
{
sha1macro((b & c) | (~b & d), 0x5a827999)
++round;
}
while (round < 20)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro((b & c) | (~b & d), 0x5a827999)
++round;
}
while (round < 40)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro(b ^ c ^ d, 0x6ed9eba1)
++round;
}
while (round < 60)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
++round;
}
while (round < 80)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro(b ^ c ^ d, 0xca62c1d6)
++round;
}
#undef sha1macro
result[0] += a;
result[1] += b;
result[2] += c;
result[3] += d;
result[4] += e;
}
} // namespace
void calc(const void* src, const int bytelength, unsigned char* hash)
{
// Init the result array.
unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
// Cast the void src pointer to be the byte array we can work with.
const unsigned char* sarray = (const unsigned char*) src;
// The reusable round buffer
unsigned int w[80];
// Loop through all complete 64byte blocks.
const int endOfFullBlocks = bytelength - 64;
int endCurrentBlock;
int currentBlock = 0;
while (currentBlock <= endOfFullBlocks)
{
endCurrentBlock = currentBlock + 64;
// Init the round buffer with the 64 byte block data.
for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
{
// This line will swap endian on big endian and keep endian on little endian.
w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
| (((unsigned int) sarray[currentBlock + 2]) << 8)
| (((unsigned int) sarray[currentBlock + 1]) << 16)
| (((unsigned int) sarray[currentBlock]) << 24);
}
innerHash(result, w);
}
// Handle the last and not full 64 byte block if existing.
endCurrentBlock = bytelength - currentBlock;
clearWBuffert(w);
int lastBlockBytes = 0;
for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes)
{
w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
}
w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
if (endCurrentBlock >= 56)
{
innerHash(result, w);
clearWBuffert(w);
}
w[15] = bytelength << 3;
innerHash(result, w);
// Store hash in result pointer, and make sure we get in in the correct order on both endian models.
for (int hashByte = 20; --hashByte >= 0;)
{
hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
}
}
void toHexString(const unsigned char* hash, char* hexstring)
{
const char hexDigits[] = { "0123456789abcdef" };
for (int hashByte = 20; --hashByte >= 0;)
{
hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf];
hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf];
}
hexstring[40] = 0;
}
} // namespace sha1

49
3rdparty/smallsha1/smallsha1.h vendored Normal file
View File

@ -0,0 +1,49 @@
/*
Copyright (c) 2011, Micael Hildenborg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Micael Hildenborg nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SHA1_DEFINED
#define SHA1_DEFINED
namespace smallsha1
{
/**
@param src points to any kind of data to be hashed.
@param bytelength the number of bytes to hash from the src pointer.
@param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in.
*/
void calc(const void* src, const int bytelength, unsigned char* hash);
/**
@param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function.
@param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string.
*/
void toHexString(const unsigned char* hash, char* hexstring);
} // namespace sha1
#endif // SHA1_DEFINED

View File

@ -7,6 +7,12 @@ project(buildat)
add_subdirectory("3rdparty/cereal") add_subdirectory("3rdparty/cereal")
add_subdirectory("3rdparty/c55lib") add_subdirectory("3rdparty/c55lib")
add_subdirectory("3rdparty/smallsha1")
include_directories("src")
include_directories("3rdparty/cereal/include")
include_directories("3rdparty/c55lib")
include_directories("3rdparty/smallsha1")
# #
# Polycode # Polycode
@ -45,10 +51,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
# Security / crash protection # Security / crash protection
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all")
include_directories("src")
include_directories("3rdparty/c55lib")
include_directories("3rdparty/cereal/include")
# Client # Client
set(CLIENT_EXE_NAME buildat_client) set(CLIENT_EXE_NAME buildat_client)
set(CLIENT_SRCS set(CLIENT_SRCS
@ -57,11 +59,13 @@ set(CLIENT_SRCS
src/client/app.cpp src/client/app.cpp
src/core/log.cpp src/core/log.cpp
src/impl/tcpsocket.cpp src/impl/tcpsocket.cpp
src/impl/sha1.cpp
) )
add_executable(${CLIENT_EXE_NAME} ${CLIENT_SRCS}) add_executable(${CLIENT_EXE_NAME} ${CLIENT_SRCS})
TARGET_LINK_LIBRARIES(${CLIENT_EXE_NAME} TARGET_LINK_LIBRARIES(${CLIENT_EXE_NAME}
${POLYCODE_DEPENDENCY_LIBS} ${POLYCODE_DEPENDENCY_LIBS}
c55lib c55lib
smallsha1
) )
# Server # Server
@ -76,10 +80,12 @@ set(SERVER_SRCS
src/impl/tcpsocket.cpp src/impl/tcpsocket.cpp
src/impl/module.cpp src/impl/module.cpp
src/impl/linux/file_watch.cpp src/impl/linux/file_watch.cpp
src/impl/sha1.cpp
) )
add_executable(${SERVER_EXE_NAME} ${SERVER_SRCS}) add_executable(${SERVER_EXE_NAME} ${SERVER_SRCS})
TARGET_LINK_LIBRARIES(${SERVER_EXE_NAME} TARGET_LINK_LIBRARIES(${SERVER_EXE_NAME}
c55lib c55lib
smallsha1
dl dl
) )
add_definitions() add_definitions()

View File

@ -0,0 +1,12 @@
-- Buildat: client_lua/boot.lua
local log = buildat:Logger("client_lua")
log:info("boot.lua loaded")
-- Temporary test
require "Polycode/Core"
scene = Scene(Scene.SCENE_2D)
scene:getActiveCamera():setOrthoSize(640, 480)
label = SceneLabel("Hello from remote module!", 32)
label:setPosition(0, -100, 0)
scene:addChild(label)

View File

@ -0,0 +1,140 @@
#include "core/log.h"
#include "interface/module.h"
#include "interface/server.h"
#include "interface/event.h"
#include "interface/fs.h"
#include "interface/sha1.h"
#include "client_file/include/api.h"
#include "network/include/api.h"
#include <cereal/archives/binary.hpp>
#include <cereal/types/string.hpp>
#include <fstream>
#include <streambuf>
using interface::Event;
struct FileInfo {
ss_ name;
ss_ content;
ss_ hash;
FileInfo(const ss_ &name, const ss_ &content, const ss_ &hash):
name(name), content(content), hash(hash){}
};
namespace client_file {
struct Module: public interface::Module, public client_file::Interface
{
interface::Server *m_server;
sm_<ss_, sp_<FileInfo>> m_files;
Module(interface::Server *server):
m_server(server),
interface::Module("client_file")
{
log_v(MODULE, "client_file construct");
}
~Module()
{
log_v(MODULE, "client_file destruct");
}
void init()
{
log_v(MODULE, "client_file init");
m_server->sub_event(this, Event::t("core:start"));
m_server->sub_event(this, Event::t("network:new_client"));
}
void event(const Event::Type &type, const Event::Private *p)
{
EVENT_VOIDN("core:start", on_start)
EVENT_TYPEN("network:new_client", on_new_client, network::NewClient)
}
void on_start()
{
}
void on_new_client(const network::NewClient &new_client)
{
log_i(MODULE, "client_file::on_new_client: id=%zu", new_client.info.id);
// Tell file hashes to client
for(auto &pair : m_files){
const FileInfo &info = *pair.second.get();
std::ostringstream os(std::ios::binary);
{
cereal::BinaryOutputArchive ar(os);
ar(info.name);
ar(info.hash);
}
network::access(m_server, [&](network::Interface * inetwork){
inetwork->send(new_client.info.id, "core:announce_file", os.str());
});
}
m_server->emit_event(ss_()+"client_file:files_sent",
new FilesSent(new_client.info.id));
#if 0
sv_<ss_> module_names = m_server->get_loaded_modules();
for(const ss_ &module_name : module_names){
ss_ module_path = m_server->get_module_path(module_name);
ss_ client_file_path = module_path+"/client_file";
auto list = interface::getGlobalFilesystem()->list_directory(client_file_path);
sv_<ss_> log_list;
for(const interface::Filesystem::Node &n : list){
if(n.is_directory)
continue;
log_list.push_back(n.name);
}
log_i(MODULE, "client_file: %s: %s", cs(module_name), cs(dump(log_list)));
for(const interface::Filesystem::Node &n : list){
if(n.is_directory)
continue;
std::ifstream f(client_file_path+"/"+n.name);
std::string file_content((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
std::ostringstream os(std::ios::binary);
{
cereal::BinaryOutputArchive ar(os);
ar(n.name);
ar(file_content);
}
network::access(m_server, [&](network::Interface * inetwork){
inetwork->send(new_client.info.id, "core:cache_file", os.str());
});
}
m_server->emit_event(ss_()+"client_file:files_sent:"+module_name,
new FilesSent(new_client.info.id));
}
#endif
}
// Interface
void add_file_content(const ss_ &name, const ss_ &content)
{
ss_ hash = interface::sha1::calculate(content);
log_v(MODULE, "File: %s: %s", cs(name),
cs(interface::sha1::hex(hash)));
m_files[name] = up_<FileInfo>(new FileInfo(name, content, hash));
}
void* get_interface()
{
return dynamic_cast<Interface*>(this);
}
};
extern "C" {
EXPORT void* createModule_client_file(interface::Server *server){
return (void*)(new Module(server));
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "interface/event.h"
#include "network/include/api.h"
namespace client_file
{
struct FilesSent: public interface::Event::Private
{
network::PeerInfo::Id recipient;
FilesSent(const network::PeerInfo::Id &recipient): recipient(recipient){}
};
struct Interface
{
virtual void add_file_content(const ss_ &name, const ss_ &content) = 0;
};
inline bool access(interface::Server *server,
std::function<void(client_file::Interface*)> cb)
{
return server->access_module("client_file", [&](interface::Module * module){
cb((client_file::Interface*)module->check_interface());
});
}
}

View File

@ -3,6 +3,7 @@
#include "interface/server.h" #include "interface/server.h"
#include "interface/event.h" #include "interface/event.h"
#include "interface/fs.h" #include "interface/fs.h"
#include "client_file/include/api.h"
#include "client_lua/include/api.h" #include "client_lua/include/api.h"
#include "network/include/api.h" #include "network/include/api.h"
#include <cereal/archives/binary.hpp> #include <cereal/archives/binary.hpp>
@ -34,12 +35,18 @@ struct Module: public interface::Module
{ {
log_v(MODULE, "client_lua init"); log_v(MODULE, "client_lua init");
m_server->sub_event(this, Event::t("core:start")); m_server->sub_event(this, Event::t("core:start"));
m_server->sub_event(this, Event::t("core:module_loaded"));
m_server->sub_event(this, Event::t("core:module_unloaded"));
m_server->sub_event(this, Event::t("network:new_client")); m_server->sub_event(this, Event::t("network:new_client"));
} }
void event(const Event::Type &type, const Event::Private *p) void event(const Event::Type &type, const Event::Private *p)
{ {
EVENT_VOIDN("core:start", on_start) EVENT_VOIDN("core:start", on_start)
EVENT_TYPEN("core:module_loaded", on_module_loaded,
interface::ModuleLoadedEvent)
EVENT_TYPEN("core:module_unloaded", on_module_unloaded,
interface::ModuleUnloadedEvent)
EVENT_TYPEN("network:new_client", on_new_client, network::NewClient) EVENT_TYPEN("network:new_client", on_new_client, network::NewClient)
} }
@ -47,12 +54,51 @@ struct Module: public interface::Module
{ {
} }
void on_module_loaded(const interface::ModuleLoadedEvent &event)
{
log_v(MODULE, "on_module_loaded(): %s", cs(event.name));
ss_ module_name = event.name;
ss_ module_path = m_server->get_module_path(module_name);
ss_ client_lua_path = module_path+"/client_lua";
auto list = interface::getGlobalFilesystem()
->list_directory(client_lua_path);
sv_<ss_> log_list;
for(const interface::Filesystem::Node &n : list){
if(n.is_directory)
continue;
log_list.push_back(n.name);
}
log_i(MODULE, "client_lua: %s: %s", cs(module_name), cs(dump(log_list)));
for(const interface::Filesystem::Node &n : list){
if(n.is_directory)
continue;
const ss_ &file_name = n.name;
std::ifstream f(client_lua_path+"/"+file_name);
std::string file_content((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
client_file::access(m_server, [&](client_file::Interface * i){
i->add_file_content(file_name, file_content);
});
}
}
void on_module_unloaded(const interface::ModuleUnloadedEvent &event)
{
log_v(MODULE, "on_module_unloaded(): %s", cs(event.name));
// TODO
}
void on_new_client(const network::NewClient &new_client) void on_new_client(const network::NewClient &new_client)
{ {
log_i(MODULE, "client_lua::on_new_client: id=%zu", new_client.info.id); log_i(MODULE, "client_lua::on_new_client: id=%zu", new_client.info.id);
ss_ module_path = m_server->get_module_path(MODULE); ss_ module_path = m_server->get_module_path(MODULE);
#if 0
// Not sure what this is useful for
std::ifstream f(module_path+"/boot.lua"); std::ifstream f(module_path+"/boot.lua");
std::string script_content((std::istreambuf_iterator<char>(f)), std::string script_content((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
@ -60,13 +106,15 @@ struct Module: public interface::Module
network::access(m_server, [&](network::Interface * inetwork){ network::access(m_server, [&](network::Interface * inetwork){
inetwork->send(new_client.info.id, "core:run_script", script_content); inetwork->send(new_client.info.id, "core:run_script", script_content);
}); });
#endif
#if 0
sv_<ss_> module_names = m_server->get_loaded_modules(); sv_<ss_> module_names = m_server->get_loaded_modules();
for(const ss_ &module_name : module_names){ for(const ss_ &module_name : module_names){
ss_ module_path = m_server->get_module_path(module_name); ss_ module_path = m_server->get_module_path(module_name);
ss_ client_lua_path = module_path+"/client_lua"; ss_ client_lua_path = module_path+"/client_lua";
auto list = interface::getGlobalFilesystem()->list_directory(client_lua_path); auto list = interface::getGlobalFilesystem()
->list_directory(client_lua_path);
sv_<ss_> log_list; sv_<ss_> log_list;
for(const interface::Filesystem::Node &n : list){ for(const interface::Filesystem::Node &n : list){
@ -96,6 +144,7 @@ struct Module: public interface::Module
m_server->emit_event(ss_()+"client_lua:files_sent:"+module_name, m_server->emit_event(ss_()+"client_lua:files_sent:"+module_name,
new FilesSent(new_client.info.id)); new FilesSent(new_client.info.id));
} }
#endif
} }
}; };

View File

@ -1,15 +1,6 @@
#pragma once #pragma once
#include "interface/event.h" #include "interface/event.h"
#include "network/include/api.h"
namespace client_lua namespace client_lua
{ {
struct FilesSent: public interface::Event::Private
{
network::PeerInfo::Id recipient;
FilesSent(const network::PeerInfo::Id &recipient): recipient(recipient){}
};
} }

View File

@ -95,11 +95,6 @@ struct Module: public interface::Module, public network::Interface
EVENT_TYPEN("network:incoming_data", on_incoming_data, interface::SocketEvent) EVENT_TYPEN("network:incoming_data", on_incoming_data, interface::SocketEvent)
} }
void* get_interface()
{
return dynamic_cast<Interface*>(this);
}
void on_start() void on_start()
{ {
ss_ address = "any4"; ss_ address = "any4";
@ -257,6 +252,11 @@ struct Module: public interface::Module, public network::Interface
{ {
send(recipient, m_packet_types.get(name), data); send(recipient, m_packet_types.get(name), data);
} }
void* get_interface()
{
return dynamic_cast<Interface*>(this);
}
}; };
extern "C" { extern "C" {

View File

@ -8,5 +8,6 @@ namespace client
ss_ server_address; ss_ server_address;
ss_ polycode_path = "/home/celeron55/softat/polycode/"; ss_ polycode_path = "/home/celeron55/softat/polycode/";
ss_ share_path = "../share"; ss_ share_path = "../share";
ss_ cache_path = "../cache";
}; };
} }

View File

@ -47,13 +47,14 @@ int main(int argc, char *argv[])
client::Config &config = g_client_config; client::Config &config = g_client_config;
const char opts[100] = "hs:p:P:"; const char opts[100] = "hs:p:P:C:";
const char usagefmt[1000] = const char usagefmt[1000] =
"Usage: %s [OPTION]...\n" "Usage: %s [OPTION]...\n"
" -h Show this help\n" " -h Show this help\n"
" -s [address] Specify server address\n" " -s [address] Specify server address\n"
" -p [polycode_path] Specify polycode path\n" " -p [polycode_path] Specify polycode path\n"
" -P [share_path] Specify share/ path\n" " -P [share_path] Specify share/ path\n"
" -C [cache_path] Specify cache/ path\n"
; ;
int c; int c;
@ -76,6 +77,10 @@ int main(int argc, char *argv[])
fprintf(stderr, "INFO: config.share_path: %s\n", c55_optarg); fprintf(stderr, "INFO: config.share_path: %s\n", c55_optarg);
config.share_path = c55_optarg; config.share_path = c55_optarg;
break; break;
case 'C':
fprintf(stderr, "INFO: config.cache_path: %s\n", c55_optarg);
config.cache_path = c55_optarg;
break;
default: default:
fprintf(stderr, "ERROR: Invalid command-line argument\n"); fprintf(stderr, "ERROR: Invalid command-line argument\n");
fprintf(stderr, usagefmt, argv[0]); fprintf(stderr, usagefmt, argv[0]);

View File

@ -1,14 +1,18 @@
#include "core/log.h" #include "core/log.h"
#include "client/state.h" #include "client/state.h"
#include "client/app.h" #include "client/app.h"
#include "client/config.h"
#include "interface/tcpsocket.h" #include "interface/tcpsocket.h"
//#include <cereal/archives/binary.hpp> #include <cereal/archives/binary.hpp>
//#include <cereal/types/string.hpp> #include <cereal/types/string.hpp>
#include <cstring> #include <cstring>
#include <deque> #include <deque>
#include <fstream>
#include <sys/socket.h> #include <sys/socket.h>
#define MODULE "__state" #define MODULE "__state"
extern client::Config g_client_config;
namespace client { namespace client {
struct ClientPacketTypeRegistry struct ClientPacketTypeRegistry
@ -79,10 +83,10 @@ struct CState: public State
if(r == -1) if(r == -1)
throw Exception(ss_()+"Receive failed: "+strerror(errno)); throw Exception(ss_()+"Receive failed: "+strerror(errno));
if(r == 0){ if(r == 0){
log_i(MODULE, "Peer disconnected"); log_w(MODULE, "Peer disconnected");
return; return;
} }
log_i(MODULE, "Received %zu bytes", r); log_d(MODULE, "Received %zu bytes", r);
m_socket_buffer.insert(m_socket_buffer.end(), buf, buf + r); m_socket_buffer.insert(m_socket_buffer.end(), buf, buf + r);
} }
@ -99,10 +103,10 @@ struct CState: public State
(m_socket_buffer[3] & 0xff)<<8 | (m_socket_buffer[3] & 0xff)<<8 |
(m_socket_buffer[4] & 0xff)<<16 | (m_socket_buffer[4] & 0xff)<<16 |
(m_socket_buffer[5] & 0xff)<<24; (m_socket_buffer[5] & 0xff)<<24;
log_i(MODULE, "size=%zu", size); log_d(MODULE, "size=%zu", size);
if(m_socket_buffer.size() < 6 + size) if(m_socket_buffer.size() < 6 + size)
return; return;
log_i(MODULE, "Received full packet; type=%zu, length=6+%zu", log_v(MODULE, "Received full packet; type=%zu, length=6+%zu",
type, size); type, size);
ss_ data(m_socket_buffer.begin() + 6, m_socket_buffer.begin() + 6 + size); ss_ data(m_socket_buffer.begin() + 6, m_socket_buffer.begin() + 6 + size);
m_socket_buffer.erase(m_socket_buffer.begin(), m_socket_buffer.erase(m_socket_buffer.begin(),
@ -145,6 +149,38 @@ struct CState: public State
m_app->run_script(data); m_app->run_script(data);
return; return;
} }
if(packet_name == "core:announce_file"){
ss_ file_name;
ss_ file_hash;
std::istringstream is(data, std::ios::binary);
{
cereal::BinaryInputArchive ar(is);
ar(file_name);
ar(file_hash);
}
// TODO: Check if we already have this file
ss_ path = g_client_config.cache_path+"/remote/"+file_hash;
std::ifstream is(path, std::ios::binary);
if(is.good()){
// We have it; no need to ask this file
} else {
// We don't have it; request this file
}
}
if(packet_name == "core:transfer_file"){
ss_ file_name;
ss_ file_content;
std::istringstream is(data, std::ios::binary);
{
cereal::BinaryInputArchive ar(is);
ar(file_name);
ar(file_content);
}
// TODO: Check filename for malicious characters "/.\"\\"
ss_ path = g_client_config.cache_path+"/remote/"+file_name;
std::ofstream of(path, std::ios::binary);
of<<file_content;
}
} }
}; };

24
src/impl/sha1.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "core/types.h"
#include <smallsha1.h>
namespace interface {
namespace sha1 {
ss_ calculate(const ss_ &data)
{
unsigned char result[20];
smallsha1::calc(data.c_str(), data.size(), result);
return ss_((char*)result, 20);
}
ss_ hex(const ss_ &raw)
{
if(raw.size() != 20)
throw Exception("interface::sha1::hex() only accepts raw.size()=20");
char result[41];
smallsha1::toHexString((const unsigned char*)raw.c_str(), result);
return ss_(result, 40);
}
}
}

View File

@ -24,6 +24,16 @@ namespace interface
name(name), path(path){} name(name), path(path){}
}; };
struct ModuleLoadedEvent: public interface::Event::Private {
ss_ name;
ModuleLoadedEvent(const ss_ &name): name(name){}
};
struct ModuleUnloadedEvent: public interface::Event::Private {
ss_ name;
ModuleUnloadedEvent(const ss_ &name): name(name){}
};
struct Server struct Server
{ {
virtual ~Server(){} virtual ~Server(){}

11
src/interface/sha1.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "core/types.h"
namespace interface
{
namespace sha1
{
ss_ calculate(const ss_ &data);
ss_ hex(const ss_ &raw);
}
}

View File

@ -127,6 +127,9 @@ struct CState: public State, public interface::Server
interface::MutexScope ms2(mc.mutex); interface::MutexScope ms2(mc.mutex);
mc.module->init(); mc.module->init();
} }
emit_event(Event("core:module_loaded",
new interface::ModuleLoadedEvent(module_name)));
} }
void load_modules(const ss_ &path) void load_modules(const ss_ &path)
@ -207,6 +210,9 @@ struct CState: public State, public interface::Server
delete mc->module; delete mc->module;
m_modules.erase(module_name); m_modules.erase(module_name);
m_compiler->unload(module_name); m_compiler->unload(module_name);
emit_event(Event("core:module_unloaded",
new interface::ModuleUnloadedEvent(module_name)));
} }
ss_ get_modules_path() ss_ get_modules_path()

View File

@ -42,6 +42,7 @@ struct Module: public interface::Module
{ {
ss_ builtin = m_server->get_builtin_modules_path(); ss_ builtin = m_server->get_builtin_modules_path();
m_server->load_module("network", builtin+"/network"); m_server->load_module("network", builtin+"/network");
m_server->load_module("client_file", builtin+"/client_file");
m_server->load_module("client_lua", builtin+"/client_lua"); m_server->load_module("client_lua", builtin+"/client_lua");
sv_<ss_> load_list = { sv_<ss_> load_list = {

View File

@ -2,7 +2,8 @@
#include "interface/server.h" #include "interface/server.h"
#include "interface/event.h" #include "interface/event.h"
#include "test1/include/api.h" #include "test1/include/api.h"
#include "client_lua/include/api.h" //#include "client_lua/include/api.h"
#include "client_file/include/api.h"
#include "network/include/api.h" #include "network/include/api.h"
#include "core/log.h" #include "core/log.h"
@ -35,7 +36,7 @@ struct Module: public interface::Module
m_server->sub_event(this, Event::t("core:start")); m_server->sub_event(this, Event::t("core:start"));
m_server->sub_event(this, m_EventType_test1_thing); m_server->sub_event(this, m_EventType_test1_thing);
m_server->sub_event(this, Event::t("network:new_client")); m_server->sub_event(this, Event::t("network:new_client"));
m_server->sub_event(this, Event::t("client_lua:files_sent:test1")); m_server->sub_event(this, Event::t("client_file:files_sent"));
m_server->sub_event(this, Event::t("network:packet_received")); m_server->sub_event(this, Event::t("network:packet_received"));
} }
@ -44,8 +45,8 @@ struct Module: public interface::Module
EVENT_VOIDN("core:start", on_start) EVENT_VOIDN("core:start", on_start)
EVENT_TYPE(m_EventType_test1_thing, on_thing, Thing) EVENT_TYPE(m_EventType_test1_thing, on_thing, Thing)
EVENT_TYPEN("network:new_client", on_new_client, network::NewClient) EVENT_TYPEN("network:new_client", on_new_client, network::NewClient)
EVENT_TYPEN("client_lua:files_sent:test1", on_lua_files_sent, EVENT_TYPEN("client_file:files_sent", on_files_sent,
client_lua::FilesSent) client_file::FilesSent)
EVENT_TYPEN("network:packet_received", on_packet_received, network::Packet) EVENT_TYPEN("network:packet_received", on_packet_received, network::Packet)
} }
@ -76,8 +77,9 @@ struct Module: public interface::Module
}); });
} }
void on_lua_files_sent(const client_lua::FilesSent &event) void on_files_sent(const client_file::FilesSent &event)
{ {
log_v(MODULE, "on_files_sent(): recipient=%zu", event.recipient);
network::access(m_server, [&](network::Interface * inetwork){ network::access(m_server, [&](network::Interface * inetwork){
inetwork->send(event.recipient, "core:run_script", inetwork->send(event.recipient, "core:run_script",
"print(\"TODO: Run init.lua\")"); "print(\"TODO: Run init.lua\")");

View File

@ -9,3 +9,8 @@ Buildat TODO
- Modules should be run in threads. - Modules should be run in threads.
- Network should leave socket fds in m_server when destructing, and pick them up - Network should leave socket fds in m_server when destructing, and pick them up
on core:continue. on core:continue.
- Move module/include/api.h to module/api.h
Buildat TODO later
==================
- Watch dependencies of module source and client_lua/*.lua in addition to main file