Comparer les révisions

9 Révisions

118 fichiers modifiés avec 664 ajouts et 2151 suppressions

9
.gitmodules externe
Voir le fichier

@ -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

Voir le fichier

@ -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)

Voir le fichier

@ -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

Voir le fichier

@ -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,8 +42,7 @@ 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";

Voir le fichier

@ -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 externe
Voir le fichier

@ -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 externe
Voir le fichier

@ -1 +0,0 @@
#include "mapcache.h"

38
cache/mapcache.h externe
Voir le fichier

@ -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

278
cli.cpp
Voir le fichier

@ -1,278 +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(Page &page : result)
{
stream << page.name << " " << page.pageid << " " << std::string(page.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(Page &member : members)
{
stream << member.name << std::endl;
}
return {true, stream.str()};
}

94
cli.h
Voir le fichier

@ -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

Voir le fichier

@ -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";
}

Voir le fichier

@ -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

Voir le fichier

@ -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;
}

Voir le fichier

@ -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

Voir le fichier

@ -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,22 +71,20 @@ 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");
this->templatepath = required("templatepath"); this->templatepath = required("templatepath");
this->urls.linkallcats = required("linkallcats"); this->urls.linkallcats = required("linkallcats");
this->urls.linkallpages = required("linkallpages"); this->urls.linkallpages = required("linkallpages");
this->urls.linkallpagesrendertype = required ("linkallpagesrendertype");
this->urls.linkcategory = required("linkcategory"); this->urls.linkcategory = required("linkcategory");
this->urls.linkcategoryrendertype = required("linkcategoryrendertype");
this->urls.linkdelete = required("linkdelete"); this->urls.linkdelete = required("linkdelete");
this->urls.linkedit = required("linkedit"); this->urls.linkedit = required("linkedit");
this->urls.linkhistory = required("linkhistory"); this->urls.linkhistory = required("linkhistory");
this->urls.linkindex = required("linkindex"); this->urls.linkindex = required("linkindex");
this->urls.linklogout = required("linklogout"); this->urls.linklogout = required("linklogout");
this->urls.linkpage = required("linkpage"); this->urls.linkpage = required("linkpage");
this->urls.linkpagebytitle = required("linkpagebytitle");
this->urls.linkrecent = required("linkrecent"); this->urls.linkrecent = required("linkrecent");
this->urls.linkrevision = required("linkrevision"); this->urls.linkrevision = required("linkrevision");
this->urls.linksettings = required("linksettings"); this->urls.linksettings = required("linksettings");
@ -100,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);

Voir le fichier

@ -23,11 +23,9 @@ struct ConfigUrls
std::string linkindex; std::string linkindex;
std::string linkrecent; std::string linkrecent;
std::string linkallpages; std::string linkallpages;
std::string linkallpagesrendertype;
std::string linkallcats; std::string linkallcats;
std::string linkshere; std::string linkshere;
std::string linkpage; std::string linkpage;
std::string linkpagebytitle;
std::string linkrevision; std::string linkrevision;
std::string linkhistory; std::string linkhistory;
std::string linkedit; std::string linkedit;
@ -35,7 +33,6 @@ struct ConfigUrls
std::string linkdelete; std::string linkdelete;
std::string linklogout; std::string linklogout;
std::string linkcategory; std::string linkcategory;
std::string linkcategoryrendertype;
std::string loginurl; std::string loginurl;
std::string linkrecentsort; std::string linkrecentsort;
std::string actionurl; std::string actionurl;
@ -44,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
@ -91,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;

Voir le fichier

@ -5,7 +5,7 @@
#include <optional> #include <optional>
#include "queryoption.h" #include "queryoption.h"
#include "../category.h" #include "../category.h"
#include "../page.h"
class CategoryDao class CategoryDao
{ {
public: public:
@ -14,7 +14,7 @@ class CategoryDao
virtual std::vector<std::string> fetchList(QueryOption o) = 0; virtual std::vector<std::string> fetchList(QueryOption o) = 0;
virtual std::optional<Category> find(std::string name) = 0; virtual std::optional<Category> find(std::string name) = 0;
virtual void deleteCategory(std::string name) = 0; virtual void deleteCategory(std::string name) = 0;
virtual std::vector<Page> fetchMembers(std::string name, QueryOption o) = 0; virtual std::vector<std::string> fetchMembers(std::string name, QueryOption o) = 0;
}; };
#endif // CATEGORYDAO_H #endif // CATEGORYDAO_H

Voir le fichier

