diff --git a/3rdparty/smallsha1/CMakeLists.txt b/3rdparty/smallsha1/CMakeLists.txt new file mode 100644 index 0000000..db27c33 --- /dev/null +++ b/3rdparty/smallsha1/CMakeLists.txt @@ -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") diff --git a/3rdparty/smallsha1/smallsha1.cpp b/3rdparty/smallsha1/smallsha1.cpp new file mode 100644 index 0000000..b562d26 --- /dev/null +++ b/3rdparty/smallsha1/smallsha1.cpp @@ -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 diff --git a/3rdparty/smallsha1/smallsha1.h b/3rdparty/smallsha1/smallsha1.h new file mode 100644 index 0000000..c25f379 --- /dev/null +++ b/3rdparty/smallsha1/smallsha1.h @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 265fa29..6cb1642 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,12 @@ project(buildat) add_subdirectory("3rdparty/cereal") 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 @@ -45,10 +51,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") # Security / crash protection #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-all") -include_directories("src") -include_directories("3rdparty/c55lib") -include_directories("3rdparty/cereal/include") - # Client set(CLIENT_EXE_NAME buildat_client) set(CLIENT_SRCS @@ -57,11 +59,13 @@ set(CLIENT_SRCS src/client/app.cpp src/core/log.cpp src/impl/tcpsocket.cpp + src/impl/sha1.cpp ) add_executable(${CLIENT_EXE_NAME} ${CLIENT_SRCS}) TARGET_LINK_LIBRARIES(${CLIENT_EXE_NAME} ${POLYCODE_DEPENDENCY_LIBS} c55lib + smallsha1 ) # Server @@ -76,10 +80,12 @@ set(SERVER_SRCS src/impl/tcpsocket.cpp src/impl/module.cpp src/impl/linux/file_watch.cpp + src/impl/sha1.cpp ) add_executable(${SERVER_EXE_NAME} ${SERVER_SRCS}) TARGET_LINK_LIBRARIES(${SERVER_EXE_NAME} c55lib + smallsha1 dl ) add_definitions() diff --git a/share/builtin/client_file/boot.lua b/share/builtin/client_file/boot.lua new file mode 100644 index 0000000..34b2495 --- /dev/null +++ b/share/builtin/client_file/boot.lua @@ -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) + diff --git a/share/builtin/client_file/client_file.cpp b/share/builtin/client_file/client_file.cpp new file mode 100644 index 0000000..d3b0803 --- /dev/null +++ b/share/builtin/client_file/client_file.cpp @@ -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 +#include +#include +#include + +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_> 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_ 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_ 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(f)), + std::istreambuf_iterator()); + + 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_(new FileInfo(name, content, hash)); + } + + void* get_interface() + { + return dynamic_cast(this); + } +}; + +extern "C" { + EXPORT void* createModule_client_file(interface::Server *server){ + return (void*)(new Module(server)); + } +} +} diff --git a/share/builtin/client_file/include/api.h b/share/builtin/client_file/include/api.h new file mode 100644 index 0000000..8f7b209 --- /dev/null +++ b/share/builtin/client_file/include/api.h @@ -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 cb) + { + return server->access_module("client_file", [&](interface::Module * module){ + cb((client_file::Interface*)module->check_interface()); + }); + } +} diff --git a/share/builtin/client_lua/client_lua.cpp b/share/builtin/client_lua/client_lua.cpp index d36af30..000f023 100644 --- a/share/builtin/client_lua/client_lua.cpp +++ b/share/builtin/client_lua/client_lua.cpp @@ -3,6 +3,7 @@ #include "interface/server.h" #include "interface/event.h" #include "interface/fs.h" +#include "client_file/include/api.h" #include "client_lua/include/api.h" #include "network/include/api.h" #include @@ -34,12 +35,18 @@ struct Module: public interface::Module { log_v(MODULE, "client_lua init"); 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")); } void event(const Event::Type &type, const Event::Private *p) { 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) } @@ -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_ 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(f)), + std::istreambuf_iterator()); + + 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) { log_i(MODULE, "client_lua::on_new_client: id=%zu", new_client.info.id); 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::string script_content((std::istreambuf_iterator(f)), std::istreambuf_iterator()); @@ -60,13 +106,15 @@ struct Module: public interface::Module network::access(m_server, [&](network::Interface * inetwork){ inetwork->send(new_client.info.id, "core:run_script", script_content); }); - +#endif +#if 0 sv_ 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_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_ log_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, new FilesSent(new_client.info.id)); } +#endif } }; diff --git a/share/builtin/client_lua/include/api.h b/share/builtin/client_lua/include/api.h index 70149a4..f63e71b 100644 --- a/share/builtin/client_lua/include/api.h +++ b/share/builtin/client_lua/include/api.h @@ -1,15 +1,6 @@ #pragma once #include "interface/event.h" -#include "network/include/api.h" namespace client_lua { - struct FilesSent: public interface::Event::Private - { - network::PeerInfo::Id recipient; - - FilesSent(const network::PeerInfo::Id &recipient): recipient(recipient){} - }; } - - diff --git a/share/builtin/network/network.cpp b/share/builtin/network/network.cpp index dd30cdf..a546915 100644 --- a/share/builtin/network/network.cpp +++ b/share/builtin/network/network.cpp @@ -95,11 +95,6 @@ struct Module: public interface::Module, public network::Interface EVENT_TYPEN("network:incoming_data", on_incoming_data, interface::SocketEvent) } - void* get_interface() - { - return dynamic_cast(this); - } - void on_start() { ss_ address = "any4"; @@ -257,6 +252,11 @@ struct Module: public interface::Module, public network::Interface { send(recipient, m_packet_types.get(name), data); } + + void* get_interface() + { + return dynamic_cast(this); + } }; extern "C" { diff --git a/src/client/config.h b/src/client/config.h index 4c92feb..10648ac 100644 --- a/src/client/config.h +++ b/src/client/config.h @@ -8,5 +8,6 @@ namespace client ss_ server_address; ss_ polycode_path = "/home/celeron55/softat/polycode/"; ss_ share_path = "../share"; + ss_ cache_path = "../cache"; }; } diff --git a/src/client/main.cpp b/src/client/main.cpp index 1e171be..93abaa2 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -47,13 +47,14 @@ int main(int argc, char *argv[]) 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] = "Usage: %s [OPTION]...\n" " -h Show this help\n" " -s [address] Specify server address\n" " -p [polycode_path] Specify polycode path\n" " -P [share_path] Specify share/ path\n" + " -C [cache_path] Specify cache/ path\n" ; int c; @@ -76,6 +77,10 @@ int main(int argc, char *argv[]) fprintf(stderr, "INFO: config.share_path: %s\n", c55_optarg); config.share_path = c55_optarg; break; + case 'C': + fprintf(stderr, "INFO: config.cache_path: %s\n", c55_optarg); + config.cache_path = c55_optarg; + break; default: fprintf(stderr, "ERROR: Invalid command-line argument\n"); fprintf(stderr, usagefmt, argv[0]); diff --git a/src/client/state.cpp b/src/client/state.cpp index 85d3625..b4d77f0 100644 --- a/src/client/state.cpp +++ b/src/client/state.cpp @@ -1,14 +1,18 @@ #include "core/log.h" #include "client/state.h" #include "client/app.h" +#include "client/config.h" #include "interface/tcpsocket.h" -//#include -//#include +#include +#include #include #include +#include #include #define MODULE "__state" +extern client::Config g_client_config; + namespace client { struct ClientPacketTypeRegistry @@ -79,10 +83,10 @@ struct CState: public State if(r == -1) throw Exception(ss_()+"Receive failed: "+strerror(errno)); if(r == 0){ - log_i(MODULE, "Peer disconnected"); + log_w(MODULE, "Peer disconnected"); 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); } @@ -99,10 +103,10 @@ struct CState: public State (m_socket_buffer[3] & 0xff)<<8 | (m_socket_buffer[4] & 0xff)<<16 | (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) 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); ss_ data(m_socket_buffer.begin() + 6, m_socket_buffer.begin() + 6 + size); m_socket_buffer.erase(m_socket_buffer.begin(), @@ -145,6 +149,38 @@ struct CState: public State m_app->run_script(data); 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< + +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); +} + +} +} diff --git a/src/interface/server.h b/src/interface/server.h index 27c4bc8..2ff5362 100644 --- a/src/interface/server.h +++ b/src/interface/server.h @@ -24,6 +24,16 @@ namespace interface 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 { virtual ~Server(){} diff --git a/src/interface/sha1.h b/src/interface/sha1.h new file mode 100644 index 0000000..abb31e2 --- /dev/null +++ b/src/interface/sha1.h @@ -0,0 +1,11 @@ +#pragma once +#include "core/types.h" + +namespace interface +{ + namespace sha1 + { + ss_ calculate(const ss_ &data); + ss_ hex(const ss_ &raw); + } +} diff --git a/src/server/state.cpp b/src/server/state.cpp index 12ff2c7..a4f812f 100644 --- a/src/server/state.cpp +++ b/src/server/state.cpp @@ -127,6 +127,9 @@ struct CState: public State, public interface::Server interface::MutexScope ms2(mc.mutex); mc.module->init(); } + + emit_event(Event("core:module_loaded", + new interface::ModuleLoadedEvent(module_name))); } void load_modules(const ss_ &path) @@ -207,6 +210,9 @@ struct CState: public State, public interface::Server delete mc->module; m_modules.erase(module_name); m_compiler->unload(module_name); + + emit_event(Event("core:module_unloaded", + new interface::ModuleUnloadedEvent(module_name))); } ss_ get_modules_path() diff --git a/test/testmodules/__loader/__loader.cpp b/test/testmodules/__loader/__loader.cpp index 9929cb5..fbb1008 100644 --- a/test/testmodules/__loader/__loader.cpp +++ b/test/testmodules/__loader/__loader.cpp @@ -42,6 +42,7 @@ struct Module: public interface::Module { ss_ builtin = m_server->get_builtin_modules_path(); 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"); sv_ load_list = { diff --git a/test/testmodules/test1/test1.cpp b/test/testmodules/test1/test1.cpp index 3dc0311..cedc62f 100644 --- a/test/testmodules/test1/test1.cpp +++ b/test/testmodules/test1/test1.cpp @@ -2,7 +2,8 @@ #include "interface/server.h" #include "interface/event.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 "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, m_EventType_test1_thing); 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")); } @@ -44,8 +45,8 @@ struct Module: public interface::Module EVENT_VOIDN("core:start", on_start) EVENT_TYPE(m_EventType_test1_thing, on_thing, Thing) EVENT_TYPEN("network:new_client", on_new_client, network::NewClient) - EVENT_TYPEN("client_lua:files_sent:test1", on_lua_files_sent, - client_lua::FilesSent) + EVENT_TYPEN("client_file:files_sent", on_files_sent, + client_file::FilesSent) 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){ inetwork->send(event.recipient, "core:run_script", "print(\"TODO: Run init.lua\")"); diff --git a/todo.txt b/todo.txt index 15c788d..87ec915 100644 --- a/todo.txt +++ b/todo.txt @@ -9,3 +9,8 @@ Buildat TODO - Modules should be run in threads. - Network should leave socket fds in m_server when destructing, and pick them up 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