Commitok összehasonlítása
9 Commit-ok
next
...
feature/ma
Szerző | SHA1 | Dátum | |
---|---|---|---|
ebd4adf22f | |||
3745a778e5 | |||
cec56d5a73 | |||
534d9f74e7 | |||
5f674cfe6f | |||
ea7476a882 | |||
ab9e5fb0bd | |||
7ca04fbf45 | |||
8b44dd4e94 |
9
.gitmodules
vendored
9
.gitmodules
vendored
@ -4,6 +4,9 @@
|
|||||||
[submodule "submodules/cpp-httplib"]
|
[submodule "submodules/cpp-httplib"]
|
||||||
path = submodules/cpp-httplib
|
path = submodules/cpp-httplib
|
||||||
url = https://github.com/yhirose/cpp-httplib
|
url = https://github.com/yhirose/cpp-httplib
|
||||||
[submodule "submodules/exile.h"]
|
[submodule "submodules/qssb.h"]
|
||||||
path = submodules/exile.h
|
path = submodules/qssb.h
|
||||||
url = https://gitea.quitesimple.org/crtxcr/exile.h.git
|
url = https://gitea.quitesimple.org/crtxcr/qssb.h.git
|
||||||
|
[submodule "submodules/qsmaddy"]
|
||||||
|
path = submodules/qsmaddy
|
||||||
|
url = https://gitea.quitesimple.org/crtxcr/qsmaddy
|
||||||
|
34
Makefile
34
Makefile
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
CPPSTD=c++20
|
|
||||||
CXXFLAGS=-std=$(CPPSTD) -O0 -g -no-pie -pipe -MMD -Wall -Wextra
|
CXXFLAGS=-std=c++17 -O0 -g -no-pie -pipe -MMD -Wall -Wextra
|
||||||
RELEASE_CXXFLAGS=-std=$(CPPSTD) -O3 -pipe -MMD -Wall -Wextra
|
RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra
|
||||||
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs
|
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs -lseccomp
|
||||||
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/exile.h
|
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h -I submodules/qsmaddy/include/
|
||||||
|
|
||||||
CXX=g++
|
CXX=g++
|
||||||
|
|
||||||
@ -14,7 +14,6 @@ SOURCES+=$(wildcard handlers/*.cpp)
|
|||||||
SOURCES+=$(wildcard database/*.cpp)
|
SOURCES+=$(wildcard database/*.cpp)
|
||||||
SOURCES+=$(wildcard cache/*.cpp)
|
SOURCES+=$(wildcard cache/*.cpp)
|
||||||
SOURCES+=$(wildcard sandbox/*.cpp)
|
SOURCES+=$(wildcard sandbox/*.cpp)
|
||||||
SOURCES+=$(wildcard dynamic/*.cpp)
|
|
||||||
|
|
||||||
HEADERS=$(wildcard *.h)
|
HEADERS=$(wildcard *.h)
|
||||||
HEADERS+=$(wildcard gateway/*.h)
|
HEADERS+=$(wildcard gateway/*.h)
|
||||||
@ -22,7 +21,7 @@ HEADERS+=$(wildcard handlers/*.h)
|
|||||||
HEADERS+=$(wildcard database/*.h)
|
HEADERS+=$(wildcard database/*.h)
|
||||||
HEADERS+=$(wildcard cache/*.h)
|
HEADERS+=$(wildcard cache/*.h)
|
||||||
HEADERS+=$(wildcard sandbox/*.h)
|
HEADERS+=$(wildcard sandbox/*.h)
|
||||||
HEADERS+=$(wildcard dynamic/*.h)
|
|
||||||
|
|
||||||
OBJECTS=$(patsubst %.cpp, %.o, $(SOURCES))
|
OBJECTS=$(patsubst %.cpp, %.o, $(SOURCES))
|
||||||
WIKIOBJECTS=$(filter-out test.o, $(OBJECTS))
|
WIKIOBJECTS=$(filter-out test.o, $(OBJECTS))
|
||||||
@ -36,25 +35,16 @@ GTEST_DIR = /home/data/SOURCES/gtest/googletest
|
|||||||
|
|
||||||
GTESTS_TESTDIR = ./tests/
|
GTESTS_TESTDIR = ./tests/
|
||||||
|
|
||||||
GTEST_CXXFLAGS=-std=$(CPPSTD) -isystem $(GTEST_DIR)/include -I$(GTEST_DIR) -g -O0 -pipe -Wall -Wextra
|
GTEST_CXXFLAGS=-std=c++17 -isystem $(GTEST_DIR)/include -I$(GTEST_DIR) -g -O0 -pipe -Wall -Wextra
|
||||||
GTEST_LDFLAGS=-lsqlite3 -g -O0 -lpthread -lcrypto -lstdc++fs
|
GTEST_LDFLAGS=-lsqlite3 -g -O0 -lpthread -lcrypto -lstdc++fs
|
||||||
GTEST_OBJECTS=$(filter-out qswiki.o, $(WIKIOBJECTS))
|
GTEST_OBJECTS=$(filter-out qswiki.o, $(WIKIOBJECTS))
|
||||||
|
|
||||||
.DEFAULT_GOAL := qswiki
|
.DEFAULT_GOAL := qswiki
|
||||||
|
|
||||||
release: CXXFLAGS=$(RELEASE_CXXFLAGS)
|
release: CXXFLAGS=$(RELEASE_CXXFLAGS)
|
||||||
profile: CXXFLAGS=$(RELEASE_CXXFLAGS) -pg
|
|
||||||
profile: LDFLAGS+= -pg
|
|
||||||
|
|
||||||
release: qswiki
|
release: qswiki
|
||||||
profile: qswiki
|
qswiki: $(WIKIOBJECTS)
|
||||||
|
$(CXX) $(WIKIOBJECTS) ${LDFLAGS} ${INCLUDEFLAGS} -o qswiki
|
||||||
|
|
||||||
exile.o: submodules/exile.h/exile.c
|
|
||||||
$(CC) -std=c99 -DHAVE_LANDLOCK=0 -c submodules/exile.h/exile.c -o exile.o
|
|
||||||
|
|
||||||
qswiki: $(WIKIOBJECTS) exile.o
|
|
||||||
$(CXX) $(WIKIOBJECTS) exile.o ${LDFLAGS} ${INCLUDEFLAGS} -o qswiki
|
|
||||||
|
|
||||||
test: $(TESTOBJECTS)
|
test: $(TESTOBJECTS)
|
||||||
$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test
|
$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test
|
||||||
@ -63,11 +53,9 @@ gtest: $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS)
|
|||||||
$(CXX) -o gtest $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) $(GTEST_CXXFLAGS) $(GTEST_DIR)/src/gtest_main.cc $(GTEST_DIR)/src/gtest-all.cc $(GTEST_LDFLAGS)
|
$(CXX) -o gtest $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) $(GTEST_CXXFLAGS) $(GTEST_DIR)/src/gtest_main.cc $(GTEST_DIR)/src/gtest-all.cc $(GTEST_LDFLAGS)
|
||||||
|
|
||||||
%.o:%.cpp
|
%.o:%.cpp
|
||||||
$(CXX) ${CXXFLAGS} ${INCLUDEFLAGS} -c -o $@ $<
|
$(CXX) ${CXXFLAGS} ${LDFLAGS} ${INCLUDEFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
version.o:version.cpp
|
|
||||||
$(CXX) ${CXXFLAGS} ${INCLUDEFLAGS} -DGITCOMMIT=\"$(shell git rev-parse --short HEAD)\" -c -o $@ $<
|
|
||||||
clean:
|
clean:
|
||||||
rm -f exile.o $(OBJECTS) $(DEPENDS)
|
rm -f $(OBJECTS) $(DEPENDS)
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +72,8 @@ Building
|
|||||||
Dependencies:
|
Dependencies:
|
||||||
- cpp-httplib: https://github.com/yhirose/cpp-httplib
|
- cpp-httplib: https://github.com/yhirose/cpp-httplib
|
||||||
- SqliteModernCpp: https://github.com/SqliteModernCpp
|
- SqliteModernCpp: https://github.com/SqliteModernCpp
|
||||||
- exile.h: https://gitea.quitesimple.org/crtxcr/exile.h
|
- qssb.h: https://gitea.quitesimple.org/crtxcr/qssb.h
|
||||||
|
- libseccomp: https://github.com/seccomp/libseccomp
|
||||||
- sqlite3: https://sqlite.org/index.html
|
- sqlite3: https://sqlite.org/index.html
|
||||||
|
|
||||||
The first three are header-only libraries that are included as a git submodule. The others must
|
The first three are header-only libraries that are included as a git submodule. The others must
|
||||||
|
@ -21,7 +21,7 @@ Authenticator::Authenticator(UserDao &userDao)
|
|||||||
// TODO: make failure counter configurable
|
// TODO: make failure counter configurable
|
||||||
bool Authenticator::isBanned(std::string ip)
|
bool Authenticator::isBanned(std::string ip)
|
||||||
{
|
{
|
||||||
if(loginFails.contains(ip))
|
if(utils::hasKey(loginFails, ip))
|
||||||
{
|
{
|
||||||
LoginFail &fl = loginFails[ip];
|
LoginFail &fl = loginFails[ip];
|
||||||
std::lock_guard<std::mutex> lock(fl.mutex);
|
std::lock_guard<std::mutex> lock(fl.mutex);
|
||||||
@ -42,12 +42,11 @@ std::vector<char> Authenticator::pbkdf5(std::string password, const std::vector<
|
|||||||
unsigned char hash[32];
|
unsigned char hash[32];
|
||||||
const EVP_MD *sha256 = EVP_sha256();
|
const EVP_MD *sha256 = EVP_sha256();
|
||||||
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
|
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
|
||||||
int ret =
|
int ret = PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
|
||||||
PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
|
|
||||||
if(ret != 1)
|
if(ret != 1)
|
||||||
{
|
{
|
||||||
Logger::error() << "Authenticator: pbkdf5: Failed to create hash";
|
Logger::error() << "Authenticator: pbkdf5: Failed to create hash";
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
std::vector<char> result;
|
std::vector<char> result;
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include "database/userdao.h"
|
#include "database/userdao.h"
|
||||||
|
|
||||||
#define AUTH_DEFAULT_SALT_SIZE 32
|
|
||||||
enum AuthenticationError
|
enum AuthenticationError
|
||||||
{
|
{
|
||||||
UserNotFound,
|
UserNotFound,
|
||||||
|
2
cache/fscache.cpp
vendored
2
cache/fscache.cpp
vendored
@ -46,7 +46,7 @@ void FsCache::removePrefix(std::string_view prefix)
|
|||||||
// TODO: lock dir
|
// TODO: lock dir
|
||||||
for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path{this->path}))
|
for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path{this->path}))
|
||||||
{
|
{
|
||||||
if(std::string_view(entry.path().filename().c_str()).starts_with(prefix))
|
if(static_cast<std::string>(entry.path().filename()).find(prefix) == 0)
|
||||||
{
|
{
|
||||||
std::filesystem::remove_all(entry);
|
std::filesystem::remove_all(entry);
|
||||||
}
|
}
|
||||||
|
1
cache/mapcache.cpp
vendored
1
cache/mapcache.cpp
vendored
@ -1 +0,0 @@
|
|||||||
#include "mapcache.h"
|
|
38
cache/mapcache.h
vendored
38
cache/mapcache.h
vendored
@ -1,38 +0,0 @@
|
|||||||
#ifndef MAPCACHE_H
|
|
||||||
#define MAPCACHE_H
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
/* Thread-Safe Key-Value store */
|
|
||||||
template <class T> class MapCache
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::map<std::string, T> cache;
|
|
||||||
mutable std::shared_mutex sharedMutex;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::optional<T> find(const std::string &key) const
|
|
||||||
{
|
|
||||||
std::shared_lock<std::shared_mutex> lock(this->sharedMutex);
|
|
||||||
auto it = this->cache.find(key);
|
|
||||||
if(it != this->cache.end())
|
|
||||||
{
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
void set(const std::string &key, const T &val)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::shared_mutex> lock{sharedMutex};
|
|
||||||
this->cache[key] = val;
|
|
||||||
}
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::shared_mutex> lock{sharedMutex};
|
|
||||||
this->cache.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MAPCACHE_H
|
|
279
cli.cpp
279
cli.cpp
@ -1,279 +0,0 @@
|
|||||||
#include <map>
|
|
||||||
#include <functional>
|
|
||||||
#include <iomanip>
|
|
||||||
|
|
||||||
#include "cli.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "random.h"
|
|
||||||
#include "authenticator.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "logger.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
CLIHandler::CLIHandler(Config &config, Database &db)
|
|
||||||
{
|
|
||||||
this->db = &db;
|
|
||||||
this->conf = &config;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::user_add(const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
std::string username = args.at(0);
|
|
||||||
std::string password = args.at(1);
|
|
||||||
|
|
||||||
auto userDao = db->createUserDao();
|
|
||||||
|
|
||||||
Permissions perms = this->conf->handlersConfig.anon_permissions;
|
|
||||||
int p = perms.getPermissions();
|
|
||||||
p |= PERM_CAN_CREATE | PERM_CAN_SEARCH | PERM_CAN_EDIT;
|
|
||||||
Permissions newPermissions = Permissions{p};
|
|
||||||
|
|
||||||
Random r;
|
|
||||||
User user;
|
|
||||||
user.enabled = true;
|
|
||||||
user.login = username;
|
|
||||||
user.salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
|
|
||||||
user.permissions = newPermissions;
|
|
||||||
|
|
||||||
Authenticator auth{*userDao};
|
|
||||||
std::vector<char> hashResult = auth.hash(password, user.salt);
|
|
||||||
if(hashResult.empty())
|
|
||||||
{
|
|
||||||
return {false, "Error during hashing - Got empty hash"};
|
|
||||||
}
|
|
||||||
user.password = hashResult;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
userDao->save(user);
|
|
||||||
}
|
|
||||||
catch(std::runtime_error &e)
|
|
||||||
{
|
|
||||||
return {false, "Exception: " + std::string(e.what())};
|
|
||||||
}
|
|
||||||
return {true, ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::user_change_pw([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
std::string username = args.at(0);
|
|
||||||
std::string password = args.at(1);
|
|
||||||
|
|
||||||
auto userDao = db->createUserDao();
|
|
||||||
|
|
||||||
auto user = userDao->find(username);
|
|
||||||
if(user)
|
|
||||||
{
|
|
||||||
Random r;
|
|
||||||
Authenticator auth{*userDao};
|
|
||||||
user->salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
|
|
||||||
user->password = auth.hash(password, user->salt);
|
|
||||||
if(user->password.empty())
|
|
||||||
{
|
|
||||||
return {false, "Error during hashing - Got empty hash"};
|
|
||||||
}
|
|
||||||
|
|
||||||
userDao->save(*user);
|
|
||||||
}
|
|
||||||
return {false, "User not found"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::user_rename([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
|
|
||||||
return {true, ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::user_set_perms([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto userDao = this->db->createUserDao();
|
|
||||||
std::string username = args.at(0);
|
|
||||||
std::string permission_string = args.at(1);
|
|
||||||
|
|
||||||
Permissions perms{permission_string};
|
|
||||||
|
|
||||||
auto user = userDao->find(username);
|
|
||||||
if(user)
|
|
||||||
{
|
|
||||||
user->permissions = perms;
|
|
||||||
userDao->save(*user);
|
|
||||||
user_show({username});
|
|
||||||
return {true, ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {false, "User not found"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::user_list([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto userDao = this->db->createUserDao();
|
|
||||||
QueryOption o;
|
|
||||||
auto result = userDao->list(o);
|
|
||||||
std::stringstream stream;
|
|
||||||
for(User &u : result)
|
|
||||||
{
|
|
||||||
stream << u.login << "\t" << std::string(u.enabled ? "enabled" : "disabled") << "\t" << u.permissions.toString()
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
return {true, stream.str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::user_show(const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
std::string username = args.at(0);
|
|
||||||
auto userDao = this->db->createUserDao();
|
|
||||||
auto user = userDao->find(username);
|
|
||||||
std::stringstream stream;
|
|
||||||
if(user)
|
|
||||||
{
|
|
||||||
stream << "Username: " << user->login << std::endl;
|
|
||||||
|
|
||||||
stream << "Enabled: " << std::string(user->enabled ? "yes" : "no") << std::endl;
|
|
||||||
stream << "Permissions (general): " << user->permissions.toString() << std::endl;
|
|
||||||
return {true, stream.str()};
|
|
||||||
}
|
|
||||||
return {false, "User not found"};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::page_list([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto pageDao = this->db->createPageDao();
|
|
||||||
QueryOption o;
|
|
||||||
auto result = pageDao->getPageList(o);
|
|
||||||
std::stringstream stream;
|
|
||||||
for(std::string pagename : result)
|
|
||||||
{
|
|
||||||
Page p = pageDao->find(pagename).value();
|
|
||||||
stream << p.name << " " << p.pageid << " " << std::string(p.listed ? "listed" : "unlisted") << std::endl;
|
|
||||||
}
|
|
||||||
return {true, stream.str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::pageperms_set_permissions(const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
std::string page = args.at(0);
|
|
||||||
std::string username = args.at(1);
|
|
||||||
std::string perms = args.at(2);
|
|
||||||
|
|
||||||
auto permissionsDao = this->db->createPermissionsDao();
|
|
||||||
permissionsDao->save(page, username, Permissions{perms});
|
|
||||||
return {true, ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::attach([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
/* TODO: consider authentication */
|
|
||||||
pid_t pid = getpid();
|
|
||||||
return {true, "Hi, I am pid: " + std::to_string(pid)};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::cli_help(const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
std::string command;
|
|
||||||
if(args.size() > 0)
|
|
||||||
command = args[0];
|
|
||||||
std::stringstream stream;
|
|
||||||
for(struct cmd &cmd : cmds)
|
|
||||||
{
|
|
||||||
if(command != "" && cmd.name != command)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream << cmd.name << " - " << cmd.helptext << std::endl;
|
|
||||||
for(struct cmd &subCmd : cmd.subCommands)
|
|
||||||
{
|
|
||||||
stream << "\t" << subCmd.name << " " << subCmd.helptext << std::endl;
|
|
||||||
}
|
|
||||||
stream << std::endl;
|
|
||||||
}
|
|
||||||
return {true, stream.str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::processCommand(const std::vector<CLIHandler::cmd> &commands, std::string cmd,
|
|
||||||
const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto c = std::find_if(commands.begin(), commands.end(),
|
|
||||||
[&cmd](const struct CLIHandler::cmd &a) { return a.name == cmd; });
|
|
||||||
if(c == commands.end())
|
|
||||||
{
|
|
||||||
std::cout << "No such command: " << cmd << std::endl;
|
|
||||||
return cli_help({});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!c->subCommands.empty() && args.size() >= c->required_args)
|
|
||||||
{
|
|
||||||
std::string newcmd = args[0];
|
|
||||||
std::vector<std::string> newargs = args;
|
|
||||||
newargs.erase(newargs.begin());
|
|
||||||
return processCommand(c->subCommands, newcmd, newargs);
|
|
||||||
}
|
|
||||||
if(args.size() < c->required_args)
|
|
||||||
{
|
|
||||||
return {false, "not enough parameters passed"};
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return c->func(this, args);
|
|
||||||
}
|
|
||||||
catch(std::runtime_error &e)
|
|
||||||
{
|
|
||||||
return {false, "Exception: " + std::string(e.what())};
|
|
||||||
}
|
|
||||||
return {false, ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::processCommand(std::string cmd, const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
return processCommand(this->cmds, cmd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::string, std::vector<std::string>> CLIHandler::splitCommand(std::string input)
|
|
||||||
{
|
|
||||||
input = utils::trim(input);
|
|
||||||
std::vector<std::string> splitted = utils::split(input, "\\s+");
|
|
||||||
if(splitted.empty())
|
|
||||||
{
|
|
||||||
return {" ", splitted};
|
|
||||||
}
|
|
||||||
std::string cmd = splitted[0];
|
|
||||||
splitted.erase(splitted.begin());
|
|
||||||
return {cmd, splitted};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::version([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
return {true, get_version_string()};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::category_list([[maybe_unused]] const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto categoryDao = this->db->createCategoryDao();
|
|
||||||
auto categories = categoryDao->fetchList(QueryOption{});
|
|
||||||
std::stringstream stream;
|
|
||||||
for(std::string &cat : categories)
|
|
||||||
{
|
|
||||||
stream << cat << std::endl;
|
|
||||||
}
|
|
||||||
return {true, stream.str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::category_delete(const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto categoryDao = this->db->createCategoryDao();
|
|
||||||
categoryDao->deleteCategory(args.at(0));
|
|
||||||
return {true, ""};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIHandler::category_show(const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
auto categoryDao = this->db->createCategoryDao();
|
|
||||||
auto members = categoryDao->fetchMembers(args.at(0), QueryOption{});
|
|
||||||
std::stringstream stream;
|
|
||||||
for(std::string &member : members)
|
|
||||||
{
|
|
||||||
stream << member << std::endl;
|
|
||||||
}
|
|
||||||
return {true, stream.str()};
|
|
||||||
}
|
|
94
cli.h
94
cli.h
@ -1,94 +0,0 @@
|
|||||||
#ifndef CLI_H
|
|
||||||
#define CLI_H
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "database/database.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
class CLIHandler
|
|
||||||
{
|
|
||||||
struct cmd
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::string helptext;
|
|
||||||
unsigned int required_args;
|
|
||||||
std::vector<cmd> subCommands;
|
|
||||||
std::function<std::pair<bool, std::string>(CLIHandler *, const std::vector<std::string> &)> func;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
Database *db;
|
|
||||||
Config *conf;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::pair<bool, std::string> attach([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> cli_help([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_add([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_change_pw([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_rename([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_set_perms([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_list([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_show([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> page_list([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> pageperms_set_permissions([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> version([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> category_list([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> category_delete([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> category_show([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
|
|
||||||
std::vector<struct cmd> cmds{
|
|
||||||
{{"user",
|
|
||||||
"user operations on the database",
|
|
||||||
1,
|
|
||||||
{{{"add", "[user] [password] - creates a user", 2, {}, &CLIHandler::user_add},
|
|
||||||
{"changepw", "[user] [password] - changes the password of user", 2, {}, &CLIHandler::user_change_pw},
|
|
||||||
{"rename", "[user] [new name] - renames a user", 2, {}, &CLIHandler::user_rename},
|
|
||||||
{"setperms", "[user] [perms] - sets the permissions of the user", 2, {}, &CLIHandler::user_set_perms},
|
|
||||||
{"list", "- lists users", 0, {}, &CLIHandler::user_list},
|
|
||||||
{"show", "[user] - show detailed information about user", 1, {}, &CLIHandler::user_show}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"page",
|
|
||||||
"operation on pages",
|
|
||||||
1,
|
|
||||||
{{{"list", "- lists existing pages", 0, {}, &CLIHandler::page_list}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"category",
|
|
||||||
"operation on categories",
|
|
||||||
1,
|
|
||||||
{{{"list", "- lists existing categories", 0, {}, &CLIHandler::category_list},
|
|
||||||
{"delete", " - deletes a category", 1, {}, &CLIHandler::category_delete},
|
|
||||||
{"show", " - shows pages of a category", 1, {}, &CLIHandler::category_show}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"pageperms",
|
|
||||||
"set permissions on pages",
|
|
||||||
1,
|
|
||||||
{{{"set",
|
|
||||||
"- [page] [username] [permissions] set permisisons on page",
|
|
||||||
3,
|
|
||||||
{},
|
|
||||||
&CLIHandler::pageperms_set_permissions}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"exit",
|
|
||||||
"exit cli",
|
|
||||||
0,
|
|
||||||
{},
|
|
||||||
[](CLIHandler *, [[maybe_unused]] const std::vector<std::string> &args) -> std::pair<bool, std::string>
|
|
||||||
{
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
return {true, ""};
|
|
||||||
}},
|
|
||||||
{"help", "print this help", 0, {}, &CLIHandler::cli_help},
|
|
||||||
{"attach", "attach to running instance", 0, {}, &CLIHandler::attach},
|
|
||||||
{"version", "print verison info", 0, {}, &CLIHandler::version}}};
|
|
||||||
|
|
||||||
std::pair<bool, std::string> processCommand(const std::vector<CLIHandler::cmd> &commands, std::string cmd,
|
|
||||||
const std::vector<std::string> &args);
|
|
||||||
|
|
||||||
public:
|
|
||||||
CLIHandler(Config &config, Database &d);
|
|
||||||
std::pair<bool, std::string> processCommand(std::string cmd, const std::vector<std::string> &args);
|
|
||||||
static std::pair<std::string, std::vector<std::string>> splitCommand(std::string input);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLI_H
|
|
147
cliconsole.cpp
147
cliconsole.cpp
@ -1,147 +0,0 @@
|
|||||||
#include "cliconsole.h"
|
|
||||||
|
|
||||||
CLIConsole::CLIConsole(CLIHandler &cliHandler, std::string socketPath)
|
|
||||||
{
|
|
||||||
this->handler = &cliHandler;
|
|
||||||
this->socketPath = socketPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIConsole::send(std::string input)
|
|
||||||
{
|
|
||||||
ssize_t ret =
|
|
||||||
sendto(this->sock, input.c_str(), input.size(), 0, (const sockaddr *)&this->server, sizeof(this->server));
|
|
||||||
if((size_t)ret != input.size())
|
|
||||||
{
|
|
||||||
return {false, "sendto failed: " + std::to_string(ret) + " " + std::string(strerror(errno))};
|
|
||||||
}
|
|
||||||
char buffer[1024] = {0};
|
|
||||||
ret = recvfrom(this->sock, buffer, sizeof(buffer) - 1, 0, NULL, NULL);
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
return {false, "recvfrom failed: " + std::string(strerror(errno))};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
|
||||||
std::string_view view = buffer;
|
|
||||||
if(view[0] == '1')
|
|
||||||
{
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
view.remove_prefix(1);
|
|
||||||
std::string msg = std::string{view};
|
|
||||||
|
|
||||||
return {success, msg};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CLIConsole::attach()
|
|
||||||
{
|
|
||||||
if(attached)
|
|
||||||
{
|
|
||||||
std::cout << "Already attached" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(socketPath.size() > sizeof(this->server.sun_path) - 1)
|
|
||||||
{
|
|
||||||
std::cout << "Socket path too long" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memset(&this->server, 0, sizeof(this->server));
|
|
||||||
this->server.sun_family = AF_UNIX;
|
|
||||||
memcpy(&this->server.sun_path, socketPath.c_str(), socketPath.size());
|
|
||||||
this->server.sun_path[socketPath.size()] = 0;
|
|
||||||
|
|
||||||
int s = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
||||||
if(s == -1)
|
|
||||||
{
|
|
||||||
std::cout << "Failed to create socket" << strerror(errno) << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->sock = s;
|
|
||||||
|
|
||||||
struct sockaddr_un client;
|
|
||||||
client.sun_family = AF_UNIX;
|
|
||||||
client.sun_path[0] = '\0';
|
|
||||||
|
|
||||||
int ret = bind(this->sock, (struct sockaddr *)&client, sizeof(client));
|
|
||||||
if(ret != 0)
|
|
||||||
{
|
|
||||||
std::cout << "bind() failed: " << strerror(errno) << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto result = this->send("attach");
|
|
||||||
if(result.first)
|
|
||||||
{
|
|
||||||
std::cout << "Attached successfully: " << result.second << std::endl;
|
|
||||||
this->attached = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << "Attached unsuccessfully: " << result.second << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CLIConsole::startInteractive()
|
|
||||||
{
|
|
||||||
std::cout << "qswiki CLI" << std::endl;
|
|
||||||
std::cout << "not attached - use 'attach' to connect to running instance" << std::endl;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
std::string input;
|
|
||||||
std::cout << "> ";
|
|
||||||
std::getline(std::cin, input);
|
|
||||||
|
|
||||||
if(std::cin.bad() || std::cin.eof())
|
|
||||||
{
|
|
||||||
std::cout << "Exiting" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(input.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pair = CLIHandler::splitCommand(input);
|
|
||||||
if(pair.first == "exit")
|
|
||||||
{
|
|
||||||
if(attached)
|
|
||||||
{
|
|
||||||
std::cout << "You are attached. Quit attached instance too (y) or only this one(n)" << std::endl;
|
|
||||||
char response;
|
|
||||||
std::cin >> response;
|
|
||||||
if(response == 'y')
|
|
||||||
{
|
|
||||||
this->send("exit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout << "Exiting CLI" << std::endl;
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
if(pair.first == "attach")
|
|
||||||
{
|
|
||||||
attach();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> result;
|
|
||||||
if(!attached)
|
|
||||||
{
|
|
||||||
result = handler->processCommand(pair.first, pair.second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = this->send(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!result.second.empty())
|
|
||||||
{
|
|
||||||
std::cout << result.second << std::endl;
|
|
||||||
}
|
|
||||||
if(!result.first)
|
|
||||||
{
|
|
||||||
std::cout << "Command failed" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
27
cliconsole.h
27
cliconsole.h
@ -1,27 +0,0 @@
|
|||||||
#ifndef CLICONSOLE_H
|
|
||||||
#define CLICONSOLE_H
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include "cli.h"
|
|
||||||
|
|
||||||
class CLIConsole
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
struct sockaddr_un server;
|
|
||||||
int sock;
|
|
||||||
CLIHandler *handler;
|
|
||||||
std::string socketPath;
|
|
||||||
bool attached = false;
|
|
||||||
std::pair<bool, std::string> send(std::string input);
|
|
||||||
void attach();
|
|
||||||
|
|
||||||
public:
|
|
||||||
CLIConsole(CLIHandler &cliHandler, std::string socketPath);
|
|
||||||
void startInteractive();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLICONSOLE_H
|
|
@ -1,77 +0,0 @@
|
|||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "cliserver.h"
|
|
||||||
#include "logger.h"
|
|
||||||
CLIServer::CLIServer(CLIHandler &handler)
|
|
||||||
{
|
|
||||||
this->handler = &handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CLIServer::detachServer(std::string socketpath)
|
|
||||||
{
|
|
||||||
struct sockaddr_un name;
|
|
||||||
const int max_socket_length = sizeof(name.sun_path) - 1;
|
|
||||||
if(socketpath.size() > max_socket_length)
|
|
||||||
{
|
|
||||||
perror("socket path too long");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int s = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
||||||
if(s == -1)
|
|
||||||
{
|
|
||||||
perror("socket");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&name, 0, sizeof(name));
|
|
||||||
name.sun_family = AF_UNIX;
|
|
||||||
memcpy(&name.sun_path, socketpath.c_str(), socketpath.size());
|
|
||||||
|
|
||||||
unlink(socketpath.c_str());
|
|
||||||
int ret = bind(s, (const struct sockaddr *)&name, sizeof(name));
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
perror("bind");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto worker = [this, s]
|
|
||||||
{
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
char buffer[1024] = {0};
|
|
||||||
struct sockaddr_un peer;
|
|
||||||
socklen_t peerlen = sizeof(peer);
|
|
||||||
|
|
||||||
int ret = recvfrom(s, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &peerlen);
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
Logger::error() << "Error during recvfrom in CLI server: " << strerror(errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string input{buffer};
|
|
||||||
|
|
||||||
auto pair = CLIHandler::splitCommand(input);
|
|
||||||
|
|
||||||
auto result = handler->processCommand(pair.first, pair.second);
|
|
||||||
char resultCode = '0';
|
|
||||||
if(result.first)
|
|
||||||
{
|
|
||||||
resultCode = '1';
|
|
||||||
}
|
|
||||||
std::string resultString;
|
|
||||||
resultString += resultCode;
|
|
||||||
resultString += result.second;
|
|
||||||
ret = sendto(s, resultString.c_str(), resultString.size(), 0, (struct sockaddr *)&peer, peerlen);
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
Logger::error() << "Error during sendto in CLI server: " << strerror(errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::thread t1{worker};
|
|
||||||
t1.detach();
|
|
||||||
return true;
|
|
||||||
}
|
|
16
cliserver.h
16
cliserver.h
@ -1,16 +0,0 @@
|
|||||||
#ifndef CLISERVER_H
|
|
||||||
#define CLISERVER_H
|
|
||||||
#include <thread>
|
|
||||||
#include "cli.h"
|
|
||||||
|
|
||||||
class CLIServer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
CLIHandler *handler = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CLIServer(CLIHandler &handler);
|
|
||||||
bool detachServer(std::string socketpath);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLISERVER_H
|
|
@ -24,7 +24,6 @@ SOFTWARE.
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "permissions.h"
|
#include "permissions.h"
|
||||||
#include "varreplacer.h"
|
#include "varreplacer.h"
|
||||||
|
|
||||||
std::string Config::required(const std::string &key)
|
std::string Config::required(const std::string &key)
|
||||||
{
|
{
|
||||||
auto it = this->configmap.find(key);
|
auto it = this->configmap.find(key);
|
||||||
@ -72,6 +71,7 @@ Config::Config(const std::map<std::string, std::string> &map)
|
|||||||
|
|
||||||
this->configmap = map;
|
this->configmap = map;
|
||||||
this->wikipath = optional("wikipath", "/");
|
this->wikipath = optional("wikipath", "/");
|
||||||
|
this->parser = optional("parser", "markdown");
|
||||||
this->handlersConfig.anon_username = optional("anon_username", "anonymouse");
|
this->handlersConfig.anon_username = optional("anon_username", "anonymouse");
|
||||||
this->handlersConfig.wikiname = required("wikiname");
|
this->handlersConfig.wikiname = required("wikiname");
|
||||||
this->logfile = required("logfile");
|
this->logfile = required("logfile");
|
||||||
@ -97,8 +97,6 @@ Config::Config(const std::map<std::string, std::string> &map)
|
|||||||
this->urls.deletionurl = required("deletionurl");
|
this->urls.deletionurl = required("deletionurl");
|
||||||
this->urls.adminregisterurl = required("adminregisterurl");
|
this->urls.adminregisterurl = required("adminregisterurl");
|
||||||
this->urls.usersettingsurl = required("usersettingsurl");
|
this->urls.usersettingsurl = required("usersettingsurl");
|
||||||
this->urls.rooturl = required("rooturl");
|
|
||||||
this->urls.atomurl = required("atomurl");
|
|
||||||
this->connectionstring = required("connectionstring");
|
this->connectionstring = required("connectionstring");
|
||||||
|
|
||||||
this->handlersConfig.max_pagename_length = optional("max_pagename_length", 256);
|
this->handlersConfig.max_pagename_length = optional("max_pagename_length", 256);
|
||||||
|
3
config.h
3
config.h
@ -41,8 +41,6 @@ struct ConfigUrls
|
|||||||
std::string linkhistorysort;
|
std::string linkhistorysort;
|
||||||
std::string adminregisterurl;
|
std::string adminregisterurl;
|
||||||
std::string usersettingsurl;
|
std::string usersettingsurl;
|
||||||
std::string rooturl;
|
|
||||||
std::string atomurl;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigVariableResolver
|
class ConfigVariableResolver
|
||||||
@ -88,6 +86,7 @@ class Config
|
|||||||
std::string templateprefix;
|
std::string templateprefix;
|
||||||
std::string logfile;
|
std::string logfile;
|
||||||
std::string connectionstring;
|
std::string connectionstring;
|
||||||
|
std::string parser;
|
||||||
int session_max_lifetime;
|
int session_max_lifetime;
|
||||||
int threadscount;
|
int threadscount;
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ std::optional<Category> CategoryDaoSqlite::find(std::string name)
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryDaoSqlite::save(const Category &c)
|
void CategoryDaoSqlite::save(const Category &c)
|
||||||
@ -65,9 +64,9 @@ void CategoryDaoSqlite::deleteCategory(std::string name)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
*db << "BEGIN;";
|
*db << "BEGIN";
|
||||||
*db << "DELETE FROM categorymember WHERE category = (SELECT id FROM category WHERE name = ?);" << name;
|
*db << "DELETE FROM categorymember WHERE catid = (SELECT id FROM category WHERE name = ?)" << name;
|
||||||
*db << "DELETE FROM category WHERE name = ?;" << name;
|
*db << "DELETE FROM category WHERE name = ?" << name;
|
||||||
*db << "COMMIT;";
|
*db << "COMMIT;";
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
|
@ -58,9 +58,9 @@ std::optional<Page> PageDaoSqlite::find(unsigned int id)
|
|||||||
result.pageid = id;
|
result.pageid = id;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto ps = *db << "SELECT name, title, lastrevision, visible FROM page WHERE id = ?";
|
auto ps = *db << "SELECT name, lastrevision, visible FROM page WHERE id = ?";
|
||||||
|
|
||||||
ps << id >> std::tie(result.name, result.title, result.current_revision, result.listed);
|
ps << id >> std::tie(result.name, result.current_revision, result.listed);
|
||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
@ -97,10 +97,9 @@ void PageDaoSqlite::save(const Page &page)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
*db << "INSERT OR REPLACE INTO page (id, name, title, lastrevision, visible) VALUES((SELECT id FROM page WHERE "
|
*db << "INSERT OR REPLACE INTO page (id, name, lastrevision, visible) VALUES((SELECT id FROM page WHERE name = "
|
||||||
"name = "
|
"? OR id = ?), ?, ?, ?)"
|
||||||
"? OR id = ?), ?, ?, ?, ?)"
|
<< page.name << page.pageid << page.name << page.current_revision << page.listed;
|
||||||
<< page.name << page.pageid << page.name << page.title << page.current_revision << page.listed;
|
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
@ -184,8 +183,7 @@ std::vector<SearchResult> PageDaoSqlite::search(std::string name, QueryOption op
|
|||||||
auto query =
|
auto query =
|
||||||
*db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? "
|
*db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? "
|
||||||
<< ftsEscape(name);
|
<< ftsEscape(name);
|
||||||
query >> [&](std::string pagename)
|
query >> [&](std::string pagename) {
|
||||||
{
|
|
||||||
SearchResult sresult;
|
SearchResult sresult;
|
||||||
sresult.pagename = pagename;
|
sresult.pagename = pagename;
|
||||||
sresult.query = name;
|
sresult.query = name;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#ifndef PERMISSIONSDAO_H
|
#ifndef PERMISSIONSDAO_H
|
||||||
#define PERMISSIONSDAO_H
|
#define PERMISSIONSDAO_H
|
||||||
#include <optional>
|
|
||||||
#include "../permissions.h"
|
#include "../permissions.h"
|
||||||
#include "../user.h"
|
#include "../user.h"
|
||||||
class PermissionsDao
|
class PermissionsDao
|
||||||
@ -8,7 +7,6 @@ class PermissionsDao
|
|||||||
public:
|
public:
|
||||||
PermissionsDao();
|
PermissionsDao();
|
||||||
virtual std::optional<Permissions> find(std::string pagename, std::string username) = 0;
|
virtual std::optional<Permissions> find(std::string pagename, std::string username) = 0;
|
||||||
virtual void save(std::string pagename, std::string username, Permissions perms) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PERMISSIONSDAO_H
|
#endif // PERMISSIONSDAO_H
|
||||||
|
@ -41,21 +41,3 @@ std::optional<Permissions> PermissionsDaoSqlite::find(std::string pagename, std:
|
|||||||
|
|
||||||
return Permissions{permissions};
|
return Permissions{permissions};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PermissionsDaoSqlite::save(std::string pagename, std::string username, Permissions perms)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto query =
|
|
||||||
*db
|
|
||||||
<< "INSERT OR REPLACE INTO permissions (id, permissions, userid, page) VALUES((SELECT id FROM permissions "
|
|
||||||
"WHERE page = (SELECT id FROM page WHERE name = ?) AND userid = (SELECT id FROM user WHERE username = "
|
|
||||||
"?)), ?, (SELECT id FROM user WHERE username = ?), (SELECT id FROM page WHERE name = ?))";
|
|
||||||
query << pagename << username << perms.getPermissions() << username << pagename;
|
|
||||||
query.execute();
|
|
||||||
}
|
|
||||||
catch(const sqlite::errors::no_rows &e)
|
|
||||||
{
|
|
||||||
throwFrom(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -9,7 +9,6 @@ class PermissionsDaoSqlite : public PermissionsDao, protected SqliteDao
|
|||||||
PermissionsDaoSqlite();
|
PermissionsDaoSqlite();
|
||||||
|
|
||||||
std::optional<Permissions> find(std::string pagename, std::string username) override;
|
std::optional<Permissions> find(std::string pagename, std::string username) override;
|
||||||
virtual void save(std::string pagename, std::string username, Permissions perms) override;
|
|
||||||
using SqliteDao::SqliteDao;
|
using SqliteDao::SqliteDao;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,17 +22,18 @@ SOFTWARE.
|
|||||||
|
|
||||||
bool SqliteDao::execBool(sqlite::database_binder &binder) const
|
bool SqliteDao::execBool(sqlite::database_binder &binder) const
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
bool result;
|
||||||
binder >> result;
|
binder >> result;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
// TODO: well, we may want to check whether rows have found or not and thus log this here
|
// TODO: well, we may want to check whether rows have found or not and thus log this here
|
||||||
result = false;
|
return false;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SqliteDao::execInt(sqlite::database_binder &binder) const
|
int SqliteDao::execInt(sqlite::database_binder &binder) const
|
||||||
@ -51,5 +52,4 @@ int SqliteDao::execInt(sqlite::database_binder &binder) const
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -46,18 +46,15 @@ SqliteQueryOption &SqliteQueryOption::setPrependWhere(bool b)
|
|||||||
std::string SqliteQueryOption::build()
|
std::string SqliteQueryOption::build()
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
if(this->prependWhere)
|
|
||||||
{
|
|
||||||
result += "WHERE ";
|
|
||||||
}
|
|
||||||
if(!o.includeInvisible && !this->visibleColumnName.empty())
|
if(!o.includeInvisible && !this->visibleColumnName.empty())
|
||||||
{
|
{
|
||||||
|
if(this->prependWhere)
|
||||||
|
{
|
||||||
|
result += "WHERE ";
|
||||||
|
}
|
||||||
result += this->visibleColumnName + " = 1";
|
result += this->visibleColumnName + " = 1";
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
result += " 1 = 1";
|
|
||||||
}
|
|
||||||
result += " ORDER BY " + orderByColumnName;
|
result += " ORDER BY " + orderByColumnName;
|
||||||
if(o.order == ASCENDING)
|
if(o.order == ASCENDING)
|
||||||
{
|
{
|
||||||
@ -69,8 +66,7 @@ std::string SqliteQueryOption::build()
|
|||||||
}
|
}
|
||||||
// TODO: limits for offset?
|
// TODO: limits for offset?
|
||||||
if(o.limit > 0)
|
if(o.limit > 0)
|
||||||
{
|
|
||||||
result += " LIMIT " + std::to_string(o.limit) + " OFFSET " + std::to_string(o.offset);
|
result += " LIMIT " + std::to_string(o.limit) + " OFFSET " + std::to_string(o.offset);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "../user.h"
|
#include "../user.h"
|
||||||
#include "queryoption.h"
|
|
||||||
class UserDao
|
class UserDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -11,7 +10,6 @@ class UserDao
|
|||||||
virtual bool exists(std::string username) = 0;
|
virtual bool exists(std::string username) = 0;
|
||||||
virtual std::optional<User> find(std::string username) = 0;
|
virtual std::optional<User> find(std::string username) = 0;
|
||||||
virtual std::optional<User> find(int id) = 0;
|
virtual std::optional<User> find(int id) = 0;
|
||||||
virtual std::vector<User> list(QueryOption o) = 0;
|
|
||||||
virtual void deleteUser(std::string username) = 0;
|
virtual void deleteUser(std::string username) = 0;
|
||||||
virtual void save(const User &u) = 0;
|
virtual void save(const User &u) = 0;
|
||||||
virtual ~UserDao(){};
|
virtual ~UserDao(){};
|
||||||
|
@ -23,7 +23,6 @@ SOFTWARE.
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "userdaosqlite.h"
|
#include "userdaosqlite.h"
|
||||||
#include "sqlitequeryoption.h"
|
|
||||||
|
|
||||||
UserDaoSqlite::UserDaoSqlite()
|
UserDaoSqlite::UserDaoSqlite()
|
||||||
{
|
{
|
||||||
@ -37,6 +36,7 @@ bool UserDaoSqlite::exists(std::string username)
|
|||||||
|
|
||||||
std::optional<User> UserDaoSqlite::find(std::string username)
|
std::optional<User> UserDaoSqlite::find(std::string username)
|
||||||
{
|
{
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
User user;
|
User user;
|
||||||
@ -47,7 +47,7 @@ std::optional<User> UserDaoSqlite::find(std::string username)
|
|||||||
stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled);
|
stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled);
|
||||||
user.permissions = Permissions{perms};
|
user.permissions = Permissions{perms};
|
||||||
|
|
||||||
return user;
|
return std::move(user);
|
||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
@ -57,7 +57,6 @@ std::optional<User> UserDaoSqlite::find(std::string username)
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<User> UserDaoSqlite::find(int id)
|
std::optional<User> UserDaoSqlite::find(int id)
|
||||||
@ -71,7 +70,7 @@ std::optional<User> UserDaoSqlite::find(int id)
|
|||||||
stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled);
|
stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled);
|
||||||
user.permissions = Permissions{perms};
|
user.permissions = Permissions{perms};
|
||||||
|
|
||||||
return user;
|
return std::move(user);
|
||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
@ -81,43 +80,9 @@ std::optional<User> UserDaoSqlite::find(int id)
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<User> UserDaoSqlite::list(QueryOption o)
|
void UserDaoSqlite::deleteUser(std::string username)
|
||||||
{
|
|
||||||
std::vector<User> result;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
std::string queryOption = SqliteQueryOption(o).setOrderByColumn("username").setPrependWhere(true).build();
|
|
||||||
std::string query = "SELECT username, password, salt, permissions, enabled FROM user " + queryOption;
|
|
||||||
|
|
||||||
*db << query >>
|
|
||||||
[&](std::string username, std::vector<char> pw, std::vector<char> salt, int permisisons, bool enabled)
|
|
||||||
{
|
|
||||||
User tmp;
|
|
||||||
tmp.login = username;
|
|
||||||
tmp.password = pw;
|
|
||||||
tmp.salt = salt;
|
|
||||||
tmp.permissions = Permissions{permisisons};
|
|
||||||
tmp.enabled = enabled;
|
|
||||||
result.push_back(tmp);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch(const sqlite::errors::no_rows &e)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch(sqlite::sqlite_exception &e)
|
|
||||||
{
|
|
||||||
throwFrom(e);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserDaoSqlite::deleteUser([[maybe_unused]] std::string username)
|
|
||||||
{
|
{
|
||||||
// What to do with the contributions of the user?
|
// What to do with the contributions of the user?
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ class UserDaoSqlite : public UserDao, protected SqliteDao
|
|||||||
std::optional<User> find(std::string username);
|
std::optional<User> find(std::string username);
|
||||||
std::optional<User> find(int id);
|
std::optional<User> find(int id);
|
||||||
|
|
||||||
std::vector<User> list(QueryOption o);
|
|
||||||
void deleteUser(std::string username);
|
void deleteUser(std::string username);
|
||||||
void save(const User &u);
|
void save(const User &u);
|
||||||
using SqliteDao::SqliteDao;
|
using SqliteDao::SqliteDao;
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#include "dynamiccontent.h"
|
|
||||||
|
|
||||||
DynamicContent::DynamicContent(Template &templ, Database &database, UrlProvider &provider)
|
|
||||||
{
|
|
||||||
this->templ = &templ;
|
|
||||||
this->database = &database;
|
|
||||||
this->urlProvider = &provider;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
#ifndef DYNAMICCONTENT_H
|
|
||||||
#define DYNAMICCONTENT_H
|
|
||||||
#include <string>
|
|
||||||
#include "../database/database.h"
|
|
||||||
#include "../template.h"
|
|
||||||
#include "../urlprovider.h"
|
|
||||||
class DynamicContent
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
Template *templ;
|
|
||||||
Database *database;
|
|
||||||
UrlProvider *urlProvider;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DynamicContent(Template &templ, Database &database, UrlProvider &urlProvider);
|
|
||||||
virtual std::string render() = 0;
|
|
||||||
virtual ~DynamicContent()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DYNAMICCONTENT_H
|
|
@ -1,44 +0,0 @@
|
|||||||
#include <chrono>
|
|
||||||
#include "dynamiccontentpostlist.h"
|
|
||||||
|
|
||||||
void DynamicContentPostList::setCategory(std::string catname)
|
|
||||||
{
|
|
||||||
this->catname = catname;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DynamicContentPostList::render()
|
|
||||||
{
|
|
||||||
auto categoryDao = this->database->createCategoryDao();
|
|
||||||
auto pageDao = this->database->createPageDao();
|
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
|
||||||
QueryOption option;
|
|
||||||
option.includeInvisible = false;
|
|
||||||
auto members = categoryDao->fetchMembers(this->catname, option);
|
|
||||||
std::vector<std::pair<std::string, time_t>> pageList;
|
|
||||||
for(std::string &member : members)
|
|
||||||
{
|
|
||||||
auto revision = revisionDao->getRevisionForPage(member, 1);
|
|
||||||
pageList.push_back({member, revision->timestamp});
|
|
||||||
}
|
|
||||||
std::sort(pageList.begin(), pageList.end(),
|
|
||||||
[](std::pair<std::string, time_t> &a, std::pair<std::string, time_t> &b) { return a.second > b.second; });
|
|
||||||
|
|
||||||
std::string postListBegin = this->templ->loadResolvedPart("dynamic/postlistbegin");
|
|
||||||
std::string postListEnd = this->templ->loadResolvedPart("dynamic/postlistend");
|
|
||||||
std::string postLink = this->templ->loadResolvedPart("dynamic/postlistlink");
|
|
||||||
std::stringstream stream;
|
|
||||||
stream << postListBegin;
|
|
||||||
for(auto &pair : pageList)
|
|
||||||
{
|
|
||||||
std::string link = this->urlProvider->page(pair.first);
|
|
||||||
std::string date = utils::toISODate(pair.second);
|
|
||||||
Varreplacer replacer{"{"};
|
|
||||||
replacer.addKeyValue("url", link);
|
|
||||||
replacer.addKeyValue("date", date);
|
|
||||||
replacer.addKeyValue("title", pageDao->find(pair.first)->title);
|
|
||||||
|
|
||||||
stream << replacer.parse(postLink);
|
|
||||||
}
|
|
||||||
stream << postListEnd;
|
|
||||||
return stream.str();
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef DYNAMICCONTENTPOSTLIST_H
|
|
||||||
#define DYNAMICCONTENTPOSTLIST_H
|
|
||||||
|
|
||||||
#include "dynamiccontent.h"
|
|
||||||
class DynamicContentPostList : public DynamicContent
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string catname;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using DynamicContent::DynamicContent;
|
|
||||||
void setCategory(std::string catname);
|
|
||||||
std::string render() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DYNAMICCONTENTPOSTLIST_H
|
|
@ -41,7 +41,7 @@ Request HttpGateway::convertRequest(httplib::Request request)
|
|||||||
// TODO: this eats resources, where perhaps it does not need to. move it to request?
|
// TODO: this eats resources, where perhaps it does not need to. move it to request?
|
||||||
for(auto &it : request.params)
|
for(auto &it : request.params)
|
||||||
{
|
{
|
||||||
it.second = utils::html_xss(it.second);
|
it.second = utils::html_xss(std::string{it.second});
|
||||||
}
|
}
|
||||||
if(request.method == "GET")
|
if(request.method == "GET")
|
||||||
{
|
{
|
||||||
@ -83,8 +83,7 @@ void HttpGateway::work(RequestWorker &worker)
|
|||||||
{
|
{
|
||||||
httplib::Server server;
|
httplib::Server server;
|
||||||
server.set_payload_max_length(this->maxPayloadLength);
|
server.set_payload_max_length(this->maxPayloadLength);
|
||||||
auto handler = [&](const httplib::Request &req, httplib::Response &res)
|
auto handler = [&](const httplib::Request &req, httplib::Response &res) {
|
||||||
{
|
|
||||||
Request wikiRequest = convertRequest(req);
|
Request wikiRequest = convertRequest(req);
|
||||||
Logger::debug() << "httpgateway: received request " << wikiRequest;
|
Logger::debug() << "httpgateway: received request " << wikiRequest;
|
||||||
Response wikiresponse = worker.processRequest(wikiRequest);
|
Response wikiresponse = worker.processRequest(wikiRequest);
|
||||||
|
@ -34,7 +34,7 @@ void Handler::setGeneralVars(TemplatePage &page)
|
|||||||
}
|
}
|
||||||
Response Handler::errorResponse(std::string errortitle, std::string errormessage, int status)
|
Response Handler::errorResponse(std::string errortitle, std::string errormessage, int status)
|
||||||
{
|
{
|
||||||
TemplatePage error = this->templ->getPage("error");
|
TemplatePage &error = this->templ->getPage("error");
|
||||||
error.setVar("title", createPageTitle(errortitle));
|
error.setVar("title", createPageTitle(errortitle));
|
||||||
error.setVar("errortitle", errortitle);
|
error.setVar("errortitle", errortitle);
|
||||||
error.setVar("errormessage", errormessage);
|
error.setVar("errormessage", errormessage);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#ifndef HANDLER_H
|
#ifndef HANDLER_H
|
||||||
#define HANDLER_H
|
#define HANDLER_H
|
||||||
#include <memory>
|
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
#include "../response.h"
|
#include "../response.h"
|
||||||
#include "../request.h"
|
#include "../request.h"
|
||||||
@ -10,12 +9,13 @@
|
|||||||
#include "../database/queryoption.h"
|
#include "../database/queryoption.h"
|
||||||
#include "../logger.h"
|
#include "../logger.h"
|
||||||
#include "../cache/icache.h"
|
#include "../cache/icache.h"
|
||||||
#include "../dynamic/dynamiccontent.h"
|
#include "../iparser.h"
|
||||||
|
|
||||||
class Handler
|
class Handler
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
ICache *cache;
|
ICache *cache;
|
||||||
|
IParser *parser;
|
||||||
Template *templ;
|
Template *templ;
|
||||||
Database *database;
|
Database *database;
|
||||||
Session *userSession;
|
Session *userSession;
|
||||||
@ -28,7 +28,7 @@ class Handler
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider,
|
Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider,
|
||||||
ICache &cache)
|
ICache &cache, IParser &parser)
|
||||||
{
|
{
|
||||||
this->handlersConfig = &handlersConfig;
|
this->handlersConfig = &handlersConfig;
|
||||||
this->templ = &templ;
|
this->templ = &templ;
|
||||||
@ -36,15 +36,16 @@ class Handler
|
|||||||
this->userSession = &userSession;
|
this->userSession = &userSession;
|
||||||
this->urlProvider = &provider;
|
this->urlProvider = &provider;
|
||||||
this->cache = &cache;
|
this->cache = &cache;
|
||||||
|
this->parser = &parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Response handle(const Request &r);
|
virtual Response handle(const Request &r);
|
||||||
virtual Response handleRequest([[maybe_unused]] const Request &r)
|
virtual Response handleRequest(const Request &r)
|
||||||
{
|
{
|
||||||
return this->errorResponse("Invalid action", "This action is not implemented yet");
|
return this->errorResponse("Invalid action", "This action is not implemented yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool canAccess([[maybe_unused]] const Permissions &perms)
|
virtual bool canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -56,12 +57,6 @@ class Handler
|
|||||||
virtual ~Handler()
|
virtual ~Handler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> inline std::shared_ptr<T> createDynamic()
|
|
||||||
{
|
|
||||||
return std::make_shared<T>(*this->templ, *this->database, *this->urlProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
Response errorResponse(std::string errortitle, std::string errormessage, int status = 200);
|
Response errorResponse(std::string errortitle, std::string errormessage, int status = 200);
|
||||||
std::string createPageTitle(std::string append);
|
std::string createPageTitle(std::string append);
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,7 @@ Response HandlerAllCategories::handleRequest(const Request &r)
|
|||||||
"No categories",
|
"No categories",
|
||||||
"This wiki does not have any categories defined yet or your query options did not yield any results");
|
"This wiki does not have any categories defined yet or your query options did not yield any results");
|
||||||
}
|
}
|
||||||
TemplatePage searchPage = this->templ->getPage("allcategories");
|
TemplatePage &searchPage = this->templ->getPage("allcategories");
|
||||||
std::string body =
|
std::string body =
|
||||||
this->templ->renderSearch(resultList, [&](std::string str) { return this->urlProvider->category(str); });
|
this->templ->renderSearch(resultList, [&](std::string str) { return this->urlProvider->category(str); });
|
||||||
searchPage.setVar("categorylist", body);
|
searchPage.setVar("categorylist", body);
|
||||||
|
@ -32,7 +32,7 @@ Response HandlerAllPages::handleRequest(const Request &r)
|
|||||||
{
|
{
|
||||||
return errorResponse("No pages", "This wiki does not have any pages yet");
|
return errorResponse("No pages", "This wiki does not have any pages yet");
|
||||||
}
|
}
|
||||||
TemplatePage searchPage = this->templ->getPage("allpages");
|
TemplatePage &searchPage = this->templ->getPage("allpages");
|
||||||
std::string body = this->templ->renderSearch(resultList);
|
std::string body = this->templ->renderSearch(resultList);
|
||||||
searchPage.setVar("pagelist", body);
|
searchPage.setVar("pagelist", body);
|
||||||
searchPage.setVar("title", createPageTitle("All pages"));
|
searchPage.setVar("title", createPageTitle("All pages"));
|
||||||
|
@ -33,7 +33,7 @@ Response HandlerCategory::handleRequest(const Request &r)
|
|||||||
}
|
}
|
||||||
QueryOption qo = queryOption(r);
|
QueryOption qo = queryOption(r);
|
||||||
auto resultList = categoryDao->fetchMembers(categoryname, qo);
|
auto resultList = categoryDao->fetchMembers(categoryname, qo);
|
||||||
TemplatePage searchPage = this->templ->getPage("show_category");
|
TemplatePage &searchPage = this->templ->getPage("show_category");
|
||||||
std::string body = this->templ->renderSearch(resultList);
|
std::string body = this->templ->renderSearch(resultList);
|
||||||
searchPage.setVar("pagelist", body);
|
searchPage.setVar("pagelist", body);
|
||||||
searchPage.setVar("categoryname", categoryname);
|
searchPage.setVar("categoryname", categoryname);
|
||||||
|
@ -20,7 +20,7 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include "handlerdefault.h"
|
#include "handlerdefault.h"
|
||||||
|
|
||||||
Response HandlerDefault::handleRequest([[maybe_unused]] const Request &r)
|
Response HandlerDefault::handleRequest(const Request &r)
|
||||||
{
|
{
|
||||||
return Response::redirectTemporarily(this->urlProvider->index());
|
return Response::redirectTemporarily(this->urlProvider->index());
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ HandlerDefault::~HandlerDefault()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerDefault::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerDefault::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,7 @@ SOFTWARE.
|
|||||||
#include "handlerhistory.h"
|
#include "handlerhistory.h"
|
||||||
#include "handlerpagedelete.h"
|
#include "handlerpagedelete.h"
|
||||||
#include "handlerusersettings.h"
|
#include "handlerusersettings.h"
|
||||||
#include "handlerversion.h"
|
|
||||||
#include "handlerfeedgenerator.h"
|
|
||||||
std::unique_ptr<Handler> HandlerFactory::createHandler(const std::string &action, Session &userSession)
|
std::unique_ptr<Handler> HandlerFactory::createHandler(const std::string &action, Session &userSession)
|
||||||
{
|
{
|
||||||
if(action == "" || action == "index")
|
if(action == "" || action == "index")
|
||||||
@ -81,14 +80,6 @@ std::unique_ptr<Handler> HandlerFactory::createHandler(const std::string &action
|
|||||||
{
|
{
|
||||||
return produce<HandlerUserSettings>(userSession);
|
return produce<HandlerUserSettings>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "version")
|
|
||||||
{
|
|
||||||
return produce<HandlerVersion>(userSession);
|
|
||||||
}
|
|
||||||
if(action == "feed")
|
|
||||||
{
|
|
||||||
return produce<HandlerFeedGenerator>(userSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
return produce<HandlerInvalidAction>(userSession);
|
return produce<HandlerInvalidAction>(userSession);
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,16 @@ class HandlerFactory
|
|||||||
Database &db;
|
Database &db;
|
||||||
UrlProvider &urlProvider;
|
UrlProvider &urlProvider;
|
||||||
ICache &cache;
|
ICache &cache;
|
||||||
|
IParser &parser;
|
||||||
|
|
||||||
template <class T> inline std::unique_ptr<T> produce(Session &userSession)
|
template <class T> inline std::unique_ptr<T> produce(Session &userSession)
|
||||||
{
|
{
|
||||||
return std::make_unique<T>(handlerConfig, templ, db, userSession, urlProvider, cache);
|
return std::make_unique<T>(handlerConfig, templ, db, userSession, urlProvider, cache, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache)
|
HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache, IParser &parser)
|
||||||
: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache)
|
: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache), parser(parser)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession);
|
std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession);
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
#include "handlerfeedgenerator.h"
|
|
||||||
#include "../parser.h"
|
|
||||||
std::vector<HandlerFeedGenerator::EntryRevisionPair> HandlerFeedGenerator::fetchEntries(
|
|
||||||
std::vector<std::string> categories)
|
|
||||||
{
|
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
|
||||||
auto pageDao = this->database->createPageDao();
|
|
||||||
|
|
||||||
std::vector<EntryRevisionPair> result;
|
|
||||||
QueryOption option;
|
|
||||||
option.includeInvisible = false;
|
|
||||||
// option.limit = 20;
|
|
||||||
std::set<std::string> members;
|
|
||||||
if(categories.empty())
|
|
||||||
{
|
|
||||||
auto pages = pageDao->getPageList(option);
|
|
||||||
std::copy(pages.begin(), pages.end(), std::inserter(members, members.end()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto categoryDao = this->database->createCategoryDao();
|
|
||||||
for(std::string cat : categories)
|
|
||||||
{
|
|
||||||
auto catmembers = categoryDao->fetchMembers(cat, option);
|
|
||||||
std::copy(catmembers.begin(), catmembers.end(), std::inserter(members, members.end()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(const std::string &member : members)
|
|
||||||
{
|
|
||||||
auto page = pageDao->find(member).value();
|
|
||||||
auto revision = revisionDao->getRevisionForPage(page.name, 1).value();
|
|
||||||
result.push_back({page, revision});
|
|
||||||
}
|
|
||||||
std::sort(result.begin(), result.end(),
|
|
||||||
[](EntryRevisionPair &a, EntryRevisionPair &b) { return a.second.timestamp > b.second.timestamp; });
|
|
||||||
|
|
||||||
const int maxResults = 20;
|
|
||||||
if(result.size() > maxResults)
|
|
||||||
{
|
|
||||||
result.erase(result.begin() + maxResults - 1, result.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Response HandlerFeedGenerator::generateAtom(const std::vector<HandlerFeedGenerator::EntryRevisionPair> &entries,
|
|
||||||
std::string filter)
|
|
||||||
{
|
|
||||||
|
|
||||||
std::stringstream stream;
|
|
||||||
// don't care about offset for now especially since "%z" does not return what we need exactly (with ':')
|
|
||||||
const std::string dateformat = "%Y-%m-%dT%T";
|
|
||||||
|
|
||||||
time_t newestPublished = 0;
|
|
||||||
std::string atomfooter = this->templ->loadResolvedPart("feeds/atomfooter");
|
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
|
||||||
auto pageDao = this->database->createPageDao();
|
|
||||||
|
|
||||||
if(utils::trim(filter).empty())
|
|
||||||
{
|
|
||||||
filter = "All pages";
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const EntryRevisionPair &entry : entries)
|
|
||||||
{
|
|
||||||
const Page &page = entry.first;
|
|
||||||
const Revision &initialRevision = entry.second;
|
|
||||||
Revision current = revisionDao->getCurrentForPage(page.name).value();
|
|
||||||
std::string url = this->urlProvider->rootUrl() + this->urlProvider->page(page.name);
|
|
||||||
if(initialRevision.timestamp > newestPublished)
|
|
||||||
{
|
|
||||||
newestPublished = initialRevision.timestamp;
|
|
||||||
}
|
|
||||||
std::string entryPublished = utils::formatLocalDate(initialRevision.timestamp, dateformat) + "Z";
|
|
||||||
std::string entryurl =
|
|
||||||
this->urlProvider->combine({this->urlProvider->rootUrl(), this->urlProvider->page(page.name)});
|
|
||||||
TemplatePage atomentry = this->templ->getPage("feeds/atomentry");
|
|
||||||
atomentry.setVar("entrytitle", utils::html_xss(page.title));
|
|
||||||
atomentry.setVar("entryurl", utils::html_xss(entryurl));
|
|
||||||
atomentry.setVar("entryid", utils::html_xss(entryurl));
|
|
||||||
atomentry.setVar("entrypublished", entryPublished);
|
|
||||||
Parser parser;
|
|
||||||
atomentry.setVar("entrycontent", utils::html_xss(parser.parse(*pageDao, *this->urlProvider, current.content)));
|
|
||||||
stream << atomentry.render();
|
|
||||||
}
|
|
||||||
stream << atomfooter;
|
|
||||||
TemplatePage atomheader = this->templ->getPage("feeds/atomheader");
|
|
||||||
atomheader.setVar("subtitle", filter);
|
|
||||||
atomheader.setVar("atomfeeduniqueid", utils::html_xss(this->urlProvider->atomFeed(filter)));
|
|
||||||
atomheader.setVar("atomfeedupdate", utils::formatLocalDate(newestPublished, dateformat) + "Z");
|
|
||||||
|
|
||||||
Response result;
|
|
||||||
result.setStatus(200);
|
|
||||||
result.setContentType("application/atom+xml");
|
|
||||||
result.setBody(atomheader.render() + stream.str());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Response HandlerFeedGenerator::handleRequest(const Request &r)
|
|
||||||
{
|
|
||||||
Response response;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::string type = r.get("type");
|
|
||||||
std::string categories = r.get("cats");
|
|
||||||
|
|
||||||
auto entries = fetchEntries(utils::split(categories, ','));
|
|
||||||
if(type == "atom")
|
|
||||||
{
|
|
||||||
std::string filter = categories;
|
|
||||||
response = generateAtom(entries, filter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return errorResponse("Invalid feed type", "Unknown feed type, try atom", 400);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(std::runtime_error &e)
|
|
||||||
{
|
|
||||||
Logger::error() << "Error while serving feed: " << e.what();
|
|
||||||
return errorResponse("Error", "An error occured while trying to serve the feed", 500);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandlerFeedGenerator::canAccess(const Permissions &perms)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
#ifndef HANDLERFEEDGENERATOR_H
|
|
||||||
#define HANDLERFEEDGENERATOR_H
|
|
||||||
#include "handler.h"
|
|
||||||
#include "../page.h"
|
|
||||||
#include "../revision.h"
|
|
||||||
class HandlerFeedGenerator : public Handler
|
|
||||||
{
|
|
||||||
typedef std::pair<Page, Revision> EntryRevisionPair;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::vector<EntryRevisionPair> fetchEntries(std::vector<std::string> categories);
|
|
||||||
Response generateAtom(const std::vector<EntryRevisionPair> &entries, std::string atomtitle);
|
|
||||||
Response generateRss(const std::vector<EntryRevisionPair> &entries);
|
|
||||||
|
|
||||||
public:
|
|
||||||
using Handler::Handler;
|
|
||||||
Response handleRequest(const Request &r) override;
|
|
||||||
bool canAccess(const Permissions &perms) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // HANDLERFEEDGENERATOR_H
|
|
@ -50,8 +50,7 @@ Response HandlerHistory::handleRequest(const Request &r)
|
|||||||
std::vector<Revision> resultList;
|
std::vector<Revision> resultList;
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
auto revisionDao = this->database->createRevisionDao();
|
||||||
|
|
||||||
auto makeSortedLink = [&](unsigned int limit, unsigned int offset, unsigned int order)
|
auto makeSortedLink = [&](unsigned int limit, unsigned int offset, unsigned int order) {
|
||||||
{
|
|
||||||
if(!page.empty())
|
if(!page.empty())
|
||||||
{
|
{
|
||||||
return this->urlProvider->pageHistorySort(page, limit, offset, order);
|
return this->urlProvider->pageHistorySort(page, limit, offset, order);
|
||||||
@ -123,7 +122,7 @@ Response HandlerHistory::handleRequest(const Request &r)
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerHistory::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerHistory::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return true; // This is a lie but we need to this a little more fine grained here, which we do in the handleRequest
|
return true; // This is a lie but we need to this a little more fine grained here, which we do in the handleRequest
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include "handlerinvalidaction.h"
|
#include "handlerinvalidaction.h"
|
||||||
|
|
||||||
Response HandlerInvalidAction::handleRequest([[maybe_unused]] const Request &r)
|
Response HandlerInvalidAction::handleRequest(const Request &r)
|
||||||
{
|
{
|
||||||
return errorResponse("Invalid action", "No action defined for this action");
|
return errorResponse("Invalid action", "No action defined for this action");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerInvalidAction::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerInvalidAction::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ Response HandlerLogin::handleRequest(const Request &r)
|
|||||||
{
|
{
|
||||||
page = "index";
|
page = "index";
|
||||||
}
|
}
|
||||||
TemplatePage loginTemplatePage = this->templ->getPage("login");
|
TemplatePage &loginTemplatePage = this->templ->getPage("login");
|
||||||
setGeneralVars(loginTemplatePage);
|
setGeneralVars(loginTemplatePage);
|
||||||
loginTemplatePage.setVar("loginurl", urlProvider->login(page));
|
loginTemplatePage.setVar("loginurl", urlProvider->login(page));
|
||||||
loginTemplatePage.setVar("title", createPageTitle("Login"));
|
loginTemplatePage.setVar("title", createPageTitle("Login"));
|
||||||
@ -66,7 +66,7 @@ Response HandlerLogin::handleRequest(const Request &r)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerLogin::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerLogin::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,8 @@ void HandlerPage::setPageVars(TemplatePage &page, std::string pagename)
|
|||||||
if(!pagename.empty())
|
if(!pagename.empty())
|
||||||
{
|
{
|
||||||
std::string headerlinks;
|
std::string headerlinks;
|
||||||
TemplatePage headerlink = this->templ->getPage("_headerlink");
|
TemplatePage &headerlink = this->templ->getPage("_headerlink");
|
||||||
auto addHeaderLink = [&headerlinks, &headerlink](std::string href, std::string value)
|
auto addHeaderLink = [&headerlinks, &headerlink](std::string href, std::string value) {
|
||||||
{
|
|
||||||
headerlink.setVar("href", href);
|
headerlink.setVar("href", href);
|
||||||
headerlink.setVar("value", value);
|
headerlink.setVar("value", value);
|
||||||
headerlinks += headerlink.render();
|
headerlinks += headerlink.render();
|
||||||
|
@ -21,11 +21,11 @@ SOFTWARE.
|
|||||||
#include "handlerpageedit.h"
|
#include "handlerpageedit.h"
|
||||||
#include "../database/exceptions.h"
|
#include "../database/exceptions.h"
|
||||||
#include "../request.h"
|
#include "../request.h"
|
||||||
|
#include "../parserlegacy.h"
|
||||||
|
|
||||||
#include "../parser.h"
|
bool HandlerPageEdit::canAccess(std::string page)
|
||||||
bool HandlerPageEdit::canAccess([[maybe_unused]] std::string page)
|
|
||||||
{
|
{
|
||||||
return effectivePermissions(page).canEdit();
|
return this->userSession->user.permissions.canEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerPageEdit::pageMustExist()
|
bool HandlerPageEdit::pageMustExist()
|
||||||
@ -59,14 +59,13 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
|
|
||||||
// TODO: must check, whether categories differ, and perhaps don't allow every user
|
// TODO: must check, whether categories differ, and perhaps don't allow every user
|
||||||
// to set categories
|
// to set categories
|
||||||
Parser parser;
|
ParserLegacy parser;
|
||||||
std::vector<std::string> cats = parser.extractCategories(newContent);
|
std::vector<std::string> cats = parser.extractCategories(newContent);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->database->beginTransaction();
|
this->database->beginTransaction();
|
||||||
std::string visiblecmd = parser.extractCommand("visible", newContent);
|
std::string visiblecmd = parser.extractCommand("visible", newContent);
|
||||||
std::string rename = parser.extractCommand("rename", newContent);
|
std::string rename = parser.extractCommand("rename", newContent);
|
||||||
std::string customtitle = parser.extractCommand("pagetitle", newContent);
|
|
||||||
Page page;
|
Page page;
|
||||||
std::optional<Page> currentPage = pageDao.find(pagename);
|
std::optional<Page> currentPage = pageDao.find(pagename);
|
||||||
if(currentPage)
|
if(currentPage)
|
||||||
@ -84,11 +83,6 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
page.current_revision = current_revision;
|
page.current_revision = current_revision;
|
||||||
page.listed = !(visiblecmd == "0");
|
page.listed = !(visiblecmd == "0");
|
||||||
page.name = pagename;
|
page.name = pagename;
|
||||||
page.title = customtitle;
|
|
||||||
if(page.title.empty())
|
|
||||||
{
|
|
||||||
page.title = page.name;
|
|
||||||
}
|
|
||||||
pageDao.save(page);
|
pageDao.save(page);
|
||||||
|
|
||||||
Revision newRevision;
|
Revision newRevision;
|
||||||
@ -113,7 +107,7 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
if(r.post("do") == "preview")
|
if(r.post("do") == "preview")
|
||||||
{
|
{
|
||||||
std::string newContent = r.post("content");
|
std::string newContent = r.post("content");
|
||||||
Parser parser;
|
ParserLegacy parser;
|
||||||
TemplatePage templatePage = this->templ->getPage("page_creation_preview");
|
TemplatePage templatePage = this->templ->getPage("page_creation_preview");
|
||||||
templatePage.setVar("actionurl", urlProvider->editPage(pagename));
|
templatePage.setVar("actionurl", urlProvider->editPage(pagename));
|
||||||
templatePage.setVar("preview_content", parser.parse(pageDao, *this->urlProvider, newContent));
|
templatePage.setVar("preview_content", parser.parse(pageDao, *this->urlProvider, newContent));
|
||||||
@ -127,7 +121,7 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplatePage templatePage = this->templ->getPage("page_creation");
|
TemplatePage &templatePage = this->templ->getPage("page_creation");
|
||||||
templatePage.setVar("actionurl", urlProvider->editPage(pagename));
|
templatePage.setVar("actionurl", urlProvider->editPage(pagename));
|
||||||
templatePage.setVar("content", body);
|
templatePage.setVar("content", body);
|
||||||
setPageVars(templatePage, pagename);
|
setPageVars(templatePage, pagename);
|
||||||
|
@ -21,9 +21,9 @@ SOFTWARE.
|
|||||||
#include "handlerpageview.h"
|
#include "handlerpageview.h"
|
||||||
#include "../database/exceptions.h"
|
#include "../database/exceptions.h"
|
||||||
#include "../logger.h"
|
#include "../logger.h"
|
||||||
#include "../parser.h"
|
#include "../parserlegacy.h"
|
||||||
#include "../htmllink.h"
|
#include "../htmllink.h"
|
||||||
#include "../dynamic/dynamiccontentpostlist.h"
|
|
||||||
bool HandlerPageView::canAccess(std::string page)
|
bool HandlerPageView::canAccess(std::string page)
|
||||||
{
|
{
|
||||||
return effectivePermissions(page).canRead();
|
return effectivePermissions(page).canRead();
|
||||||
@ -128,29 +128,18 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
return errorResponse("Database error", "While trying to fetch revision, a database error occured");
|
return errorResponse("Database error", "While trying to fetch revision, a database error occured");
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplatePage page = this->templ->getPage(templatepartname);
|
TemplatePage &page = this->templ->getPage(templatepartname);
|
||||||
|
|
||||||
|
|
||||||
Parser parser;
|
|
||||||
Response result;
|
Response result;
|
||||||
result.setStatus(200);
|
result.setStatus(200);
|
||||||
std::string indexcontent;
|
std::string indexcontent;
|
||||||
std::string parsedcontent;
|
std::string parsedcontent;
|
||||||
|
|
||||||
std::function<std::string(std::string_view, std::string_view)> dynamicParseCallback =
|
|
||||||
[&](std::string_view key, std::string_view value) -> std::string
|
|
||||||
{
|
|
||||||
if(key == "dynamic:postlist")
|
|
||||||
{
|
|
||||||
std::shared_ptr<DynamicContentPostList> postlist = createDynamic<DynamicContentPostList>();
|
|
||||||
postlist->setCategory(std::string(value));
|
|
||||||
return postlist->render();
|
|
||||||
}
|
|
||||||
return std::string{};
|
|
||||||
};
|
|
||||||
if(revisionid > 0)
|
if(revisionid > 0)
|
||||||
{
|
{
|
||||||
indexcontent = createIndexContent(parser, revision->content);
|
indexcontent = createIndexContent(*parser, revision->content);
|
||||||
parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content, dynamicParseCallback);
|
parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -164,7 +153,7 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
indexcontent = createIndexContent(parser, revision->content);
|
indexcontent = createIndexContent(*parser, revision->content);
|
||||||
this->cache->put(cachekeyindexcontent, indexcontent);
|
this->cache->put(cachekeyindexcontent, indexcontent);
|
||||||
}
|
}
|
||||||
if(cachedparsedcontent)
|
if(cachedparsedcontent)
|
||||||
@ -173,23 +162,18 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content, dynamicParseCallback);
|
parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content);
|
||||||
this->cache->put(cachekeyparsedcontent, parsedcontent);
|
this->cache->put(cachekeyparsedcontent, parsedcontent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string revisionstr = std::to_string(revision->revision);
|
std::string revisionstr = std::to_string(revision->revision);
|
||||||
std::string customtitle = parser.extractCommand("pagetitle", revision->content);
|
|
||||||
page.setVar("content", parsedcontent);
|
page.setVar("content", parsedcontent);
|
||||||
page.setVar("index", indexcontent);
|
page.setVar("index", indexcontent);
|
||||||
page.setVar("editedby", revision->author);
|
page.setVar("editedby", revision->author);
|
||||||
page.setVar("editedon", utils::toISODateTime(revision->timestamp));
|
page.setVar("editedon", utils::toISODate(revision->timestamp));
|
||||||
page.setVar("historyurl", this->urlProvider->pageHistory(pagename));
|
page.setVar("historyurl", this->urlProvider->pageHistory(pagename));
|
||||||
page.setVar("revision", revisionstr);
|
page.setVar("revision", revisionstr);
|
||||||
setPageVars(page, pagename);
|
setPageVars(page, pagename);
|
||||||
if(!customtitle.empty())
|
|
||||||
{
|
|
||||||
page.setVar("title", createPageTitle(customtitle));
|
|
||||||
}
|
|
||||||
std::string body = page.render();
|
std::string body = page.render();
|
||||||
if(revisionid == 0 && !this->userSession->loggedIn)
|
if(revisionid == 0 && !this->userSession->loggedIn)
|
||||||
{
|
{
|
||||||
|
@ -25,11 +25,7 @@ Response HandlerSearch::handleRequest(const Request &r)
|
|||||||
std::string q = r.get("q");
|
std::string q = r.get("q");
|
||||||
if(q.empty())
|
if(q.empty())
|
||||||
{
|
{
|
||||||
TemplatePage searchForm = this->templ->getPage("searchform");
|
return errorResponse("Missing search term", "No search term supplied");
|
||||||
response.setBody(searchForm.render());
|
|
||||||
response.setStatus(200);
|
|
||||||
setGeneralVars(searchForm);
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pageDao = this->database->createPageDao();
|
auto pageDao = this->database->createPageDao();
|
||||||
@ -41,7 +37,7 @@ Response HandlerSearch::handleRequest(const Request &r)
|
|||||||
{
|
{
|
||||||
return errorResponse("No results", "Your search for " + q + " did not yield any results.");
|
return errorResponse("No results", "Your search for " + q + " did not yield any results.");
|
||||||
}
|
}
|
||||||
TemplatePage searchPage = this->templ->getPage("search");
|
TemplatePage &searchPage = this->templ->getPage("search");
|
||||||
std::string body = this->templ->renderSearch(resultList);
|
std::string body = this->templ->renderSearch(resultList);
|
||||||
searchPage.setVar("pagelist", body);
|
searchPage.setVar("pagelist", body);
|
||||||
searchPage.setVar("searchterm", q);
|
searchPage.setVar("searchterm", q);
|
||||||
|
@ -15,20 +15,19 @@ Response HandlerUserSettings::handleRequest(const Request &r)
|
|||||||
|
|
||||||
if(newpassword != newpasswordconfirm)
|
if(newpassword != newpasswordconfirm)
|
||||||
{
|
{
|
||||||
// TODO: is not nice, users has to hit the back button...
|
//TODO: is not nice, users has to hit the back button...
|
||||||
return this->errorResponse("Passwords don't match", "The entered new passwords don't match");
|
return this->errorResponse("Passwords don't match", "The entered new passwords don't match");
|
||||||
}
|
}
|
||||||
auto userDao = this->database->createUserDao();
|
auto userDao = this->database->createUserDao();
|
||||||
Authenticator authenticator(*userDao);
|
Authenticator authenticator(*userDao);
|
||||||
|
|
||||||
std::variant<User, AuthenticationError> authresult =
|
std::variant<User, AuthenticationError> authresult = authenticator.authenticate(this->userSession->user.login, oldpassword);
|
||||||
authenticator.authenticate(this->userSession->user.login, oldpassword);
|
|
||||||
if(std::holds_alternative<AuthenticationError>(authresult))
|
if(std::holds_alternative<AuthenticationError>(authresult))
|
||||||
{
|
{
|
||||||
return this->errorResponse("Invalid current password", "The old password you entered is invalid");
|
return this->errorResponse("Invalid current password", "The old password you entered is invalid");
|
||||||
}
|
}
|
||||||
Random r;
|
Random r;
|
||||||
std::vector<char> salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE);
|
std::vector<char> salt = r.getRandom(23);
|
||||||
User user = std::get<User>(authresult);
|
User user = std::get<User>(authresult);
|
||||||
user.salt = salt;
|
user.salt = salt;
|
||||||
user.password = authenticator.hash(newpassword, user.salt);
|
user.password = authenticator.hash(newpassword, user.salt);
|
||||||
@ -51,7 +50,7 @@ Response HandlerUserSettings::handleRequest(const Request &r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplatePage userSettingsPage = this->templ->getPage("usersettings");
|
TemplatePage &userSettingsPage = this->templ->getPage("usersettings");
|
||||||
setGeneralVars(userSettingsPage);
|
setGeneralVars(userSettingsPage);
|
||||||
userSettingsPage.setVar("usersettingsurl", urlProvider->userSettings());
|
userSettingsPage.setVar("usersettingsurl", urlProvider->userSettings());
|
||||||
userSettingsPage.setVar("title", createPageTitle("User settings - " + this->userSession->user.login));
|
userSettingsPage.setVar("title", createPageTitle("User settings - " + this->userSession->user.login));
|
||||||
@ -62,7 +61,7 @@ Response HandlerUserSettings::handleRequest(const Request &r)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerUserSettings::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerUserSettings::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return this->userSession->loggedIn;
|
return this->userSession->loggedIn;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#include "handlerversion.h"
|
|
||||||
#include "../version.h"
|
|
||||||
Response HandlerVersion::handleRequest([[maybe_unused]] const Request &r)
|
|
||||||
{
|
|
||||||
Response response;
|
|
||||||
response.setContentType("text/plain");
|
|
||||||
response.setBody(get_version_string());
|
|
||||||
return response;
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#ifndef HANDLERVERSION_H
|
|
||||||
#define HANDLERVERSION_H
|
|
||||||
#include "handler.h"
|
|
||||||
class HandlerVersion : public Handler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Handler::Handler;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Response handleRequest(const Request &r) override;
|
|
||||||
|
|
||||||
bool canAccess([[maybe_unused]] const Permissions &perms) override
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // HANDLERVERSION_H
|
|
24
iparser.h
24
iparser.h
@ -2,30 +2,22 @@
|
|||||||
#define IPARSER_H
|
#define IPARSER_H
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <functional>
|
|
||||||
#include "headline.h"
|
#include "headline.h"
|
||||||
#include "database/pagedao.h"
|
#include "database/pagedao.h"
|
||||||
#include "urlprovider.h"
|
#include "urlprovider.h"
|
||||||
|
|
||||||
|
|
||||||
class IParser
|
class IParser
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
static std::string empty(std::string_view key, std::string_view content)
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual std::string extractCommand(std::string cmdname, const std::string &content) const = 0;
|
virtual std::string extractCommand(std::string cmdname, std::string content) const = 0;
|
||||||
virtual std::vector<Headline> extractHeadlines(const std::string &content) const = 0;
|
virtual std::vector<Headline> extractHeadlines(std::string content) const = 0;
|
||||||
virtual inline std::string parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content) const
|
virtual std::string parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const = 0;
|
||||||
{
|
virtual std::vector<std::string> extractCategories(std::string content) const = 0;
|
||||||
return parse(pagedao, provider, content, empty);
|
|
||||||
}
|
|
||||||
virtual std::string parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content,
|
|
||||||
const std::function<std::string(std::string_view, std::string_view)> &callback) const = 0;
|
|
||||||
virtual std::vector<std::string> extractCategories(const std::string &content) const = 0;
|
|
||||||
|
|
||||||
virtual ~IParser(){};
|
virtual ~IParser(){};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // PARSER_H
|
#endif // PARSER_H
|
||||||
|
1
page.h
1
page.h
@ -7,7 +7,6 @@ class Page
|
|||||||
public:
|
public:
|
||||||
Page();
|
Page();
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string title;
|
|
||||||
bool listed;
|
bool listed;
|
||||||
unsigned int current_revision;
|
unsigned int current_revision;
|
||||||
unsigned int pageid;
|
unsigned int pageid;
|
||||||
|
22
parser.h
22
parser.h
@ -1,22 +0,0 @@
|
|||||||
#ifndef PARSER_H
|
|
||||||
#define PARSER_H
|
|
||||||
#include "iparser.h"
|
|
||||||
|
|
||||||
class Parser : public IParser
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string extractCommand(std::string cmdname, const std::string &content) const;
|
|
||||||
std::vector<Headline> extractHeadlines(const std::string &content) const override;
|
|
||||||
std::vector<std::string> extractCategories(const std::string &content) const override;
|
|
||||||
using IParser::parse;
|
|
||||||
virtual std::string parse(
|
|
||||||
const PageDao &pagedao, UrlProvider &provider, const std::string &content,
|
|
||||||
const std::function<std::string(std::string_view, std::string_view)> &callback) const override;
|
|
||||||
|
|
||||||
using IParser::IParser;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PARSER_H
|
|
@ -24,29 +24,22 @@ SOFTWARE.
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include "parser.h"
|
#include "parserlegacy.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "htmllink.h"
|
#include "htmllink.h"
|
||||||
std::vector<Headline> Parser::extractHeadlines(const std::string &content) const
|
std::vector<Headline> ParserLegacy::extractHeadlines(std::string content) const
|
||||||
{
|
{
|
||||||
std::vector<Headline> result;
|
std::vector<Headline> result;
|
||||||
std::string reg = R"(\[h(1|2|3)\](.*?)\[/h\1\])";
|
utils::regex_callback_extractor(std::regex(R"(\[h(1|2|3)\](.*?)\[/h\1\])"), content, [&](std::smatch &smatch) {
|
||||||
std::regex headerfinder(reg);
|
|
||||||
auto begin = std::sregex_iterator(content.begin(), content.end(), headerfinder);
|
|
||||||
auto end = std::sregex_iterator();
|
|
||||||
|
|
||||||
for(auto it = begin; it != end; it++)
|
|
||||||
{
|
|
||||||
auto smatch = *it;
|
|
||||||
Headline h;
|
Headline h;
|
||||||
h.level = utils::toUInt(smatch.str(1));
|
h.level = utils::toUInt(smatch.str(1));
|
||||||
h.title = smatch.str(2);
|
h.title = smatch.str(2);
|
||||||
result.push_back(h);
|
result.push_back(h);
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Parser::extractCategories(const std::string &content) const
|
std::vector<std::string> ParserLegacy::extractCategories(std::string content) const
|
||||||
{
|
{
|
||||||
std::vector<std::string> result;
|
std::vector<std::string> result;
|
||||||
std::string reg = R"(\[category\](.*?)\[/category\])";
|
std::string reg = R"(\[category\](.*?)\[/category\])";
|
||||||
@ -62,7 +55,7 @@ std::vector<std::string> Parser::extractCategories(const std::string &content) c
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Parser::extractCommand(std::string cmdname, const std::string &content) const
|
std::string ParserLegacy::extractCommand(std::string cmdname, std::string content) const
|
||||||
{
|
{
|
||||||
std::string cmd = "[cmd:" + cmdname + "]";
|
std::string cmd = "[cmd:" + cmdname + "]";
|
||||||
std::string cmdend = "[/cmd:" + cmdname + "]";
|
std::string cmdend = "[/cmd:" + cmdname + "]";
|
||||||
@ -81,7 +74,7 @@ std::string Parser::extractCommand(std::string cmdname, const std::string &conte
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::string Parser::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
|
std::string ParserLegacy::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
|
||||||
{
|
{
|
||||||
std::string linktag = match.str(1);
|
std::string linktag = match.str(1);
|
||||||
std::string inside = match.str(2);
|
std::string inside = match.str(2);
|
||||||
@ -116,37 +109,31 @@ std::string Parser::processLink(const PageDao &pageDao, UrlProvider &urlProvider
|
|||||||
return htmllink.render();
|
return htmllink.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Parser::parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content,
|
std::string ParserLegacy::parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const
|
||||||
const std::function<std::string(std::string_view, std::string_view)> &callback) const
|
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
// we don't care about commands, but we nevertheless replace them with empty strings
|
// we don't care about commands, but we nevertheless replace them with empty strings
|
||||||
std::regex tagfinder(
|
std::regex tagfinder(R"(\[(b|i|u|li||ul|ol|link|wikilink|h\d|cmd:rename|cmd:redirect|category)*?\]((\s|\S)*?)\[/\1])");
|
||||||
R"(\[(b|i|u|li||ul|ol|link|wikilink|h\d|cmd:rename|cmd:redirect|cmd:pagetitle|category|dynamic:postlist)*?\]((\s|\S)*?)\[/\1])");
|
result = utils::regex_callback_replacer(tagfinder, content, [&](std::smatch &match) {
|
||||||
result = utils::regex_callback_replacer(
|
std::string tag = match.str(1);
|
||||||
tagfinder, content,
|
std::string content = match.str(2);
|
||||||
[&](std::smatch &match)
|
std::string justreplace[] = {"b", "i", "u", "ul", "li", "ol"};
|
||||||
|
content = parse(pagedao, provider, content);
|
||||||
|
if(std::find(std::begin(justreplace), std::end(justreplace), tag) != std::end(justreplace))
|
||||||
{
|
{
|
||||||
std::string tag = match.str(1);
|
return "<" + tag + ">" + content + "</" + tag + ">";
|
||||||
std::string content = match.str(2);
|
}
|
||||||
std::string justreplace[] = {"b", "i", "u", "ul", "li", "ol"};
|
if(tag == "link" || tag == "wikilink")
|
||||||
content = parse(pagedao, provider, content, callback);
|
{
|
||||||
if(std::find(std::begin(justreplace), std::end(justreplace), tag) != std::end(justreplace))
|
return this->processLink(pagedao, provider,
|
||||||
{
|
match); // TODO: recreate this so we don't check inside the function stuff again
|
||||||
return "<" + tag + ">" + content + "</" + tag + ">";
|
}
|
||||||
}
|
if(tag[0] == 'h')
|
||||||
if(tag == "link" || tag == "wikilink")
|
{
|
||||||
{
|
return "<" + tag + " id='" + content + "'>" + content + "</" + tag + ">";
|
||||||
return this->processLink(
|
}
|
||||||
pagedao, provider,
|
return std::string("");
|
||||||
match); // TODO: recreate this so we don't check inside the function stuff again
|
});
|
||||||
}
|
|
||||||
if(tag[0] == 'h')
|
|
||||||
{
|
|
||||||
return "<" + tag + " id='" + content + "'>" + content + "</" + tag + ">";
|
|
||||||
}
|
|
||||||
return callback(tag, content);
|
|
||||||
});
|
|
||||||
result = utils::strreplace(result, "\r\n", "<br>");
|
result = utils::strreplace(result, "\r\n", "<br>");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
19
parserlegacy.h
Normal file
19
parserlegacy.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef PARSER_H
|
||||||
|
#define PARSER_H
|
||||||
|
#include "iparser.h"
|
||||||
|
|
||||||
|
class ParserLegacy : public IParser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string extractCommand(std::string cmdname, std::string content) const;
|
||||||
|
std::vector<Headline> extractHeadlines(std::string content) const override;
|
||||||
|
std::vector<std::string> extractCategories(std::string content) const override;
|
||||||
|
std::string parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const override;
|
||||||
|
using IParser::IParser;
|
||||||
|
~ParserLegacy(){};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PARSER_H
|
71
parsermarkdown.cpp
Normal file
71
parsermarkdown.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#include "parsermarkdown.h"
|
||||||
|
#include "logger.h"
|
||||||
|
#include "htmllink.h"
|
||||||
|
|
||||||
|
std::string ParserMarkdown::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
|
||||||
|
{
|
||||||
|
std::string inner = match.str(1);
|
||||||
|
std::string link = match.str(2);
|
||||||
|
HtmlLink htmllink;
|
||||||
|
htmllink.href = link;
|
||||||
|
htmllink.innervalue = inner;
|
||||||
|
|
||||||
|
if(link.find("http://") == 0 || link.find("https://") == 0)
|
||||||
|
{
|
||||||
|
return htmllink.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pageDao.exists(link))
|
||||||
|
{
|
||||||
|
htmllink.cssclass = "exists";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
htmllink.cssclass = "notexists";
|
||||||
|
}
|
||||||
|
|
||||||
|
htmllink.href = urlProvider.page(htmllink.href);
|
||||||
|
|
||||||
|
return htmllink.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserMarkdown::ParserMarkdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Headline> ParserMarkdown::extractHeadlines(std::string content) const
|
||||||
|
{
|
||||||
|
std::vector<Headline> result;
|
||||||
|
utils::regex_callback_extractor(std::regex(R"((#{1,6}) (.*))"), content, [&](std::smatch &smatch) {
|
||||||
|
Headline h;
|
||||||
|
h.level = smatch.str(1).length();
|
||||||
|
h.title = smatch.str(2);
|
||||||
|
result.push_back(h);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ParserMarkdown::parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const
|
||||||
|
{
|
||||||
|
std::shared_ptr<maddy::ParserConfig> config = std::make_shared<maddy::ParserConfig>();
|
||||||
|
auto maddy = std::make_shared<maddy::Parser>(config);
|
||||||
|
|
||||||
|
auto linkParser = std::make_shared<maddy::LinkParser>();
|
||||||
|
linkParser->setCallback([&](std::smatch &match) { return processLink(pagedao, provider, match); });
|
||||||
|
maddy->setLinkParser(linkParser);
|
||||||
|
// TODO: hack because the parser breaks if there is an \r
|
||||||
|
content = utils::strreplace(content, "\r", "");
|
||||||
|
std::stringstream s{content};
|
||||||
|
std::string result = maddy->Parse(s);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ParserMarkdown::extractCommand(std::string cmdname, std::string content) const
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ParserMarkdown::extractCategories(std::string content) const
|
||||||
|
{
|
||||||
|
return { };
|
||||||
|
}
|
21
parsermarkdown.h
Normal file
21
parsermarkdown.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef PARSER_MARKDOWN_H
|
||||||
|
#define PARSER_MARKDOWN_H
|
||||||
|
|
||||||
|
#include "iparser.h"
|
||||||
|
#include "maddy/parser.h"
|
||||||
|
|
||||||
|
class ParserMarkdown : public IParser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParserMarkdown();
|
||||||
|
std::vector<Headline> extractHeadlines(std::string content) const;
|
||||||
|
std::string parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const;
|
||||||
|
|
||||||
|
std::string extractCommand(std::string cmdname, std::string content) const;
|
||||||
|
std::vector<std::string> extractCategories(std::string content) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PARSER_MARKDOWN_H
|
@ -20,17 +20,6 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include "permissions.h"
|
#include "permissions.h"
|
||||||
|
|
||||||
static const std::map<std::string, int> permmap = {{"can_read", PERM_CAN_READ},
|
|
||||||
{"can_edit", PERM_CAN_EDIT},
|
|
||||||
{"can_page_history", PERM_CAN_PAGE_HISTORY},
|
|
||||||
{"can_global_history", PERM_CAN_GLOBAL_HISTORY},
|
|
||||||
{"can_delete", PERM_CAN_DELETE},
|
|
||||||
{"can_see_page_list", PERM_CAN_SEE_PAGE_LIST},
|
|
||||||
{"can_create", PERM_CAN_CREATE},
|
|
||||||
{"can_see_category_list", PERM_CAN_SEE_CATEGORY_LIST},
|
|
||||||
{"can_see_links_here", PERM_CAN_SEE_LINKS_HERE},
|
|
||||||
{"can_search", PERM_CAN_SEARCH}};
|
|
||||||
|
|
||||||
Permissions::Permissions(int permissions)
|
Permissions::Permissions(int permissions)
|
||||||
{
|
{
|
||||||
this->permissions = permissions;
|
this->permissions = permissions;
|
||||||
@ -47,20 +36,3 @@ Permissions::Permissions(const std::string &str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Permissions::toString(int perms)
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
for(auto pair : permmap)
|
|
||||||
{
|
|
||||||
if(pair.second & perms)
|
|
||||||
{
|
|
||||||
result += pair.first + ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(result.size() > 0)
|
|
||||||
{
|
|
||||||
result.pop_back();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -14,12 +14,20 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
class Permissions
|
class Permissions
|
||||||
|
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int permissions = 0;
|
int permissions = 0;
|
||||||
|
const std::map<std::string, int> permmap = {{"can_read", PERM_CAN_READ},
|
||||||
|
{"can_edit", PERM_CAN_EDIT},
|
||||||
|
{"can_page_history", PERM_CAN_PAGE_HISTORY},
|
||||||
|
{"can_global_history", PERM_CAN_GLOBAL_HISTORY},
|
||||||
|
{"can_delete", PERM_CAN_DELETE},
|
||||||
|
{"can_see_page_list", PERM_CAN_SEE_PAGE_LIST},
|
||||||
|
{"can_create", PERM_CAN_CREATE},
|
||||||
|
{"can_see_category_list", PERM_CAN_SEE_CATEGORY_LIST},
|
||||||
|
{"can_see_links_here", PERM_CAN_SEE_LINKS_HERE},
|
||||||
|
{"can_search", PERM_CAN_SEARCH}};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Permissions()
|
Permissions()
|
||||||
@ -94,13 +102,6 @@ class Permissions
|
|||||||
{
|
{
|
||||||
return this->permissions & PERM_CAN_SEE_PAGE_LIST;
|
return this->permissions & PERM_CAN_SEE_PAGE_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString() const
|
|
||||||
{
|
|
||||||
return Permissions::toString(this->permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string toString(int perms);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PERMISSIONS_H
|
#endif // PERMISSIONS_H
|
||||||
|
116
qswiki.cpp
116
qswiki.cpp
@ -25,7 +25,6 @@ SOFTWARE.
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <getopt.h>
|
|
||||||
#include "gateway/gatewayinterface.h"
|
#include "gateway/gatewayinterface.h"
|
||||||
#include "gateway/gatewayfactory.h"
|
#include "gateway/gatewayfactory.h"
|
||||||
#include "handlers/handlerfactory.h"
|
#include "handlers/handlerfactory.h"
|
||||||
@ -38,12 +37,11 @@ SOFTWARE.
|
|||||||
#include "requestworker.h"
|
#include "requestworker.h"
|
||||||
#include "cache/fscache.h"
|
#include "cache/fscache.h"
|
||||||
#include "sandbox/sandboxfactory.h"
|
#include "sandbox/sandboxfactory.h"
|
||||||
#include "cli.h"
|
#include "iparser.h"
|
||||||
#include "cliconsole.h"
|
#include "parserlegacy.h"
|
||||||
#include "cliserver.h"
|
#include "parsermarkdown.h"
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
void sigterm_handler([[maybe_unused]] int arg)
|
void sigterm_handler(int arg)
|
||||||
{
|
{
|
||||||
// TODO: proper shutdown.
|
// TODO: proper shutdown.
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@ -62,10 +60,6 @@ void setup_signal_handlers()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OPT_PRINT_VERSION 23
|
|
||||||
|
|
||||||
static struct option long_options[] = {{"cli", no_argument, 0, 'c'}, {"version", no_argument, 0, OPT_PRINT_VERSION}};
|
|
||||||
|
|
||||||
std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
|
std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -74,42 +68,23 @@ std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
|
|||||||
return std::make_unique<FsCache>(path);
|
return std::make_unique<FsCache>(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<IParser> createParser(const ConfigVariableResolver &resolver)
|
||||||
|
{
|
||||||
|
std::string parser = resolver.getConfig("parser");
|
||||||
|
if(parser == "legacy")
|
||||||
|
{
|
||||||
|
return std::make_unique<ParserLegacy>();
|
||||||
|
}
|
||||||
|
return std::make_unique<ParserMarkdown>();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
||||||
char *configfilepath = NULL;
|
|
||||||
int option;
|
|
||||||
int option_index;
|
|
||||||
bool cli_mode = false;
|
|
||||||
|
|
||||||
if(geteuid() == 0)
|
if(geteuid() == 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Do not run this as root!" << std::endl;
|
std::cerr << "Do not run this as root!" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while((option = getopt_long(argc, argv, "cv", long_options, &option_index)) != -1)
|
|
||||||
{
|
|
||||||
switch(option)
|
|
||||||
{
|
|
||||||
case 'c':
|
|
||||||
cli_mode = true;
|
|
||||||
break;
|
|
||||||
case OPT_PRINT_VERSION:
|
|
||||||
std::cout << get_version_string() << std::endl;
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(optind == argc)
|
|
||||||
{
|
|
||||||
std::cerr << "Missing config path" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
configfilepath = argv[optind++];
|
|
||||||
|
|
||||||
auto sandbox = createSandbox();
|
auto sandbox = createSandbox();
|
||||||
// TODO: do we want to keep it mandatory or configurable?
|
// TODO: do we want to keep it mandatory or configurable?
|
||||||
if(!sandbox->supported())
|
if(!sandbox->supported())
|
||||||
@ -117,18 +92,35 @@ int main(int argc, char **argv)
|
|||||||
Logger::error() << "Sandbox is not supported, exiting";
|
Logger::error() << "Sandbox is not supported, exiting";
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
if(!sandbox->enableForInit())
|
||||||
|
{
|
||||||
|
Logger::error() << "Sandboxing for init mode could not be activated.";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
if(argc < 2)
|
if(argc < 2)
|
||||||
{
|
{
|
||||||
std::cerr << "no path to config file provided" << std::endl;
|
std::cerr << "no path to config file provided" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
std::string configpath = std::filesystem::absolute(configfilepath).string();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConfigReader configreader(configpath);
|
ConfigReader configreader(argv[1]);
|
||||||
Config config = configreader.readConfig();
|
Config config = configreader.readConfig();
|
||||||
|
|
||||||
|
// TODO: config.connectiontring only works as long as we only support sqlite of course
|
||||||
|
if(!sandbox->enablePreWorker({
|
||||||
|
config.configVarResolver.getConfig("cache_fs_dir"),
|
||||||
|
config.templatepath,
|
||||||
|
std::filesystem::path(config.logfile).parent_path(),
|
||||||
|
std::filesystem::path(config.connectionstring).parent_path(),
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
Logger::error() << "Sandboxing for pre worker stage could not be activated.";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
setup_signal_handlers();
|
setup_signal_handlers();
|
||||||
|
|
||||||
std::fstream logstream;
|
std::fstream logstream;
|
||||||
@ -136,34 +128,6 @@ int main(int argc, char **argv)
|
|||||||
Logger::setStream(&logstream);
|
Logger::setStream(&logstream);
|
||||||
|
|
||||||
auto database = createDatabase(config);
|
auto database = createDatabase(config);
|
||||||
std::string socketPath = config.configVarResolver.getConfig("socketpath");
|
|
||||||
CLIHandler cliHandler(config, *database);
|
|
||||||
|
|
||||||
if(cli_mode)
|
|
||||||
{
|
|
||||||
CLIConsole console{cliHandler, socketPath};
|
|
||||||
console.startInteractive();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: config.connectiontring only works as long as we only support sqlite of course
|
|
||||||
if(!sandbox->enable({
|
|
||||||
config.configVarResolver.getConfig("cache_fs_dir"),
|
|
||||||
config.templatepath,
|
|
||||||
std::filesystem::path(config.logfile).parent_path(),
|
|
||||||
std::filesystem::path(config.connectionstring).parent_path(),
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
Logger::error() << "Sandboxing for worker could not be enabled!";
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
CLIServer cliServer{cliHandler};
|
|
||||||
if(!cliServer.detachServer(socketPath))
|
|
||||||
{
|
|
||||||
Logger::error() << "Error: Failed to detach unix socket server";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: quite ugly, anon-handling must be rethought
|
// TODO: quite ugly, anon-handling must be rethought
|
||||||
auto userdao = database->createUserDao();
|
auto userdao = database->createUserDao();
|
||||||
@ -180,19 +144,25 @@ int main(int argc, char **argv)
|
|||||||
userdao->save(anon.value());
|
userdao->save(anon.value());
|
||||||
User::setAnon(anon.value());
|
User::setAnon(anon.value());
|
||||||
|
|
||||||
MapCache<TemplatePage> mapCache;
|
Template siteTemplate{config.templateprefix, config.templatepath, config.urls, config.configVarResolver};
|
||||||
Template siteTemplate{config.templateprefix, config.templatepath, config.urls, config.configVarResolver,
|
|
||||||
mapCache};
|
|
||||||
UrlProvider urlProvider{config.urls};
|
UrlProvider urlProvider{config.urls};
|
||||||
|
|
||||||
auto cache = createCache(config.configVarResolver);
|
auto cache = createCache(config.configVarResolver);
|
||||||
cache->clear();
|
cache->clear();
|
||||||
|
|
||||||
HandlerFactory handlerFactory{config.handlersConfig, siteTemplate, *database.get(), urlProvider, *cache.get()};
|
auto parser = createParser(config.configVarResolver);
|
||||||
|
|
||||||
|
|
||||||
|
HandlerFactory handlerFactory{config.handlersConfig, siteTemplate, *database.get(), urlProvider, *cache.get(), *parser.get()};
|
||||||
RequestWorker requestWorker{handlerFactory, database->createSessionDao(), siteTemplate};
|
RequestWorker requestWorker{handlerFactory, database->createSessionDao(), siteTemplate};
|
||||||
|
|
||||||
auto interface = createGateway(config);
|
auto interface = createGateway(config);
|
||||||
|
|
||||||
|
if(!sandbox->enableForWorker())
|
||||||
|
{
|
||||||
|
Logger::error() << "Sandboxing for worker could not be enabled!";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
interface->work(requestWorker);
|
interface->work(requestWorker);
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
|
@ -59,7 +59,7 @@ Response RequestWorker::processRequest(const Request &r)
|
|||||||
if(session.loggedIn && session.csrf_token != r.post("csrf_token"))
|
if(session.loggedIn && session.csrf_token != r.post("csrf_token"))
|
||||||
{
|
{
|
||||||
// TODO: this is code duplication
|
// TODO: this is code duplication
|
||||||
TemplatePage error = this->templ->getPage("error");
|
TemplatePage &error = this->templ->getPage("error");
|
||||||
error.setVar("errortitle", "Invalid csrf token");
|
error.setVar("errortitle", "Invalid csrf token");
|
||||||
error.setVar("errormessage", "Invalid csrf token");
|
error.setVar("errormessage", "Invalid csrf token");
|
||||||
return {403, error.render()};
|
return {403, error.render()};
|
||||||
|
@ -32,12 +32,12 @@ Response::Response(int http_status_code, std::string html)
|
|||||||
this->html = std::move(html);
|
this->html = std::move(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::addHeader(std::string key, std::string value)
|
void Response::addHeader(const std::string &key, const std::string &value)
|
||||||
{
|
{
|
||||||
this->responseHeaders.insert(std::make_pair(key, value));
|
this->responseHeaders.insert(std::make_pair(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Response Response::redirectTemporarily(std::string url)
|
Response Response::redirectTemporarily(const std::string &url)
|
||||||
{
|
{
|
||||||
Response result;
|
Response result;
|
||||||
result.addHeader("Location", url);
|
result.addHeader("Location", url);
|
||||||
|
@ -27,8 +27,8 @@ class Response
|
|||||||
return this->html;
|
return this->html;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHeader(std::string key, std::string value);
|
void addHeader(const std::string &key, const std::string &value);
|
||||||
static Response redirectTemporarily(std::string url);
|
static Response redirectTemporarily(const std::string &url);
|
||||||
|
|
||||||
void setStatus(int status)
|
void setStatus(int status)
|
||||||
{
|
{
|
||||||
|
@ -12,13 +12,63 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
#include <exile.hpp>
|
#include <qssb.h>
|
||||||
#include "../logger.h"
|
#include "../logger.h"
|
||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
#include "../random.h"
|
#include "../random.h"
|
||||||
|
|
||||||
#include "sandbox-linux.h"
|
#include "sandbox-linux.h"
|
||||||
|
|
||||||
|
/* TODO: make a whitelist approach. So far we simply blacklist
|
||||||
|
* obvious systemcalls. To whitelist, we need to analyse our
|
||||||
|
* dependencies (http library, sqlite wrapper, sqlite lib etc.) */
|
||||||
|
|
||||||
|
bool SandboxLinux::enableForInit()
|
||||||
|
{
|
||||||
|
umask(0027);
|
||||||
|
struct qssb_policy policy = {0};
|
||||||
|
int blacklisted_syscalls[] = {QSSB_SYS(execveat), QSSB_SYS(execve), -1};
|
||||||
|
policy.blacklisted_syscalls = blacklisted_syscalls;
|
||||||
|
policy.no_new_privs = 1;
|
||||||
|
int result = qssb_enable_policy(&policy);
|
||||||
|
if(result != 0)
|
||||||
|
{
|
||||||
|
Logger::error() << "Failed to install sandboxing policy (init): " << result;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SandboxLinux::enablePreWorker(std::vector<std::string> fsPaths)
|
||||||
|
{
|
||||||
|
std::sort(fsPaths.begin(), fsPaths.end(),
|
||||||
|
[](const std::string &a, const std::string &b) { return a.length() < b.length(); });
|
||||||
|
|
||||||
|
struct qssb_path_policy *policies = new qssb_path_policy[fsPaths.size()];
|
||||||
|
for(unsigned int i = 0; i < fsPaths.size(); i++)
|
||||||
|
{
|
||||||
|
policies[i].next = policies + (i + 1);
|
||||||
|
policies[i].mountpoint = fsPaths[i].c_str();
|
||||||
|
policies[i].policy = QSSB_MOUNT_ALLOW_READ | QSSB_MOUNT_ALLOW_WRITE;
|
||||||
|
}
|
||||||
|
policies[fsPaths.size() - 1].next = NULL;
|
||||||
|
|
||||||
|
struct qssb_policy policy = {0};
|
||||||
|
policy.path_policies = policies;
|
||||||
|
policy.namespace_options |= QSSB_UNSHARE_MOUNT;
|
||||||
|
policy.namespace_options |= QSSB_UNSHARE_USER;
|
||||||
|
int blacklisted_syscalls[] = {QSSB_SYS(execveat), QSSB_SYS(execve), -1};
|
||||||
|
policy.blacklisted_syscalls = blacklisted_syscalls;
|
||||||
|
int result = qssb_enable_policy(&policy);
|
||||||
|
if(result != 0)
|
||||||
|
{
|
||||||
|
Logger::error() << "Failed to install sandboxing policy (preworker): %i" << result;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delete[] policies;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SandboxLinux::supported()
|
bool SandboxLinux::supported()
|
||||||
{
|
{
|
||||||
std::fstream stream;
|
std::fstream stream;
|
||||||
@ -36,35 +86,32 @@ bool SandboxLinux::supported()
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool SandboxLinux::enable(std::vector<std::string> fsPaths)
|
bool SandboxLinux::enableForWorker()
|
||||||
{
|
{
|
||||||
std::sort(fsPaths.begin(), fsPaths.end(),
|
struct qssb_policy policy = {0};
|
||||||
[](const std::string &a, const std::string &b) { return a.length() < b.length(); });
|
policy.drop_caps = 1;
|
||||||
|
policy.not_dumpable = 1;
|
||||||
|
policy.no_new_privs = 1;
|
||||||
|
|
||||||
struct exile_policy *policy = exile_init_policy();
|
/* TODO: as said, a whitelist approach is better. As such, this list is bound to be incomplete in the
|
||||||
if(policy == NULL)
|
* sense that more could be listed here and some critical ones are probably missing */
|
||||||
|
int blacklisted_syscalls[] = {QSSB_SYS(setuid),
|
||||||
|
QSSB_SYS(connect),
|
||||||
|
QSSB_SYS(chroot),
|
||||||
|
QSSB_SYS(pivot_root),
|
||||||
|
QSSB_SYS(mount),
|
||||||
|
QSSB_SYS(setns),
|
||||||
|
QSSB_SYS(unshare),
|
||||||
|
QSSB_SYS(ptrace),
|
||||||
|
QSSB_SYS(personality),
|
||||||
|
QSSB_SYS(prctl),
|
||||||
|
-1};
|
||||||
|
policy.blacklisted_syscalls = blacklisted_syscalls;
|
||||||
|
if(qssb_enable_policy(&policy) != 0)
|
||||||
{
|
{
|
||||||
Logger::error() << "Failed to init sandboxing policy (worker) ";
|
Logger::error() << "Sandbox: Activation of seccomp blacklist failed!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for(unsigned int i = 0; i < fsPaths.size(); i++)
|
|
||||||
{
|
|
||||||
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ | EXILE_FS_ALLOW_ALL_WRITE, fsPaths[i].c_str());
|
|
||||||
}
|
|
||||||
policy->drop_caps = 1;
|
|
||||||
policy->not_dumpable = 1;
|
|
||||||
policy->no_new_privs = 1;
|
|
||||||
policy->mount_path_policies_to_chroot = 1;
|
|
||||||
policy->vow_promises = EXILE_SYSCALL_VOW_STDIO | EXILE_SYSCALL_VOW_WPATH | EXILE_SYSCALL_VOW_CPATH |
|
|
||||||
EXILE_SYSCALL_VOW_RPATH | EXILE_SYSCALL_VOW_INET | EXILE_SYSCALL_VOW_UNIX |
|
|
||||||
EXILE_SYSCALL_VOW_THREAD;
|
|
||||||
|
|
||||||
if(exile_enable_policy(policy) != 0)
|
|
||||||
{
|
|
||||||
Logger::error() << "Sandbox: Activation of exile failed!";
|
|
||||||
exile_free_policy(policy);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
exile_free_policy(policy);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ class SandboxLinux : public Sandbox
|
|||||||
public:
|
public:
|
||||||
using Sandbox::Sandbox;
|
using Sandbox::Sandbox;
|
||||||
bool supported() override;
|
bool supported() override;
|
||||||
bool enable(std::vector<std::string> fsPaths) override;
|
bool enableForInit() override;
|
||||||
|
bool enablePreWorker(std::vector<std::string> fsPaths) override;
|
||||||
|
bool enableForWorker() override;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -6,6 +6,10 @@ class SandboxOpenBSD : public Sandbox
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool supported() override;
|
bool supported() override;
|
||||||
bool enable(std::vector<std::string> fsPaths) override;
|
bool enableForInit() override;
|
||||||
|
bool enableForWorker() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool seccomp_blacklist(std::vector<int> syscalls);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,7 +10,16 @@ class Sandbox
|
|||||||
/* Whether the platform has everything required to active all sandbnox modes */
|
/* Whether the platform has everything required to active all sandbnox modes */
|
||||||
virtual bool supported() = 0;
|
virtual bool supported() = 0;
|
||||||
|
|
||||||
/* Activated after we have acquired resources (bound to ports etc.)*/
|
/* Activated early. At this point, we need more system calls
|
||||||
virtual bool enable(std::vector<std::string> fsPaths) = 0;
|
* than later on */
|
||||||
|
virtual bool enableForInit() = 0;
|
||||||
|
|
||||||
|
/* Activated after config has been read. Now we now which paths we need access to */
|
||||||
|
virtual bool enablePreWorker(std::vector<std::string> fsPaths) = 0;
|
||||||
|
|
||||||
|
/* Activated after we have acquired resources (bound to ports etc.)
|
||||||
|
*
|
||||||
|
* This should allow us to further restrcit the process */
|
||||||
|
virtual bool enableForWorker() = 0;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
CREATE TABLE page(id INTEGER PRIMARY KEY, name varchar(256), title varchar(1024), lastrevision integer, visible integer DEFAULT 1);
|
CREATE TABLE page(id INTEGER PRIMARY KEY, name varchar(256), lastrevision integer, visible integer DEFAULT 1);
|
||||||
CREATE TABLE user(id INTEGER PRIMARY KEY,username varchar(64),
|
CREATE TABLE user(id INTEGER PRIMARY KEY,username varchar(64),
|
||||||
password blob, salt blob, permissions integer, enabled integer DEFAULT 1);
|
password blob, salt blob, permissions integer, enabled integer DEFAULT 1);
|
||||||
CREATE TABLE session(id INTEGER PRIMARY KEY, csrf_token varchar(32),
|
CREATE TABLE session(id INTEGER PRIMARY KEY, csrf_token varchar(32),
|
||||||
@ -25,13 +25,24 @@ count integer
|
|||||||
CREATE TABLE category(id INTEGER PRIMARY KEY, name varchar(255));
|
CREATE TABLE category(id INTEGER PRIMARY KEY, name varchar(255));
|
||||||
CREATE TABLE categorymember(id INTEGER PRIMARY KEY, category REFERENCES category(id), page REFERENCES page (id));
|
CREATE TABLE categorymember(id INTEGER PRIMARY KEY, category REFERENCES category(id), page REFERENCES page (id));
|
||||||
CREATE INDEX revisionid ON revision (revisionid DESC);
|
CREATE INDEX revisionid ON revision (revisionid DESC);
|
||||||
CREATE INDEX pagename ON page (name);
|
CREATE INDEX pagename ON page (name)
|
||||||
CREATE INDEX token ON session (token);
|
;
|
||||||
CREATE VIRTUAL TABLE search USING fts5(content, page UNINDEXED, content=revision,content_rowid=id);
|
CREATE INDEX token ON session (token)
|
||||||
|
;
|
||||||
|
CREATE TRIGGER search_ai AFTER INSERT ON revision BEGIN
|
||||||
|
DELETE FROM search WHERE page = new.page;
|
||||||
|
INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page);
|
||||||
|
END;
|
||||||
|
CREATE TRIGGER search_au AFTER UPDATE ON revision BEGIN
|
||||||
|
DELETE FROM search WHERE page = old.page;
|
||||||
|
INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page);
|
||||||
|
END;
|
||||||
|
CREATE VIRTUAL TABLE search USING fts5(content, page UNINDEXED, content=revision,content_rowid=id)
|
||||||
|
/* search(content,page) */;
|
||||||
|
CREATE TABLE IF NOT EXISTS 'search_data'(id INTEGER PRIMARY KEY, block BLOB);
|
||||||
|
CREATE TABLE IF NOT EXISTS 'search_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
|
||||||
|
CREATE TABLE IF NOT EXISTS 'search_docsize'(id INTEGER PRIMARY KEY, sz BLOB);
|
||||||
|
CREATE TABLE IF NOT EXISTS 'search_config'(k PRIMARY KEY, v) WITHOUT ROWID;
|
||||||
CREATE TRIGGER search_ad AFTER DELETE ON revision BEGIN
|
CREATE TRIGGER search_ad AFTER DELETE ON revision BEGIN
|
||||||
INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page);
|
INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page);
|
||||||
END;
|
END;
|
||||||
CREATE TRIGGER search_ai AFTER INSERT ON revision BEGIN
|
|
||||||
INSERT INTO search(search, rowid, content, page) SELECT 'delete', id, content, page FROM revision WHERE page = new.page AND revisionid = new.revisionid - 1;
|
|
||||||
INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page);
|
|
||||||
END;
|
|
||||||
|
Submodule submodules/cpp-httplib updated: b324921c1a...63643e6386
Submodule submodules/exile.h deleted from f2ca26010a
1
submodules/qsmaddy
Submodule
1
submodules/qsmaddy
Submodule
Submodule submodules/qsmaddy added at 167ce3943d
1
submodules/qssb.h
Submodule
1
submodules/qssb.h
Submodule
Submodule submodules/qssb.h added at 9df2e9ee90
39
template.cpp
39
template.cpp
@ -24,25 +24,12 @@ SOFTWARE.
|
|||||||
#include "htmllink.h"
|
#include "htmllink.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
Template::Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls,
|
Template::Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls,
|
||||||
ConfigVariableResolver &configVarsResolver, MapCache<TemplatePage> &pageCache)
|
ConfigVariableResolver &configVarsResolver)
|
||||||
{
|
{
|
||||||
this->templateprefix = templateprefix;
|
this->templateprefix = templateprefix;
|
||||||
this->templatepath = templatepath;
|
this->templatepath = templatepath;
|
||||||
this->configUrls = &configUrls;
|
this->configUrls = &configUrls;
|
||||||
this->configVarResolver = &configVarsResolver;
|
this->configVarResolver = &configVarsResolver;
|
||||||
this->pageCache = &pageCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
TemplatePage Template::getPage(const std::string &pagename)
|
|
||||||
{
|
|
||||||
auto result = this->pageCache->find(pagename);
|
|
||||||
if(result)
|
|
||||||
{
|
|
||||||
return *result;
|
|
||||||
}
|
|
||||||
auto page = createPage(pagename);
|
|
||||||
this->pageCache->set(pagename, page);
|
|
||||||
return page;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Template::getPartPath(std::string_view partname)
|
std::string Template::getPartPath(std::string_view partname)
|
||||||
@ -67,7 +54,7 @@ std::string Template::resolveIncludes(std::string_view content)
|
|||||||
return replacer.parse(content);
|
return replacer.parse(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TemplatePage Template::createPage(std::string_view name)
|
TemplatePage Template::createPage(std::string name)
|
||||||
{
|
{
|
||||||
std::string content = loadResolvedPart(name);
|
std::string content = loadResolvedPart(name);
|
||||||
Varreplacer replacer(this->templateprefix);
|
Varreplacer replacer(this->templateprefix);
|
||||||
@ -78,6 +65,16 @@ TemplatePage Template::createPage(std::string_view name)
|
|||||||
return TemplatePage(replacer.parse(content));
|
return TemplatePage(replacer.parse(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TemplatePage &Template::getPage(const std::string &pagename)
|
||||||
|
{
|
||||||
|
if(utils::hasKey(pagesMap, pagename))
|
||||||
|
{
|
||||||
|
return pagesMap[pagename];
|
||||||
|
}
|
||||||
|
pagesMap.insert(std::make_pair(pagename, createPage(pagename)));
|
||||||
|
return pagesMap[pagename];
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this restricts template a bit
|
// TODO: this restricts template a bit
|
||||||
std::string Template::renderSearch(const std::vector<std::string> &results,
|
std::string Template::renderSearch(const std::vector<std::string> &results,
|
||||||
std::function<std::string(std::string)> callback) const
|
std::function<std::string(std::string)> callback) const
|
||||||
@ -133,20 +130,20 @@ std::string Template::renderRevisionList(const std::vector<Revision> &revisions,
|
|||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
UrlProvider urlprovider(*this->configUrls);
|
UrlProvider urlprovider(*this->configUrls);
|
||||||
|
|
||||||
auto genwithoutpage = [&]
|
auto genwithoutpage = [&] {
|
||||||
{
|
|
||||||
for(const Revision &revision : revisions)
|
for(const Revision &revision : revisions)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
Logger::debug() << "processing: " << revision.revision;
|
||||||
stream << "<tr><td><a href=\"" << urlprovider.pageRevision(revision.page, revision.revision) << "\">"
|
stream << "<tr><td><a href=\"" << urlprovider.pageRevision(revision.page, revision.revision) << "\">"
|
||||||
<< revision.revision << "</a></td>"
|
<< revision.revision << "</a></td>"
|
||||||
<< "<td>" << revision.author << "</td>"
|
<< "<td>" << revision.author << "</td>"
|
||||||
<< "<td>" << revision.comment << "</td>"
|
<< "<td>" << revision.comment << "</td>"
|
||||||
<< "<td>" << utils::toISODateTime(revision.timestamp) << "</td></tr>";
|
<< "<td>" << utils::toISODate(revision.timestamp) << "</td></tr>";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto genwithpage = [&]
|
auto genwithpage = [&] {
|
||||||
{
|
|
||||||
for(const Revision &revision : revisions)
|
for(const Revision &revision : revisions)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -155,7 +152,7 @@ std::string Template::renderRevisionList(const std::vector<Revision> &revisions,
|
|||||||
<< "<td>" << revision.revision << "</td>"
|
<< "<td>" << revision.revision << "</td>"
|
||||||
<< "<td>" << revision.author << "</td>"
|
<< "<td>" << revision.author << "</td>"
|
||||||
<< "<td>" << revision.comment << "</td>"
|
<< "<td>" << revision.comment << "</td>"
|
||||||
<< "<td>" << utils::toISODateTime(revision.timestamp) << "</td></tr>";
|
<< "<td>" << utils::toISODate(revision.timestamp) << "</td></tr>";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
16
template.h
16
template.h
@ -8,29 +8,31 @@
|
|||||||
#include "response.h"
|
#include "response.h"
|
||||||
#include "searchresult.h"
|
#include "searchresult.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "cache/mapcache.h"
|
|
||||||
class Template
|
class Template
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
ConfigVariableResolver *configVarResolver;
|
ConfigVariableResolver *configVarResolver;
|
||||||
ConfigUrls *configUrls;
|
ConfigUrls *configUrls;
|
||||||
MapCache<TemplatePage> *pageCache;
|
|
||||||
|
|
||||||
std::string templateprefix;
|
std::string templateprefix;
|
||||||
std::string templatepath;
|
std::string templatepath;
|
||||||
|
|
||||||
|
std::map<std::string, TemplatePage> pagesMap;
|
||||||
std::string resolveIncludes(std::string_view content);
|
std::string resolveIncludes(std::string_view content);
|
||||||
|
|
||||||
std::string getPartPath(std::string_view partname);
|
std::string getPartPath(std::string_view partname);
|
||||||
|
std::string loadResolvedPart(std::string_view partname);
|
||||||
std::string loadPartContent(std::string_view partname);
|
std::string loadPartContent(std::string_view partname);
|
||||||
TemplatePage createPage(std::string_view name);
|
TemplatePage createPage(std::string name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls,
|
Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls,
|
||||||
ConfigVariableResolver &configVarsResolver, MapCache<TemplatePage> &pageCache);
|
ConfigVariableResolver &configVarsResolver);
|
||||||
|
/* TODO: returning this as a reference is by no means a risk free business,
|
||||||
TemplatePage getPage(const std::string &pagename);
|
because between requests, different vars can be set conditionally,
|
||||||
std::string loadResolvedPart(std::string_view partname);
|
thus creating a mess
|
||||||
|
*/
|
||||||
|
TemplatePage &getPage(const std::string &pagename);
|
||||||
|
|
||||||
std::string renderSearch(const std::vector<std::string> &results,
|
std::string renderSearch(const std::vector<std::string> &results,
|
||||||
std::function<std::string(std::string)> callback) const;
|
std::function<std::string(std::string)> callback) const;
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<ul>
|
|
@ -1 +0,0 @@
|
|||||||
</ul>
|
|
@ -1 +0,0 @@
|
|||||||
<li>{date}: <a href="{url}">{title}</a></li>
|
|
@ -1,7 +0,0 @@
|
|||||||
<entry>
|
|
||||||
<title>{qswiki:var:entrytitle}</title>
|
|
||||||
<link href="{qswiki:var:entryurl}"/>
|
|
||||||
<id>{qswiki:var:entryid}</id>
|
|
||||||
<published>{qswiki:var:entrypublished}</published>
|
|
||||||
<content type="html">{qswiki:var:entrycontent}</content>
|
|
||||||
</entry>
|
|
@ -1 +0,0 @@
|
|||||||
</feed>
|
|
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
|
||||||
<author>
|
|
||||||
<name>{qswiki:config:wikiownername}</name>
|
|
||||||
</author>
|
|
||||||
<title>{qswiki:config:wikiname} - {qswiki:var:subtitle}</title>
|
|
||||||
<id>{qswiki:var:atomfeeduniqueid}</id>
|
|
||||||
<updated>{qswiki:var:atomfeedupdate}</updated>
|
|
@ -6,15 +6,16 @@
|
|||||||
<title>{qswiki:var:title}</title>
|
<title>{qswiki:var:title}</title>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
<ul id="nav">
|
<ul>
|
||||||
<li><a href="{qswiki:config:linkindex}"><h2>{qswiki:config:wikiname}</h2></a></li>
|
<li><a href="{qswiki:config:linkindex}"><h2>{qswiki:config:wikiname}</h2></a></li>
|
||||||
<li><a href="{qswiki:config:linkrecent}">Recent changes</a></li>
|
|
||||||
<li><a href="{qswiki:config:linkallpages}">All pages</a></li>
|
|
||||||
<li><a href="{qswiki:config:linkallcats}">All categories</a></li>
|
|
||||||
<li id="searchlink"><a href="{qswiki:config:linksearch}">Search</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul id="nav">
|
||||||
|
<li><a href="{qswiki:config:linkrecent}">Recent changes</a></li>
|
||||||
|
<li><a href="{qswiki:config:linkallpages}">All pages</a></li>
|
||||||
|
<li><a href="{qswiki:config:linkallcats}">All categories</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<ul id="right" class="search">
|
<ul id="right" class="search">
|
||||||
<li><div id="searchbar"><form action="{qswiki:config:wikipath}" method="GET"><input type="hidden" name="action" value="search"/><input type="text" name="q" value="search here" onfocus="this.value=''"></form></div></li>
|
<li><div><form action="{qswiki:config:wikipath}" method="GET"><input type="hidden" name="action" value="search"/><input type="text" name="q" value="search here" onfocus="this.value=''"></form></div></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -6,15 +6,20 @@
|
|||||||
<title>{qswiki:var:title}</title>
|
<title>{qswiki:var:title}</title>
|
||||||
<body>
|
<body>
|
||||||
<nav>
|
<nav>
|
||||||
<ul id="nav">
|
<ul>
|
||||||
<li><a href="{qswiki:config:linkindex}"><h2>{qswiki:config:wikiname}</h2></a></li>
|
<li><a href="{qswiki:config:linkindex}"><h2>{qswiki:config:wikiname}</h2></a></li>
|
||||||
|
</ul>
|
||||||
|
<ul id="nav">
|
||||||
<li><a href="{qswiki:config:linkrecent}">Recent changes</a></li>
|
<li><a href="{qswiki:config:linkrecent}">Recent changes</a></li>
|
||||||
<li><a href="{qswiki:config:linkallpages}">All pages</a></li>
|
<li><a href="{qswiki:config:linkallpages}">All pages</a></li>
|
||||||
<li><a href="{qswiki:config:linkallcats}">All categories</a></li>
|
<li><a href="{qswiki:config:linkallcats}">All categories</a></li>
|
||||||
<li id="searchlink"><a href="{qswiki:config:linksearch}">Search</a></li>
|
|
||||||
{qswiki:var:headerlinks}
|
|
||||||
</ul>
|
</ul>
|
||||||
<ul id="right" class="search">
|
|
||||||
<li><div id="searchbar"><form action="{qswiki:config:wikipath}" method="GET"><input type="hidden" name="action" value="search"/><input type="text" value="search here" onfocus="this.value=''" name="q"/></form></div></li>
|
<ul>
|
||||||
|
{qswiki:var:headerlinks}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
|
||||||
|
<ul id="right" class="search">
|
||||||
|
<li><div><form action="{qswiki:config:wikipath}" method="GET"><input type="hidden" name="action" value="search"/><input type="text" value="search here" onfocus="this.value=''" name="q"/></form></div></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
@ -1,7 +0,0 @@
|
|||||||
{qswiki:include:general_header}
|
|
||||||
<main id="content">
|
|
||||||
<h2>Search</h2><br>
|
|
||||||
Search content of pages:
|
|
||||||
<form action="{qswiki:config:wikipath}" method="GET"><input type="hidden" name="action" value="search"/><input type="text" name="q" value="search here" onfocus="this.value=''"></form>
|
|
||||||
</main>
|
|
||||||
{qswiki:include:general_footer}
|
|
@ -23,7 +23,7 @@ h1, h2, h3
|
|||||||
{
|
{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav
|
nav
|
||||||
@ -37,7 +37,6 @@ nav
|
|||||||
grid-area: nav;
|
grid-area: nav;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav ul
|
nav ul
|
||||||
{
|
{
|
||||||
background-color: #062463;
|
background-color: #062463;
|
||||||
@ -48,12 +47,16 @@ nav ul
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
nav li
|
nav li
|
||||||
{
|
{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a, nav a:visited
|
nav a, nav a:visited
|
||||||
@ -65,6 +68,7 @@ nav a, nav a:visited
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav a:hover, nav a:focus
|
nav a:hover, nav a:focus
|
||||||
@ -77,6 +81,8 @@ nav a:hover, nav a:focus
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
a, a:visited
|
a, a:visited
|
||||||
{
|
{
|
||||||
color: #062463;;
|
color: #062463;;
|
||||||
@ -86,36 +92,40 @@ a:hover
|
|||||||
{
|
{
|
||||||
background-color: #062463;
|
background-color: #062463;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#content
|
#content
|
||||||
{
|
{
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 14pt;
|
font-size: 14pt;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
grid-area: main
|
grid-area: main
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar
|
#sidebar
|
||||||
{
|
{
|
||||||
grid-area: side;
|
grid-area: side;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar ul
|
#sidebar ul
|
||||||
{
|
{
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar a, a:visited
|
#sidebar a, a:visited
|
||||||
{
|
{
|
||||||
color: #062463;
|
color: #062463;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar a:hover
|
#sidebar a:hover
|
||||||
{
|
{
|
||||||
background-color: #062463;
|
background-color: #062463;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content a, a:visited
|
#content a, a:visited
|
||||||
@ -125,10 +135,11 @@ a:hover
|
|||||||
|
|
||||||
#content a:hover
|
#content a:hover
|
||||||
{
|
{
|
||||||
background-color: #062463;
|
background-color: #062463;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
footer
|
footer
|
||||||
{
|
{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -149,7 +160,6 @@ footer ul
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer li
|
footer li
|
||||||
{
|
{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -158,12 +168,14 @@ footer li
|
|||||||
line-height: 45px;
|
line-height: 45px;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
//flex: 1 1 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer a, a:visited
|
footer a, a:visited
|
||||||
{
|
{
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
color: white;
|
color: white;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@ -178,7 +190,7 @@ footer a:hover, ul#nav a:focus
|
|||||||
|
|
||||||
#cats
|
#cats
|
||||||
{
|
{
|
||||||
background-color: #062463;
|
background-color: #062463;
|
||||||
}
|
}
|
||||||
|
|
||||||
.letter_search_result
|
.letter_search_result
|
||||||
@ -186,27 +198,20 @@ footer a:hover, ul#nav a:focus
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol
|
ol
|
||||||
{
|
{
|
||||||
counter-reset: item;
|
counter-reset: item;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indexlink
|
.indexlink
|
||||||
{
|
{
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notexists
|
.notexists
|
||||||
{
|
{
|
||||||
color: red !important;
|
color: red !important;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchlink
|
|
||||||
{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (orientation: portrait)
|
@media screen and (orientation: portrait)
|
||||||
{
|
{
|
||||||
@ -214,23 +219,13 @@ ol
|
|||||||
{
|
{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#footer li:nth-child(-n+2)
|
#footer li:nth-child(-n+2)
|
||||||
{
|
{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#footer li:nth-of-type(3)
|
#footer li:nth-of-type(3)
|
||||||
{
|
{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#searchlink {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ TemplatePage::TemplatePage()
|
|||||||
|
|
||||||
TemplatePage::TemplatePage(std::string content)
|
TemplatePage::TemplatePage(std::string content)
|
||||||
{
|
{
|
||||||
this->content = std::make_shared<std::string>(content);
|
this->content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TemplatePage::setVar(const std::string &key, std::string value)
|
void TemplatePage::setVar(const std::string &key, std::string value)
|
||||||
@ -40,5 +40,5 @@ std::string TemplatePage::render() const
|
|||||||
Varreplacer replacer("{qswiki:");
|
Varreplacer replacer("{qswiki:");
|
||||||
replacer.addResolver("var",
|
replacer.addResolver("var",
|
||||||
[&](std::string_view key) { return utils::getKeyOrEmpty(this->varsMap, std::string(key)); });
|
[&](std::string_view key) { return utils::getKeyOrEmpty(this->varsMap, std::string(key)); });
|
||||||
return replacer.parse(*this->content);
|
return replacer.parse(this->content);
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
|
||||||
class TemplatePage
|
class TemplatePage
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<const std::string> content;
|
std::string content;
|
||||||
std::map<std::string, std::string> varsMap;
|
std::map<std::string, std::string> varsMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -18,7 +18,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include <string_view>
|
|
||||||
#include "urlprovider.h"
|
#include "urlprovider.h"
|
||||||
|
|
||||||
std::string replaceSingleVar(std::string where, std::string varname, std::string replacement)
|
std::string replaceSingleVar(std::string where, std::string varname, std::string replacement)
|
||||||
@ -120,28 +119,3 @@ std::string UrlProvider::login(std::string page)
|
|||||||
{
|
{
|
||||||
return replaceOnlyPage(config->loginurl, page);
|
return replaceOnlyPage(config->loginurl, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UrlProvider::rootUrl()
|
|
||||||
{
|
|
||||||
return config->rooturl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UrlProvider::atomFeed(std::string filter)
|
|
||||||
{
|
|
||||||
return combine({config->rooturl, replaceSingleVar(config->atomurl, "filter", filter)});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UrlProvider::combine(std::initializer_list<std::string> urls)
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
for(const std::string &url : urls)
|
|
||||||
{
|
|
||||||
std::string_view urlview{url};
|
|
||||||
if(result.back() == '/' && urlview.front() == '/')
|
|
||||||
{
|
|
||||||
urlview.remove_prefix(1);
|
|
||||||
}
|
|
||||||
result += urlview;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -48,12 +48,6 @@ class UrlProvider
|
|||||||
std::string category(std::string catname);
|
std::string category(std::string catname);
|
||||||
|
|
||||||
std::string login(std::string page);
|
std::string login(std::string page);
|
||||||
|
|
||||||
std::string rootUrl();
|
|
||||||
|
|
||||||
std::string atomFeed(std::string filter);
|
|
||||||
|
|
||||||
std::string combine(std::initializer_list<std::string> urls);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LINKCREATOR_H
|
#endif // LINKCREATOR_H
|
||||||
|
52
utils.cpp
52
utils.cpp
@ -46,12 +46,6 @@ std::string utils::html_xss(std::string_view str)
|
|||||||
case '%':
|
case '%':
|
||||||
result += "%";
|
result += "%";
|
||||||
break;
|
break;
|
||||||
case '\'':
|
|
||||||
result += "'";
|
|
||||||
break;
|
|
||||||
case '&':
|
|
||||||
result += "&";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
result += c;
|
result += c;
|
||||||
}
|
}
|
||||||
@ -84,7 +78,7 @@ std::string utils::urldecode(std::string_view str)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> utils::split(std::string str, char delim)
|
std::vector<std::string> utils::split(const std::string &str, char delim)
|
||||||
{
|
{
|
||||||
std::vector<std::string> result;
|
std::vector<std::string> result;
|
||||||
std::stringstream stream(str);
|
std::stringstream stream(str);
|
||||||
@ -97,9 +91,9 @@ std::vector<std::string> utils::split(std::string str, char delim)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can easily break if we pass a regex here
|
// TODO: can easily break if we pass a regex here
|
||||||
std::vector<std::string> utils::split(std::string str, const std::string &delim)
|
std::vector<std::string> utils::split(const std::string &str, const std::string &delim)
|
||||||
{
|
{
|
||||||
std::regex regex{delim + "+"};
|
std::regex regex { delim + "+" };
|
||||||
return split(str, regex);
|
return split(str, regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +106,7 @@ std::vector<std::string> utils::split(const std::string &str, std::regex ®ex)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string utils::strreplace(std::string str, const std::string &search, const std::string &replace)
|
std::string utils::strreplace(const std::string &str, const std::string &search, const std::string &replace)
|
||||||
{
|
{
|
||||||
std::string result = str;
|
std::string result = str;
|
||||||
auto searchlength = search.length();
|
auto searchlength = search.length();
|
||||||
@ -138,10 +132,6 @@ std::string utils::readCompleteFile(std::string_view filepath)
|
|||||||
{
|
{
|
||||||
|
|
||||||
std::fstream stream(std::string{filepath});
|
std::fstream stream(std::string{filepath});
|
||||||
if(!stream.is_open())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("stream is not open");
|
|
||||||
}
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << stream.rdbuf();
|
ss << stream.rdbuf();
|
||||||
std::string content = ss.str();
|
std::string content = ss.str();
|
||||||
@ -170,10 +160,7 @@ std::string utils::regex_callback_replacer(std::regex regex, const std::string &
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Convert to C++20, but currently the state is rather poor and would
|
std::string utils::toISODate(time_t t)
|
||||||
* require workarounds, so keep it this way for now, and do it properly
|
|
||||||
* once compiler support gets there */
|
|
||||||
std::string utils::formatLocalDate(time_t t, std::string format)
|
|
||||||
{
|
{
|
||||||
struct tm lt;
|
struct tm lt;
|
||||||
if(localtime_r(&t, <) == nullptr)
|
if(localtime_r(&t, <) == nullptr)
|
||||||
@ -181,7 +168,7 @@ std::string utils::formatLocalDate(time_t t, std::string format)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
char result[20];
|
char result[20];
|
||||||
size_t x = strftime(result, sizeof(result), format.c_str(), <);
|
size_t x = strftime(result, sizeof(result), "%Y-%m-%d %H:%M:%S", <);
|
||||||
if(x == 0)
|
if(x == 0)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
@ -189,28 +176,13 @@ std::string utils::formatLocalDate(time_t t, std::string format)
|
|||||||
return std::string{result};
|
return std::string{result};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string utils::toISODateTime(time_t t)
|
void utils::regex_callback_extractor(std::regex regex, const std::string &input, std::function<void (std::smatch &)> callback)
|
||||||
{
|
{
|
||||||
return utils::formatLocalDate(t, "%Y-%m-%d %H:%M:%S");
|
auto begin = std::sregex_iterator(input.begin(), input.end(), regex);
|
||||||
}
|
auto end = std::sregex_iterator();
|
||||||
|
for(auto it = begin; it != end; it++)
|
||||||
std::string utils::toISODate(time_t t)
|
|
||||||
{
|
|
||||||
return utils::formatLocalDate(t, "%Y-%m-%d");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string utils::trim(std::string_view view)
|
|
||||||
{
|
|
||||||
std::string_view chars = " \t\n\r";
|
|
||||||
auto n = view.find_first_not_of(chars);
|
|
||||||
if(n != std::string_view::npos)
|
|
||||||
{
|
{
|
||||||
view.remove_prefix(n);
|
std::smatch smatch = *it;
|
||||||
|
callback(smatch);
|
||||||
}
|
}
|
||||||
n = view.find_last_not_of(chars);
|
|
||||||
if(n != std::string_view::npos)
|
|
||||||
{
|
|
||||||
view.remove_suffix(view.size() - n - 1);
|
|
||||||
}
|
|
||||||
return std::string{view};
|
|
||||||
}
|
}
|
||||||
|
24
utils.h
24
utils.h
@ -8,20 +8,25 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <limits>
|
|
||||||
namespace utils
|
namespace utils
|
||||||
{
|
{
|
||||||
|
|
||||||
std::vector<std::string> split(std::string str, char delim);
|
std::vector<std::string> split(const std::string &str, char delim);
|
||||||
std::vector<std::string> split(std::string str, const std::string &delim);
|
std::vector<std::string> split(const std::string &str, const std::string &delim);
|
||||||
std::vector<std::string> split(const std::string &str, std::regex ®ex);
|
std::vector<std::string> split(const std::string &str, std::regex ®ex);
|
||||||
std::string urldecode(std::string_view str);
|
std::string urldecode(std::string_view str);
|
||||||
std::string strreplace(std::string str, const std::string &search, const std::string &replace);
|
std::string strreplace(const std::string &str, const std::string &search, const std::string &replace);
|
||||||
|
|
||||||
std::string html_xss(std::string_view str);
|
std::string html_xss(std::string_view str);
|
||||||
std::string getenv(const std::string &key);
|
std::string getenv(const std::string &key);
|
||||||
|
|
||||||
template <class T, class U> U getKeyOrEmpty(const std::map<T, U> &map, const T &key)
|
template <class T, class U> bool hasKey(const std::map<T, U> &map, T key)
|
||||||
|
{
|
||||||
|
auto k = map.find(key);
|
||||||
|
return k != map.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class U> U getKeyOrEmpty(const std::map<T, U> &map, T key)
|
||||||
{
|
{
|
||||||
auto k = map.find(key);
|
auto k = map.find(key);
|
||||||
if(k != map.end())
|
if(k != map.end())
|
||||||
@ -31,7 +36,7 @@ template <class T, class U> U getKeyOrEmpty(const std::map<T, U> &map, const T &
|
|||||||
return U();
|
return U();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, class U> U getKeyOrEmpty(const std::multimap<T, U> &map, const T &key)
|
template <class T, class U> U getKeyOrEmpty(std::multimap<T, U> map, T key)
|
||||||
{
|
{
|
||||||
auto k = map.find(key);
|
auto k = map.find(key);
|
||||||
if(k != map.end())
|
if(k != map.end())
|
||||||
@ -41,7 +46,7 @@ template <class T, class U> U getKeyOrEmpty(const std::multimap<T, U> &map, cons
|
|||||||
return U();
|
return U();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, class U> std::vector<U> getAll(const std::multimap<T, U> &map, const T &key)
|
template <class T, class U> std::vector<U> getAll(std::multimap<T, U> map, T key)
|
||||||
{
|
{
|
||||||
std::vector<U> result;
|
std::vector<U> result;
|
||||||
auto range = map.equal_range(key);
|
auto range = map.equal_range(key);
|
||||||
@ -55,6 +60,7 @@ template <class T, class U> std::vector<U> getAll(const std::multimap<T, U> &map
|
|||||||
std::string regex_callback_replacer(std::regex regex, const std::string &input,
|
std::string regex_callback_replacer(std::regex regex, const std::string &input,
|
||||||
std::function<std::string(std::smatch &)> callback);
|
std::function<std::string(std::smatch &)> callback);
|
||||||
|
|
||||||
|
void regex_callback_extractor(std::regex regex, const std::string &input, std::function<void(std::smatch &)> callback);
|
||||||
std::string readCompleteFile(std::string_view filepath);
|
std::string readCompleteFile(std::string_view filepath);
|
||||||
|
|
||||||
inline std::string nz(const char *s)
|
inline std::string nz(const char *s)
|
||||||
@ -81,16 +87,12 @@ inline unsigned int toUInt(const std::string &str)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string formatLocalDate(time_t t, std::string format);
|
|
||||||
std::string toISODate(time_t t);
|
std::string toISODate(time_t t);
|
||||||
std::string toISODateTime(time_t t);
|
|
||||||
|
|
||||||
template <class T> inline std::string toString(const T &v)
|
template <class T> inline std::string toString(const T &v)
|
||||||
{
|
{
|
||||||
return std::string(v.begin(), v.end());
|
return std::string(v.begin(), v.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string trim(std::string_view view);
|
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,12 +60,12 @@ std::string Varreplacer::makeReplacement(std::string_view varkeyvalue)
|
|||||||
std::string_view value;
|
std::string_view value;
|
||||||
|
|
||||||
std::tie(key, value) = extractKeyAndValue(varkeyvalue);
|
std::tie(key, value) = extractKeyAndValue(varkeyvalue);
|
||||||
if(keyValues.contains(key))
|
if(utils::hasKey(keyValues, key))
|
||||||
{
|
{
|
||||||
std::string replacementContent = keyValues[key];
|
std::string replacementContent = keyValues[key];
|
||||||
return replacementContent;
|
return replacementContent;
|
||||||
}
|
}
|
||||||
else if(resolverFunctionsMap.contains(key))
|
else if(utils::hasKey(resolverFunctionsMap, key))
|
||||||
{
|
{
|
||||||
|
|
||||||
auto resolver = this->resolverFunctionsMap[key];
|
auto resolver = this->resolverFunctionsMap[key];
|
||||||
|
11
version.cpp
11
version.cpp
@ -1,11 +0,0 @@
|
|||||||
#include "version.h"
|
|
||||||
|
|
||||||
std::string git_commit_id()
|
|
||||||
{
|
|
||||||
return std::string(GITCOMMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_version_string()
|
|
||||||
{
|
|
||||||
return git_commit_id() + " Built: " + __DATE__ + " " + __TIME__;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
#ifndef VERSION_H
|
|
||||||
#define VERSION_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
std::string git_commit_id();
|
|
||||||
std::string get_version_string();
|
|
||||||
#endif // VERSION_H
|
|
Reference in New Issue
Block a user