@ -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)
@ -94,10 +93,9 @@ std::vector<std::string> CategoryDaoSqlite::fetchList(QueryOption o)
} }
return result; return result;
} }
std::vector<std::string> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption o)
std::vector<Page> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption o)
{ {
std::vector<Page> result; std::vector<std::string> result;
SqliteQueryOption queryOption{o}; SqliteQueryOption queryOption{o};
std::string queryoptions = std::string queryoptions =
@ -105,18 +103,11 @@ std::vector<Page> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption
try try
{ {
auto query = *db << "SELECT page.id, page.name AS name, page.title, page.lastrevision, page.visible FROM categorymember INNER JOIN page ON page.id = " auto query = *db << "SELECT page.name AS name FROM categorymember INNER JOIN page ON page.id = "
"categorymember.page WHERE category = (SELECT id FROM category WHERE name = ? ) AND " + "categorymember.page WHERE category = (SELECT id FROM category WHERE name = ? ) AND " +
queryoptions queryoptions
<< name; << name;
query >> [&](unsigned int id, std::string name, std::string title, unsigned int lastrevision, bool visible) { query >> [&](std::string p) { result.push_back(p); };
Page p;
p.name = name;
p.pageid = id;
p.title = title;
p.current_revision = lastrevision;
p.listed = visible;
result.push_back(p); };
} }
catch(const sqlite::exceptions::no_rows &e) catch(const sqlite::exceptions::no_rows &e)
{ {

Voir le fichier

@ -3,13 +3,12 @@
#include "categorydao.h" #include "categorydao.h"
#include "sqlitedao.h" #include "sqlitedao.h"
#include "../page.h"
class CategoryDaoSqlite : public CategoryDao, protected SqliteDao class CategoryDaoSqlite : public CategoryDao, protected SqliteDao
{ {
public: public:
CategoryDaoSqlite(); CategoryDaoSqlite();
std::vector<std::string> fetchList(QueryOption o) override; std::vector<std::string> fetchList(QueryOption o) override;
std::vector<Page> fetchMembers(std::string name, QueryOption o) override; std::vector<std::string> fetchMembers(std::string name, QueryOption o) override;
void save(const Category &c) override; void save(const Category &c) override;
void deleteCategory(std::string name) override; void deleteCategory(std::string name) override;
std::optional<Category> find(std::string name) override; std::optional<Category> find(std::string name) override;

Voir le fichier

@ -13,9 +13,8 @@ class PageDao
virtual bool exists(std::string page) const = 0; virtual bool exists(std::string page) const = 0;
virtual bool exists(unsigned int id) const = 0; virtual bool exists(unsigned int id) const = 0;
virtual std::optional<Page> find(std::string name) = 0; virtual std::optional<Page> find(std::string name) = 0;
virtual std::optional<Page> findByTitle(std::string title) = 0;
virtual std::optional<Page> find(unsigned int id) = 0; virtual std::optional<Page> find(unsigned int id) = 0;
virtual std::vector<Page> getPageList(QueryOption option) = 0; virtual std::vector<std::string> getPageList(QueryOption option) = 0;
virtual std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) = 0; virtual std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) = 0;
virtual void deletePage(std::string page) = 0; virtual void deletePage(std::string page) = 0;
virtual void save(const Page &page) = 0; virtual void save(const Page &page) = 0;

Voir le fichier

@ -52,35 +52,15 @@ std::optional<Page> PageDaoSqlite::find(std::string name)
} }
} }
std::optional<Page> PageDaoSqlite::findByTitle(std::string title)
{
Page result;
try
{
auto ps = *db << "SELECT id, name, title, lastrevision, visible FROM page WHERE title = ?";
ps << title >> std::tie(result.pageid, result.name, result.title, result.current_revision, result.listed);
}
catch(const sqlite::errors::no_rows &e)
{
return {};
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::optional<Page> PageDaoSqlite::find(unsigned int id) std::optional<Page> PageDaoSqlite::find(unsigned int id)
{ {
Page result; Page result;
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)
{ {
@ -117,38 +97,30 @@ 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)
{ {
throwFrom(e); throwFrom(e);
} }
} }
std::vector<std::string> PageDaoSqlite::getPageList(QueryOption option)
std::vector<Page> PageDaoSqlite::getPageList(QueryOption option)
{ {
std::vector<Page> result; std::vector<std::string> result;
try try
{ {
std::string queryOption = SqliteQueryOption(option) std::string queryOption = SqliteQueryOption(option)
.setOrderByColumn("lower(name)") .setOrderByColumn("lower(name)")
.setVisibleColumnName("visible") .setVisibleColumnName("visible")
.setPrependWhere(true) .setPrependWhere(true)
.build(); .build();
std::string query = "SELECT id, name, title, lastrevision, visible FROM page " + queryOption; std::string query = "SELECT name FROM page " + queryOption;
*db << query >> [&](unsigned int pageid, std::string name, std::string title,unsigned int current_revision, bool visible ) {
Page tmp; *db << query >> [&](std::string name) { result.push_back(name); };
tmp.pageid = pageid;
tmp.name = name;
tmp.title = title;
tmp.current_revision = current_revision;
tmp.listed = visible;
result.push_back(tmp); };
} }
catch(const sqlite::errors::no_rows &e) catch(const sqlite::errors::no_rows &e)
{ {
@ -211,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;

Voir le fichier

@ -20,9 +20,8 @@ class PageDaoSqlite : public PageDao, protected SqliteDao
bool exists(std::string name) const override; bool exists(std::string name) const override;
void save(const Page &page) override; void save(const Page &page) override;
std::optional<Page> find(std::string name) override; std::optional<Page> find(std::string name) override;
std::optional<Page> findByTitle(std::string title) override;
std::optional<Page> find(unsigned int id) override; std::optional<Page> find(unsigned int id) override;
std::vector<Page> getPageList(QueryOption option) override; std::vector<std::string> getPageList(QueryOption option) override;
std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) override; std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) override;
using SqliteDao::SqliteDao; using SqliteDao::SqliteDao;
int fetchPageId(std::string pagename); int fetchPageId(std::string pagename);

Voir le fichier

@ -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

Voir le fichier

@ -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);
}
}

Voir le fichier

@ -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;
}; };

