diff --git a/filesystem/filesystem.hpp b/filesystem/filesystem.hpp index 90c2ea7..7a71f62 100644 --- a/filesystem/filesystem.hpp +++ b/filesystem/filesystem.hpp @@ -27,6 +27,7 @@ namespace FSLib { bool exists( const string &path ); bool isDirectory( const string &path ); bool rename( const string &file_a, const string &file_b ); +bool deleteFile( const string &file ); string canonical( const string &path ); bool createDirectoryFull( const string &path ); string getContainingDirectory( const string &path ); diff --git a/filesystem/unix/filesystem.cpp b/filesystem/unix/filesystem.cpp index 16c4792..e754d14 100644 --- a/filesystem/unix/filesystem.cpp +++ b/filesystem/unix/filesystem.cpp @@ -1,6 +1,7 @@ #include "../filesystem.hpp" #include #include +#include #ifdef __APPLE__ #include #endif @@ -71,6 +72,34 @@ bool FSLib::rename( const string &file_a, const string &file_b ) { return ::rename( file_a.c_str(), file_b.c_str() ) == 0; } +// TODO do windows version +bool deleteRecursive(const string &dir) { + for(const auto &file : FSLib::Directory(dir)) { + auto path = dir + "/" + file; + if(FSLib::isDirectory(path)) { + if(!deleteRecursive(path)) { + return false; + } + } else if(unlink(path.c_str()) != 0) { + return false; + } + } + return rmdir(dir.c_str()) == 0; +} + +bool FSLib::deleteFile( const string &file ) { + // TODO log + auto canon = canonical( file ); + if(canon.empty()) { + return false; + } + + if(isDirectory(canon)) { + return deleteRecursive(canon); + } + return unlink(canon.c_str()) == 0; +} + bool FSLib::createDirectoryFull( const string &path ) { uint64_t pos{}; // go through all directories leading to the last one diff --git a/main.cpp b/main.cpp index 5075b6a..a827b8e 100644 --- a/main.cpp +++ b/main.cpp @@ -20,16 +20,16 @@ std::vector libraries{}; Configuration cfg; -void sendResponse(const std::string &response, int status_code, const std::shared_ptr< restbed::Session > session) { +void sendResponse(const std::string &response, int status_code, const std::shared_ptr< restbed::Session > &session) { session->close(status_code, response, { { "Content-Length", std::to_string(response.length()) }, { "Access-Control-Allow-Origin", "*" } }); } -void performPostFunc(const std::shared_ptr session, std::function, rapidjson::GenericDocument>&)> callback) { +void performPostFunc(const std::shared_ptr &session, const std::function, rapidjson::GenericDocument>&)>& callback) { const auto request = session->get_request( ); int content_length = request->get_header( "Content-Length", 0 ); - session->fetch( content_length, [callback]( const std::shared_ptr< restbed::Session > session, const restbed::Bytes & body ) + session->fetch( content_length, [callback]( const std::shared_ptr< restbed::Session > &session, const restbed::Bytes & body ) { rapidjson::Document doc; std::cout << std::string(reinterpret_cast(body.data()), body.size()) << std::endl; @@ -49,7 +49,7 @@ bool verifyLogin( const std::shared_ptr< restbed::Session > &session, rapidjson: return false; } std::cout << "AAAA" << std::endl; - auto token = doc["token"].GetString(); + const auto *token = doc["token"].GetString(); auto res = verifyJWT(token); if(!res) { sendResponse("ERROR: Invalid token!", 401, session); @@ -69,8 +69,8 @@ std::string getTypesJson() { auto types = getTypes(); std::ostringstream result; result << "[\n"; - if(types.size() > 0) { - for(auto type : types) { + if(!types.empty()) { + for(const auto &type : types) { result << " {\n \"id\": " << type.second << "\n"; result << " \"name\": \"" << type.first << "\"\n },\n"; } @@ -81,7 +81,7 @@ std::string getTypesJson() { return result.str(); } -void getTypesRest( const std::shared_ptr< restbed::Session > session ) { +void getTypesRest( const std::shared_ptr< restbed::Session > &session ) { sendResponse(getTypesJson(), restbed::OK, session); } @@ -100,7 +100,7 @@ std::string getOptionsJson(const RenameObject &search, size_t type) { std::ostringstream res; res << "[\n"; auto options = getOptions(search, type); - if(options.size() > 0) { + if(!options.empty()) { for(auto &option : options) { res << option.toJson(); res << ",\n"; @@ -112,7 +112,7 @@ std::string getOptionsJson(const RenameObject &search, size_t type) { return res.str(); } -void getOptionsRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) +void getOptionsRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(doc.HasParseError()) { sendResponse("ERROR: Invalid body!", 401, session); @@ -155,7 +155,7 @@ std::string getCustomKeysJson(size_t type) { std::ostringstream res; res << "[\n"; auto custom_keys = getCustomKeys(type); - if(custom_keys.size() > 0) { + if(!custom_keys.empty()) { for(auto &key : custom_keys) { res << "\"" << key << "\",\n"; } @@ -166,7 +166,7 @@ std::string getCustomKeysJson(size_t type) { return res.str(); } -void getCustomKeysRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) +void getCustomKeysRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(doc.FindMember("type") == doc.MemberEnd() || !doc["type"].IsUint64()) { sendResponse("ERROR: Invalid type!", 401, session); @@ -182,9 +182,11 @@ std::pair renamePath(std::string path, const RenameObject &re } if(path[0] == '/' && path.substr(0, cfg.getSourcePath().length()) != cfg.getSourcePath()) { return {false, "Invalid path"}; - } else if(path.find("..") != std::string::npos) { + } + if(path.find("..") != std::string::npos) { return {false, "Path cannot contain '..'"}; - } else if(path[0] != '/') { + } + if(path[0] != '/') { path = cfg.getSourcePath() + "/" + path; } if(!FSLib::exists(path)) { @@ -210,7 +212,7 @@ std::string renamePathJson(const std::string &path, const RenameObject &renamer) return res.str(); } -void renamePathRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) { +void renamePathRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(!verifyLogin(session, doc)) { return; } @@ -237,7 +239,7 @@ std::string getFilesJson() { std::ostringstream res; res << "[\n"; auto files = getFilesInSource(cfg.getSourcePath()); - if(files.size() > 0) { + if(!files.empty()) { for(const auto &file : files) { res << "\"" << file << "\",\n"; } @@ -247,7 +249,7 @@ std::string getFilesJson() { return res.str(); } -void getFilesRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) +void getFilesRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(!verifyLogin(session, doc)) { return; @@ -268,7 +270,7 @@ std::string getTargetsJson() { std::ostringstream res; res << "[\n"; auto targets = getTargets(); - if(targets.size() > 0) { + if(!targets.empty()) { for(const auto &target : targets) { res << " {\n" << " \"id\": " << target.first << "\n"; res << " \"name\": \"" << target.second << "\"\n },\n"; @@ -279,7 +281,7 @@ std::string getTargetsJson() { return res.str(); } -void getTargetsRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) +void getTargetsRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(!verifyLogin(session, doc)) { return; @@ -294,7 +296,7 @@ std::string getTargetDirectoriesJson(uint64_t id) { std::ostringstream res; res << "[\n"; auto dirs = getTargetDirectories(cfg.getTargetPaths()[id].first); - if(dirs.size() > 0) { + if(!dirs.empty()) { for(const auto &dir : dirs) { res << " \"" << dir << "\",\n"; } @@ -304,7 +306,7 @@ std::string getTargetDirectoriesJson(uint64_t id) { return res.str(); } -void getTargetDirectoriesRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) +void getTargetDirectoriesRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(!verifyLogin(session, doc)) { return; @@ -320,9 +322,11 @@ void getTargetDirectoriesRest( const std::shared_ptr< restbed::Session > session std::pair move(std::string path, uint64_t target_id, const std::string &containing_dir) { if(path[0] == '/' && path.substr(0, cfg.getSourcePath().length()) != cfg.getSourcePath()) { return {false, "Invalid path"}; - } else if(path.find("..") != std::string::npos) { + } + if(path.find("..") != std::string::npos) { return {false, "Path cannot contain '..'"}; - } else if(path[0] != '/') { + } + if(path[0] != '/') { path = cfg.getSourcePath() + "/" + path; } if(target_id >= cfg.getTargetPaths().size()) { @@ -355,12 +359,12 @@ std::string moveJson(const std::string &path, uint64_t target_id, const std::str return res.str(); } -void moveRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) +void moveRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(!verifyLogin(session, doc)) { return; } - std::string containing_dir = ""; + std::string containing_dir; if(doc.FindMember("path") == doc.MemberEnd() || !doc["path"].IsString()) { // TODO validate path, also validate against config sendResponse("ERROR: Invalid path!", 401, session); @@ -379,7 +383,55 @@ void moveRest( const std::shared_ptr< restbed::Session > session, rapidjson::Gen sendResponse(moveJson(path, id, containing_dir), 200, session); } -void loginRest( const std::shared_ptr< restbed::Session > session, rapidjson::GenericDocument> &doc ) { +std::pair remove(std::string path) { + if(path[0] == '/' && path.substr(0, cfg.getSourcePath().length()) != cfg.getSourcePath()) { + return {false, "Invalid path"}; + } + if(path.find("..") != std::string::npos) { + return {false, "Path cannot contain '..'"}; + } + if(path[0] != '/') { + path = cfg.getSourcePath() + "/" + path; + } + if(!FSLib::exists(path)) { + return {false, "Source doesn't exist"}; + } + return {FSLib::deleteFile(path), "Library error"}; +} + +std::string removeJson(const std::string &path) { + std::ostringstream res; + res << "{\n \"success\": "; + auto remove_result = remove(path); + if(remove_result.first) { + res << "true"; + } else { + res << "false"; + } + res << "\"\n"; + if(!remove_result.first) { + res << " \"error\": \"" << remove_result.second << "\"\n"; + } + res << "}"; + return res.str(); +} + +void removeRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) +{ + if(!verifyLogin(session, doc)) { + return; + } + if(doc.FindMember("path") == doc.MemberEnd() || !doc["path"].IsString()) { + // TODO validate path, also validate against config + sendResponse("ERROR: Invalid path!", 401, session); + return; + } + std::string path = doc["path"].GetString(); + // TODO correct response code + sendResponse(removeJson(path), 200, session); +} + +void loginRest( const std::shared_ptr< restbed::Session > &session, rapidjson::GenericDocument> &doc ) { if(doc.FindMember("user") == doc.MemberEnd() || !doc["user"].IsString()) { sendResponse("ERROR: Invalid user!", 401, session); return; @@ -388,10 +440,10 @@ void loginRest( const std::shared_ptr< restbed::Session > session, rapidjson::Ge sendResponse("ERROR: Invalid password!", 401, session); return; } - auto user = doc["user"].GetString(); - auto password = doc["password"].GetString(); + const auto *user = doc["user"].GetString(); + const auto *password = doc["password"].GetString(); bool valid = false; - for(auto &user_cfg : cfg.getUsers()) { + for(const auto &user_cfg : cfg.getUsers()) { if(user_cfg.first == user) { valid = user_cfg.second == password; break; @@ -404,35 +456,39 @@ void loginRest( const std::shared_ptr< restbed::Session > session, rapidjson::Ge } } -void getOptionsCall(const std::shared_ptr session) { +void getOptionsCall(const std::shared_ptr &session) { performPostFunc(session, getOptionsRest); } -void getCustomKeysCall(const std::shared_ptr session) { +void getCustomKeysCall(const std::shared_ptr &session) { performPostFunc(session, getCustomKeysRest); } -void renameCall(const std::shared_ptr session) { +void renameCall(const std::shared_ptr &session) { performPostFunc(session, renamePathRest); } -void getFilesCall(const std::shared_ptr session) { +void getFilesCall(const std::shared_ptr &session) { performPostFunc(session, getFilesRest); } -void getTargetsCall(const std::shared_ptr session) { +void getTargetsCall(const std::shared_ptr &session) { performPostFunc(session, getTargetsRest); } -void getTargetDirectoriesCall(const std::shared_ptr session) { +void getTargetDirectoriesCall(const std::shared_ptr &session) { performPostFunc(session, getTargetDirectoriesRest); } -void moveCall(const std::shared_ptr session) { +void moveCall(const std::shared_ptr &session) { performPostFunc(session, moveRest); } -void loginCall(const std::shared_ptr session) { +void removeCall(const std::shared_ptr &session) { + performPostFunc(session, removeRest); +} + +void loginCall(const std::shared_ptr &session) { performPostFunc(session, loginRest); } @@ -486,6 +542,11 @@ int main(int argc, char **argv) { move->set_method_handler( "POST", moveCall ); service.publish(move); + auto remove = std::make_shared< restbed::Resource >(); + remove->set_path("/remove"); + remove->set_method_handler( "POST", removeCall ); + service.publish(remove); + auto login = std::make_shared< restbed::Resource >(); login->set_path("/login"); login->set_method_handler( "POST", loginCall );