Voir le fichier

@ -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;
} }

Voir le fichier

@ -46,18 +46,15 @@ SqliteQueryOption &SqliteQueryOption::setPrependWhere(bool b)
std::string SqliteQueryOption::build() std::string SqliteQueryOption::build()
{ {
std::string result; std::string result;
if(!o.includeInvisible && !this->visibleColumnName.empty())
{
if(this->prependWhere) if(this->prependWhere)
{ {
result += "WHERE "; result += "WHERE ";
} }
if(!o.includeInvisible && !this->visibleColumnName.empty())
{
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;
} }

Voir le fichier

@ -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(){};

Voir le fichier

@ -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?
} }

Voir le fichier

@ -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;

Voir le fichier

@ -1,8 +0,0 @@
#include "dynamiccontent.h"
DynamicContent::DynamicContent(Template &templ, Database &database, UrlProvider &provider)
{
this->templ = &templ;
this->database = &database;
this->urlProvider = &provider;
}

Voir le fichier

@ -1,28 +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;
std::string argument;
public:
DynamicContent(Template &templ, Database &database, UrlProvider &urlProvider);
virtual std::string render() = 0;
virtual void setArgument(std::string argument)
{
this->argument = argument;
}
virtual ~DynamicContent()
{
}
};
#endif // DYNAMICCONTENT_H

Voir le fichier

@ -1,11 +0,0 @@
#include "dynamiccontentgetvar.h"
std::string DynamicContentGetVar::render()
{
return (*this->map)[this->argument];
}
void DynamicContentGetVar::setMap(std::map<std::string, std::string> &map)
{
this->map = &map;
}

Voir le fichier

@ -1,19 +0,0 @@
#ifndef DYNAMICCONTENTGETVAR_H
#define DYNAMICCONTENTGETVAR_H
#include "dynamiccontent.h"
class DynamicContentGetVar : public DynamicContent
{
private:
std::map<std::string, std::string> *map;
public:
using DynamicContent::DynamicContent;
// DynamicContent interface
public:
std::string render();
void setMap(std::map<std::string, std::string> &map);
};
#endif // DYNAMICCONTENTGETVAR_H

Voir le fichier

@ -1,12 +0,0 @@
#include "dynamiccontentincludepage.h"
#include "../parser.h"
std::string DynamicContentIncludePage::render()
{
auto revisionDao = this->database->createRevisionDao();
auto rev = revisionDao->getCurrentForPage(this->argument);
if(rev)
{
return rev->content;
}
return {};
}

Voir le fichier

@ -1,12 +0,0 @@
#ifndef DYNAMICCONTENTINCLUDEPAGE_H
#define DYNAMICCONTENTINCLUDEPAGE_H
#include "dynamiccontent.h"
class DynamicContentIncludePage : public DynamicContent
{
public:
using DynamicContent::DynamicContent;
std::string render();
};
#endif // DYNAMICCONTENTINCLUDEPAGE_H

Voir le fichier

@ -1,39 +0,0 @@
#include <chrono>
#include "dynamiccontentpostlist.h"
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->argument, option);
std::vector<std::pair<std::string, time_t>> pageList;
for(const Page &member : members)
{
auto revision = revisionDao->getRevisionForPage(member.name, 1);
pageList.push_back({member.name, 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();
}

Voir le fichier

@ -1,12 +0,0 @@
#ifndef DYNAMICCONTENTPOSTLIST_H
#define DYNAMICCONTENTPOSTLIST_H
#include "dynamiccontent.h"
class DynamicContentPostList : public DynamicContent
{
public:
using DynamicContent::DynamicContent;
std::string render() override;
};
#endif // DYNAMICCONTENTPOSTLIST_H

Voir le fichier

@ -1,21 +0,0 @@
#include "dynamiccontentsetvar.h"
std::string DynamicContentSetVar::render()
{
auto result = utils::split(this->argument, '=');
if(result.size() == 2)
{
this->map->emplace(std::make_pair(result[0], result[1]));
}
return {};
}
void DynamicContentSetVar::setArgument(std::string argument)
{
this->argument = argument;
}
void DynamicContentSetVar::setMap(std::map<std::string, std::string> &map)
{
this->map = &map;
}

Voir le fichier

@ -1,17 +0,0 @@
#ifndef DYNAMCCONTENTPUSHVAR_H
#define DYNAMCCONTENTPUSHVAR_H
#include "dynamiccontent.h"
class DynamicContentSetVar : public DynamicContent
{
private:
std::map<std::string, std::string> *map;
public:
using DynamicContent::DynamicContent;
std::string render();
void setArgument(std::string argument);
void setMap(std::map<std::string, std::string> &map);
};
#endif // DYNAMCCONTENTPUSHVAR_H

Voir le fichier

@ -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);

Voir le fichier

Voir le fichier

@ -1,26 +0,0 @@
#include "utils.h"
template<class G, class V, class C>
class Grouper
{
std::map<G, std::vector<const V*>, C> results;
public:
Grouper(C c)
{
results = std::map<G, std::vector<const V*>, C>(c);
}
void group(const std::function<G(const V&)> &map, const std::vector<V> &values)
{
for(const V &v : values)
{
results[map(v)].push_back(&v);
}
}
std::map<G, std::vector<const V*>, C> &getResults()
{
return this->results;
}
};

Voir le fichier

@ -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);

Voir le fichier

@ -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);
}; };

Voir le fichier

@ -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);

Voir le fichier

@ -19,8 +19,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "handlerallpages.h" #include "handlerallpages.h"
#include "../grouper.h"
#include "../pagelistrenderer.h"
Response HandlerAllPages::handleRequest(const Request &r) Response HandlerAllPages::handleRequest(const Request &r)
{ {
@ -29,21 +27,17 @@ Response HandlerAllPages::handleRequest(const Request &r)
Response response; Response response;
auto pageDao = this->database->createPageDao(); auto pageDao = this->database->createPageDao();
QueryOption qo = queryOption(r); QueryOption qo = queryOption(r);
std::vector<Page> pageList = pageDao->getPageList(qo); auto resultList = pageDao->getPageList(qo);
if(pageList.size() == 0) if(resultList.size() == 0)
{ {
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");
PageListRenderer pagelistrender(*this->templ, *this->urlProvider, *this->database); std::string body = this->templ->renderSearch(resultList);
TemplatePage allPages = this->templ->getPage("allpages"); searchPage.setVar("pagelist", body);
allPages.setVar("pagelistcontent", pagelistrender.render(pageList, r.get("rendertype"))); searchPage.setVar("title", createPageTitle("All pages"));
allPages.setVar("pagelistletterlink", this->urlProvider->allPages(PageListRenderer::RENDER_GROUP_BY_LETTER)); setGeneralVars(searchPage);
allPages.setVar("pagelistcreationdatelink", this->urlProvider->allPages(PageListRenderer::RENDER_GROUP_BY_CREATIONDATE)); response.setBody(searchPage.render());
allPages.setVar("title", createPageTitle("All pages"));
setGeneralVars(allPages);
response.setBody(allPages.render());
response.setStatus(200); response.setStatus(200);
return response; return response;
} }

Voir le fichier

@ -19,7 +19,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
*/ */
#include "handlercategory.h" #include "handlercategory.h"
#include "../pagelistrenderer.h"
Response HandlerCategory::handleRequest(const Request &r) Response HandlerCategory::handleRequest(const Request &r)
{ {
@ -34,13 +33,9 @@ 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");
PageListRenderer pagelistrender(*this->templ, *this->urlProvider, *this->database); std::string body = this->templ->renderSearch(resultList);
searchPage.setVar("pagelist", body);
std::string body = pagelistrender.render(resultList, r.get("rendertype"));
searchPage.setVar("pagelistcontent", body);
searchPage.setVar("pagelistletterlink", this->urlProvider->category(categoryname, PageListRenderer::RENDER_GROUP_BY_LETTER));
searchPage.setVar("pagelistcreationdatelink", this->urlProvider->category(categoryname, PageListRenderer::RENDER_GROUP_BY_CREATIONDATE));
searchPage.setVar("categoryname", categoryname); searchPage.setVar("categoryname", categoryname);
setGeneralVars(searchPage); setGeneralVars(searchPage);
searchPage.setVar("title", createPageTitle("Category: " + categoryname)); searchPage.setVar("title", createPageTitle("Category: " + categoryname));

Voir le fichier

@ -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;
} }

Voir le fichier

@ -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);
} }

Voir le fichier

@ -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);

Voir le fichier

@ -1,148 +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;
auto comppage = [](const Page &a, const Page &b) { return a.name < b.name; };
std::set<Page, decltype(comppage)> members (comppage);
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)
{
if(!categoryDao->find(cat))
{
throw std::runtime_error("No such category");
}
auto catmembers = categoryDao->fetchMembers(cat, option);
std::copy(catmembers.begin(), catmembers.end(), std::inserter(members, members.end()));
}
}
for(const Page &member : members)
{
auto revision = revisionDao->getRevisionForPage(member.name, 1).value();
result.push_back({member, 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;
}
std::string 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();
std::string subtitle = filter;
if(utils::trim(filter).empty())
{
subtitle = "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 entryUpdated = utils::formatLocalDate(current.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);
atomentry.setVar("entryupdated", entryUpdated);
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", subtitle);
std::string selflink = utils::html_xss(this->urlProvider->atomFeed(filter));
atomheader.setVar("atomfeeduniqueid", selflink);
atomheader.setVar("atomselflink", selflink);
atomheader.setVar("atomfeedupdate", utils::formatLocalDate(newestPublished, dateformat) + "Z");
return atomheader.render() + stream.str();
}
Response HandlerFeedGenerator::handleRequest(const Request &r)
{
Response response;
try
{
std::string type = r.get("type");
std::string categories = r.get("cats");
if(type == "atom")
{
std::string filter = categories;
Response result;
result.setStatus(200);
result.setContentType("application/atom+xml");
std::string cacheKey = "feed:atom:" + filter;
auto cached = this->cache->get(cacheKey);
if(cached)
{
result.setBody(cached.value());
}
else
{
auto entries = fetchEntries(utils::split(categories, ','));
std::string feed = generateAtom(entries, filter);
result.setBody(feed);
this->cache->put(cacheKey, feed);
}
response = result;
}
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;
}

Voir le fichier

@ -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);
std::string 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

Voir le fichier

@ -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
} }

Voir le fichier

@ -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;
} }

Voir le fichier

@ -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;
} }

Voir le fichier

@ -26,20 +26,9 @@ Response HandlerPage::handle(const Request &r)
std::string pagename = r.get("page"); std::string pagename = r.get("page");
auto pageDao = this->database->createPageDao(); auto pageDao = this->database->createPageDao();
if(pagename.empty()) if(pagename.empty())
{
std::string title = r.get("title");
if(title.empty())
{ {
return errorResponse("No page given", "No page given to request"); return errorResponse("No page given", "No page given to request");
} }
title = utils::strreplace(title, "-", " ");
auto page = pageDao->findByTitle(title);
if(!page)
{
return errorResponse("No page by such title", "No page with such title exists");
}
pagename = page->name;
}
if(pageMustExist() && !pageDao->exists(pagename)) if(pageMustExist() && !pageDao->exists(pagename))
{ {
@ -74,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();

Voir le fichier

@ -45,7 +45,6 @@ Response HandlerPageDelete::handleRequest(PageDao &pageDao, std::string pagename
{ {
pageDao.deletePage(pagename); pageDao.deletePage(pagename);
this->cache->removePrefix("page:"); // TODO: overkill? this->cache->removePrefix("page:"); // TODO: overkill?
this->cache->removePrefix("feed:");
return Response::redirectTemporarily(this->urlProvider->index()); return Response::redirectTemporarily(this->urlProvider->index());
} }
TemplatePage delPage = this->templ->getPage("page_deletion"); TemplatePage delPage = this->templ->getPage("page_deletion");

Voir le fichier

@ -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;
@ -101,7 +95,6 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
pageDao.setCategories(pagename, cats); pageDao.setCategories(pagename, cats);
this->database->commitTransaction(); this->database->commitTransaction();
this->cache->removePrefix("page:"); // TODO: overkill? this->cache->removePrefix("page:"); // TODO: overkill?
this->cache->removePrefix("feed:");
} }
catch(const DatabaseException &e) catch(const DatabaseException &e)
{ {
@ -114,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));
@ -128,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);

Voir le fichier

@ -21,12 +21,8 @@ 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"
#include "../dynamic/dynamiccontentincludepage.h"
#include "../dynamic/dynamiccontentsetvar.h"
#include "../dynamic/dynamiccontentgetvar.h"
bool HandlerPageView::canAccess(std::string page) bool HandlerPageView::canAccess(std::string page)
{ {
@ -132,54 +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::map<std::string, std::string> dynamicVarsMap;
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->setArgument(std::string(value));
return postlist->render();
}
if(key == "dynamic:includepage")
{
if((effectivePermissions(std::string(value)).canRead()))
{
std::shared_ptr<DynamicContentIncludePage> includePage = createDynamic<DynamicContentIncludePage>();
includePage->setArgument(std::string(value));
return parser.parseDynamics(includePage->render(), dynamicParseCallback);
}
}
if(key == "dynamic:setvar")
{
std::shared_ptr<DynamicContentSetVar> setVar = createDynamic<DynamicContentSetVar>();
setVar->setMap(dynamicVarsMap);
setVar->setArgument(std::string(value));
return setVar->render();
}
if(key == "dynamic:getvar")
{
std::shared_ptr<DynamicContentGetVar> getVar = createDynamic<DynamicContentGetVar>();
getVar->setMap(dynamicVarsMap);
getVar->setArgument(std::string(value));
return getVar->render();
}
return std::string{};
};
std::string resolvedContent = parser.parseDynamics(revision->content, dynamicParseCallback);
if(revisionid > 0) if(revisionid > 0)
{ {
indexcontent = createIndexContent(parser, resolvedContent); indexcontent = createIndexContent(*parser, revision->content);
parsedcontent = parser.parse(pageDao, *this->urlProvider, resolvedContent); parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content);
} }
else else
{ {
@ -193,7 +153,7 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
} }
else else
{ {
indexcontent = createIndexContent(parser, resolvedContent); indexcontent = createIndexContent(*parser, revision->content);
this->cache->put(cachekeyindexcontent, indexcontent); this->cache->put(cachekeyindexcontent, indexcontent);
} }
if(cachedparsedcontent) if(cachedparsedcontent)
@ -202,23 +162,18 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
} }
else else
{ {
parsedcontent = parser.parse(pageDao, *this->urlProvider, resolvedContent); 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)
{ {

Voir le fichier

@ -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);

Voir le fichier

@ -21,14 +21,13 @@ Response HandlerUserSettings::handleRequest(const Request &r)
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;
} }

Voir le fichier

@ -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;
}

Voir le fichier

@ -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

Voir le fichier

@ -2,34 +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::string parseDynamics(
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
Voir le fichier

@ -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;

Voir le fichier

@ -1,66 +0,0 @@
#include "pagelistrenderer.h"
#include "grouper.h"
PageListRenderer::PageListRenderer(Template &templ, UrlProvider &provider, Database &database)
{
this->templ = &templ;
this->urlProvider = &provider;
this->database = &database;
}
std::string PageListRenderer::render(const std::vector<Page> &pagelist, std::string type) const
{
TemplatePage pagelistrendergroup = this->templ->loadResolvedPart("pagelistrender_group");
TemplatePage pagelistlink = this->templ->loadResolvedPart("pagelistrender_link");
std::function<bool(const std::string &, const std::string &)> lesser = [](const std::string &a, const std::string &b) -> bool {
return a < b;
};
std::function<bool(const std::string &, const std::string &)> greater = [](const std::string &a, const std::string &b) -> bool {
return a > b;
};
using Grouper = Grouper<std::string, Page, std::function<bool(const std::string &,const std::string &)>>;
Grouper grouper = (type == "letter") ? Grouper(lesser) : Grouper(greater);
if(type == "letter")
{
auto az = [&](const Page &p) -> std::string {
int upper = toupper(p.title[0]); // TODO: this is not unicode safe.
return { (char)upper };
};
grouper.group(az, pagelist);
}
else
{
auto revisionDao = this->database->createRevisionDao();
auto creationdate = [&revisionDao](const Page &p) -> std::string {
return utils::formatLocalDate(revisionDao->getRevisionForPage(p.name, 1).value().timestamp, "%Y-%m");
};
grouper.group(creationdate, pagelist);
}
std::stringstream stream;
std::string lastGroup = "";
for(auto &entry : grouper.getResults())
{
std::string groupname = entry.first;
const std::vector<const Page*> &pages = entry.second;
if(lastGroup != groupname)
{
lastGroup = groupname;
pagelistrendergroup.setVar("groupname", groupname);
stream << pagelistrendergroup.render();
}
for(const Page *p : pages)
{
pagelistlink.setVar("inner", p->title);
pagelistlink.setVar("href", this->urlProvider->page(p->name));
stream << pagelistlink.render();
}
}
return stream.str();
}

Voir le fichier

@ -1,27 +0,0 @@
#ifndef PAGELISTRENDERER_H
#define PAGELISTRENDERER_H
#include "utils.h"
#include "page.h"
#include "template.h"
#include "htmllink.h"
#include "urlprovider.h"
#include "database/database.h"
class PageListRenderer
{
private:
Template *templ;
UrlProvider *urlProvider;
Database *database;
public:
PageListRenderer(Template &templ, UrlProvider &provider, Database &database);
std::string render(const std::vector<Page> &pages, std::string type) const;
inline static const std::string RENDER_GROUP_BY_LETTER { "letter" };
inline static const std::string RENDER_GROUP_BY_CREATIONDATE { "creationdate" };
};
#endif

Voir le fichier

@ -1,190 +0,0 @@
/* Copyright (c) 2018 Albert S.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
SOFTWARE.
*/
#include <regex>
#include <iostream>
#include <regex>
#include <vector>
#include <algorithm>
#include <iterator>
#include "parser.h"
#include "utils.h"
#include "htmllink.h"
std::vector<Headline> Parser::extractHeadlines(const std::string &content) const
{
std::vector<Headline> result;
std::string reg = R"(\[h(1|2|3)\](\[.*?\])*(.*?)(\[.*?\])*\[\/h\1\])";
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;
h.level = utils::toUInt(smatch.str(1));
h.title = smatch.str(3);
result.push_back(h);
}
return result;
}
std::vector<std::string> Parser::extractCategories(const std::string &content) const
{
std::vector<std::string> result;
std::string reg = R"(\[category\](.*?)\[/category\])";
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;
result.emplace_back(smatch.str(1));
}
return result;
}
std::string Parser::extractCommand(std::string cmdname, const std::string &content) const
{
std::string cmd = "[cmd:" + cmdname + "]";
std::string cmdend = "[/cmd:" + cmdname + "]";
std::string_view view = content;
size_t pos = 0;
if((pos = view.find(cmd)) != std::string::npos)
{
view.remove_prefix(pos);
view.remove_prefix(cmd.size());
if((pos = view.find(cmdend)) != std::string::npos)
{
auto result = view.substr(0, pos);
return std::string{result};
}
}
return "";
}
std::string Parser::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
{
std::string linktag = match.str(1);
std::string inside = match.str(2);
std::vector<std::string> splitted = utils::split(inside, '|');
HtmlLink htmllink;
if(splitted.size() == 2)
{
htmllink.innervalue = splitted[1];
htmllink.href = splitted[0];
}
else
{
htmllink.innervalue = inside;
htmllink.href = inside;
}
if(linktag == "wikilink")
{
if(pageDao.exists(htmllink.href))
{
htmllink.cssclass = "exists";
}
else
{
htmllink.cssclass = "notexists";
}
htmllink.href = urlProvider.page(htmllink.href);
}
return htmllink.render();
}
std::string Parser::processImage(std::smatch &match) const
{
std::string tag = match.str(1);
std::string inside = match.str(2);
std::vector<std::string> splitted = utils::split(inside, '|');
std::string width;
std::string height;
std::string src;
if(splitted.size() == 3)
{
width = splitted[0];
height = splitted[1];
src = splitted[2];
}
else
{
src = splitted[0];
}
if(!width.empty() && !height.empty())
{
return "<img src=\"" + src + "\" width=\"" + width + "\" height=\"" + height + "\"/>";
}
return "<img src=\"" + src + "\"/>";
}
std::string Parser::parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content,
const std::function<std::string(std::string_view, std::string_view)> &callback) const
{
std::string result;
// we don't care about commands, but we nevertheless replace them with empty strings
std::regex tagfinder(
R"(\[(b|i|u|li||ul|ol|code|blockquote|img|link|wikilink|h\d|cmd:visible|cmd:rename|cmd:redirect|cmd:pagetitle|category|dynamic:postlist|dynamic:includepage|dynamic:getvar|dynamic:setvar)*?\]((\s|\S)*?)\[/\1])");
result = utils::regex_callback_replacer(
tagfinder, content,
[&](std::smatch &match)
{
std::string tag = match.str(1);
std::string content = match.str(2);
std::string justreplace[] = {"b", "i", "u", "ul", "li", "ol", "code", "blockquote"};
content = parse(pagedao, provider, content, callback);
if(std::find(std::begin(justreplace), std::end(justreplace), tag) != std::end(justreplace))
{
return "<" + tag + ">" + content + "</" + tag + ">";
}
if(tag == "link" || tag == "wikilink")
{
return this->processLink(
pagedao, provider,
match); // TODO: recreate this so we don't check inside the function stuff again
}
if(tag == "img")
{
return this->processImage(match);
}
if(tag[0] == 'h')
{
return "<" + tag + " id='" + content + "'>" + content + "</" + tag + ">";
}
return callback(tag, content);
});
result = utils::strreplace(result, "\r\n", "<br>");
return result;
}
std::string Parser::parseDynamics(const std::string &content,
const std::function<std::string(std::string_view, std::string_view)> &callback) const
{
std::regex tagfinder(R"(\[(dynamic:\w+)*?\]((\s|\S)*?)\[/\1])");
return utils::regex_callback_replacer(tagfinder, content,
[&](std::smatch &match) { return callback(match.str(1), match.str(2)); });
}

Voir le fichier

@ -1,26 +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;
std::string processImage(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;
std::string parseDynamics(
const std::string &content,
const std::function<std::string(std::string_view, std::string_view)> &callback) const override;
using IParser::IParser;
};
#endif // PARSER_H

139
parserlegacy.cpp Fichier normal
Voir le fichier

@ -0,0 +1,139 @@
/* Copyright (c) 2018 Albert S.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
SOFTWARE.
*/
#include <regex>
#include <iostream>
#include <regex>
#include <vector>
#include <algorithm>
#include <iterator>
#include "parserlegacy.h"
#include "utils.h"
#include "htmllink.h"
std::vector<Headline> ParserLegacy::extractHeadlines(std::string content) const
{
std::vector<Headline> result;
utils::regex_callback_extractor(std::regex(R"(\[h(1|2|3)\](.*?)\[/h\1\])"), content, [&](std::smatch &smatch) {
Headline h;
h.level = utils::toUInt(smatch.str(1));
h.title = smatch.str(2);
result.push_back(h);
});
return result;
}
std::vector<std::string> ParserLegacy::extractCategories(std::string content) const
{
std::vector<std::string> result;
std::string reg = R"(\[category\](.*?)\[/category\])";
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;
result.emplace_back(smatch.str(1));
}
return result;
}
std::string ParserLegacy::extractCommand(std::string cmdname, std::string content) const
{
std::string cmd = "[cmd:" + cmdname + "]";
std::string cmdend = "[/cmd:" + cmdname + "]";
std::string_view view = content;
size_t pos = 0;
if((pos = view.find(cmd)) != std::string::npos)
{
view.remove_prefix(pos);
view.remove_prefix(cmd.size());
if((pos = view.find(cmdend)) != std::string::npos)
{
auto result = view.substr(0, pos);
return std::string{result};
}
}
return "";
}
std::string ParserLegacy::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
{
std::string linktag = match.str(1);
std::string inside = match.str(2);
std::vector<std::string> splitted = utils::split(inside, '|');
HtmlLink htmllink;
if(splitted.size() == 2)
{
htmllink.innervalue = splitted[1];
htmllink.href = splitted[0];
}
else
{
htmllink.innervalue = inside;
htmllink.href = inside;
}
if(linktag == "wikilink")
{
if(pageDao.exists(htmllink.href))
{
htmllink.cssclass = "exists";
}
else
{
htmllink.cssclass = "notexists";
}
htmllink.href = urlProvider.page(htmllink.href);
}
return htmllink.render();
}
std::string ParserLegacy::parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const
{
std::string result;
// we don't care about commands, but we nevertheless replace them with empty strings
std::regex tagfinder(R"(\[(b|i|u|li||ul|ol|link|wikilink|h\d|cmd:rename|cmd:redirect|category)*?\]((\s|\S)*?)\[/\1])");
result = utils::regex_callback_replacer(tagfinder, content, [&](std::smatch &match) {
std::string tag = match.str(1);
std::string content = match.str(2);
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))
{
return "<" + tag + ">" + content + "</" + tag + ">";
}
if(tag == "link" || tag == "wikilink")
{
return this->processLink(pagedao, provider,
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 std::string("");
});
result = utils::strreplace(result, "\r\n", "<br>");
return result;
}

19
parserlegacy.h Fichier normal
Voir le fichier

@ -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 Fichier normal
Voir le fichier

@ -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 Fichier normal
Voir le fichier

@ -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

Voir le fichier

@ -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;
}

Voir le fichier

@ -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

Voir le fichier

@ -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)

Voir le fichier

@ -40,7 +40,7 @@ std::pair<std::string, std::string> Request::createPairFromVar(std::string var)
else else
{ {
std::string key = var.substr(0, equal); std::string key = var.substr(0, equal);
std::string val = utils::html_xss(utils::urldecode(var.substr(equal + 1))); std::string val = utils::html_xss(var.substr(equal + 1));
return std::make_pair(std::move(key), std::move(val)); return std::make_pair(std::move(key), std::move(val));
} }
} }

Voir le fichier

@ -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()};

Voir le fichier

@ -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);

Voir le fichier

@ -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)
{ {

Voir le fichier

@ -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;
} }

Voir le fichier

@ -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

Voir le fichier

@ -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

Voir le fichier

@ -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

Voir le fichier

@ -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;

Sous-module submodules/exile.h supprimé de f2ca26010a

1
submodules/qsmaddy Sous-module

Sous-module submodules/qsmaddy ajouté à 167ce3943d

1
submodules/qssb.h Sous-module

Sous-module submodules/qssb.h ajouté à 9df2e9ee90

Voir le fichier

@ -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>";
} }
}; };

Voir le fichier

@ -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;

Voir le fichier

@ -1,6 +1,6 @@
{qswiki:include:general_header} {qswiki:include:general_header}
<div id="content" style="margin-top: 10px;"> <div id="content" style="margin-top: 10px;">
<h2>All pages</h2> <h2>All pages</h2>
{qswiki:include:pagelistrender} {qswiki:var:pagelist}
</div> </div>
{qswiki:include:general_footer} {qswiki:include:general_footer}

Voir le fichier

@ -1 +0,0 @@
<ul>

Voir le fichier

@ -1 +0,0 @@
</ul>

Voir le fichier

@ -1 +0,0 @@
<li>{date}: <a href="{url}">{title}</a></li>

Voir le fichier

@ -1,8 +0,0 @@
<entry>
<title>{qswiki:var:entrytitle}</title>
<link href="{qswiki:var:entryurl}"/>
<id>{qswiki:var:entryid}</id>
<published>{qswiki:var:entrypublished}</published>
<updated>{qswiki:var:entryupdated}</updated>
<content type="html">{qswiki:var:entrycontent}</content>
</entry>

Voir le fichier

@ -1 +0,0 @@
</feed>

Voir le fichier

@ -1,9 +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>
<link rel="self" href="{qswiki:var:atomselflink}"/>
<updated>{qswiki:var:atomfeedupdate}</updated>

Voir le fichier

@ -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>
</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>
</ul> </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>

Certains fichiers ne sont pas affichés car ce diff contient trop de modifications Voir plus