sandboxing: First version using qssb.h #20
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,8 +3,6 @@
|
|||||||
*.out
|
*.out
|
||||||
*.gch
|
*.gch
|
||||||
*.user
|
*.user
|
||||||
*.swp
|
|
||||||
*.kate-swp
|
|
||||||
qswiki
|
qswiki
|
||||||
wikiqs*
|
wikiqs*
|
||||||
data/*
|
data/*
|
||||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -4,6 +4,6 @@
|
|||||||
[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://git.quitesimple.org/qssb.h
|
||||||
|
38
Makefile
38
Makefile
@ -1,14 +1,12 @@
|
|||||||
CPPSTD=c++20
|
|
||||||
|
|
||||||
#CFIFLAGS=-fsanitize=cfi -fvisibility=hidden -fsanitize=cfi -flto
|
|
||||||
#Does not work reliably atm
|
|
||||||
CFIFLAGS=
|
|
||||||
|
|
||||||
CXXFLAGS=-std=$(CPPSTD) -O2 -g -no-pie -pipe -MMD -Wall -Wextra -DGLIBCXX_ASSERTIONS -D_LIBCPP_ENABLE_ASSERTIONS=1 $(CFIFLAGS)
|
CXXFLAGS=-std=c++17 -O0 -g -no-pie -pipe -MMD -Wall -Wextra
|
||||||
RELEASE_CXXFLAGS=-std=$(CPPSTD) -O3 -pipe -MMD -Wall -Wextra -DGLIBCXX_ASSERTIONS -D_LIBCPP_ENABLE_ASSERTIONS=1 $(CFIFLAGS)
|
RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra
|
||||||
|
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs -lseccomp
|
||||||
|
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h
|
||||||
|
|
||||||
|
CXX=g++
|
||||||
|
|
||||||
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs $(CFIFLAGS)
|
|
||||||
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/exile.h
|
|
||||||
|
|
||||||
SOURCES=$(wildcard *.cpp)
|
SOURCES=$(wildcard *.cpp)
|
||||||
SOURCES+=$(wildcard gateway/*.cpp)
|
SOURCES+=$(wildcard gateway/*.cpp)
|
||||||
@ -16,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)
|
||||||
@ -24,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))
|
||||||
@ -38,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) $(shell shuf -e $(WIKIOBJECTS) exile.o ) ${LDFLAGS} ${INCLUDEFLAGS} -o qswiki
|
|
||||||
|
|
||||||
test: $(TESTOBJECTS)
|
test: $(TESTOBJECTS)
|
||||||
$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test
|
$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test
|
||||||
@ -65,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)
|
||||||
|
|
||||||
|
|
||||||
|
73
README.md
73
README.md
@ -1,26 +1,23 @@
|
|||||||
# qswiki
|
# qswiki
|
||||||
|
|
||||||
## About
|
About
|
||||||
qswiki is a wiki software, intended for my needs. Originally implemented in C, it's now written in C++.
|
====
|
||||||
|
qswiki is a wiki software, intended for small wikis. Originally
|
||||||
|
implemented in C, it's now written in C++.
|
||||||
|
|
||||||
## Dude... why?
|
History
|
||||||
|
====
|
||||||
tl;dr: It was a playground, an experiment (taken too far). I guess at some point I couldn't stop, because I've already
|
A couple of years ago, I wanted to setup a personal wiki on my raspberry
|
||||||
started.
|
|
||||||
|
|
||||||
### History
|
|
||||||
Several years ago, I wanted to setup a personal wiki on my raspberry
|
|
||||||
pi. However, the distribution I used back then did not have a PHP package
|
pi. However, the distribution I used back then did not have a PHP package
|
||||||
for ARM. So instead of switching distributions or searching for other
|
for ARM. So instead of switching distributions or searching for other
|
||||||
wikis that I could use, I simply decided I would write one in C. Yes,
|
wikis that I could use, I decided I would write one in C. Yes,
|
||||||
that's an odd way to approach the problem and indeed, I may have had too
|
that's an odd way to approach the problem and indeed, I may have had too
|
||||||
much time back then. Also, I wanted to see how it's like to write a
|
much time back then. Also, I wanted to see how it's like to write a
|
||||||
"web app" in C and wanted to sharpen my C skills a little bit.
|
"web app" in C and wanted to sharpen my C skills a little bit.
|
||||||
|
|
||||||
Of course, it's pretty straightforward at first. No really: Just use CGI
|
Of course, it's pretty straightforward at first. No really: Just use CGI.
|
||||||
and print your HTML to stdout.And indeed, that would have been more than enough for my use cases.
|
And indeed, that would have been more than enough for my use cases.
|
||||||
|
Then I decided to play around and started using FastCGI (with the official
|
||||||
But then I decided to play around and started using FastCGI (with the official
|
|
||||||
library from now defunct fastcgi.com) and created a multi-threaded version.
|
library from now defunct fastcgi.com) and created a multi-threaded version.
|
||||||
It initially used a "pile of files database", but that became too painful,
|
It initially used a "pile of files database", but that became too painful,
|
||||||
so then I started using sqlite.
|
so then I started using sqlite.
|
||||||
@ -28,66 +25,60 @@ so then I started using sqlite.
|
|||||||
C++
|
C++
|
||||||
---
|
---
|
||||||
Eventually, since it was mostly a playground for me, the code became
|
Eventually, since it was mostly a playground for me, the code became
|
||||||
unmaintainable. Furthermore, I initially wanted something quick and given that
|
unmaintainable. Furthermore, I wanted something quick and given that
|
||||||
it was CGI, I didn't bother taking care of memory leaks.
|
it was CGI, I didn't bother taking care of memory leaks.
|
||||||
After initiating a FastCGI interface, they became an issue and then the
|
After initiating a FastCGI interface, they became an issue and then the
|
||||||
task of avoiding memory leaks became too annoying. And of course, C does n
|
task of avoiding memory leaks became too annoying. And of course, C does n
|
||||||
ot include any "batteries" and while I could manage, this too was another
|
ot include any "batteries" and while I could manage, this too was another
|
||||||
good reason.
|
good reason.
|
||||||
|
|
||||||
Overall, I am just continuing the experiment with >=C++17 now. It's not
|
Overall, I am just continuing the experiment with C++17 now. It's not
|
||||||
nearly as bad as you would expect perhaps. Some things are surprisingly
|
nearly as bad as you would expect perhaps. Some things are surprisingly
|
||||||
convenient even. Still, the standard library is lacking and
|
convenient even. Still, the standard library is lacking and
|
||||||
I would hope for a some better built-in Unicode support in future C++
|
I would hope for a some better built-in Unicode support in future C++
|
||||||
standards.
|
standards.
|
||||||
|
|
||||||
|
Features
|
||||||
## Features
|
========
|
||||||
Some essential features are lacking, such as a diff between revisions,
|
To be fair, at this point it doesn't even have a "diff" between revisions
|
||||||
user registration UI, etc.
|
yet and does not have features that would make you prefer it over other
|
||||||
|
wikis.
|
||||||
It doesn't compete with any other software anyway.
|
|
||||||
|
|
||||||
- CGI
|
- CGI
|
||||||
- HTTP server using the header only library [cpp-httplib](https://github.com/yhirose/cpp-httplib). It's more
|
- HTTP server using the header only library cpp-httplib. It's more
|
||||||
portable and more "future-proof" than FastCGI (since the official website
|
portable and more "future-proof" than FastCGI (since the official website
|
||||||
disappeared, the library's future appears to be uncertain).
|
disappeared, the library's future appears to be uncertain).
|
||||||
- Support for user accounts. Passwords are stored using PBKDF2.
|
- Support for user accounts. Passwords are stored using PBKDF2.
|
||||||
sqlite database, but not too much of an effort to add other types of
|
sqlite database, but not too much of an effort to add other types of
|
||||||
storage backends. sqlite is using the great header only library
|
storage backends. sqlite is using the great header only library
|
||||||
[sqlite_modern_cpp](https://github.com/SqliteModernCpp)
|
sqlite_modern_cpp
|
||||||
- Relatively fine-grained permission system.
|
- Relatively fine-grained permission system.
|
||||||
- Categories
|
- Categories
|
||||||
- Templates
|
- Templates
|
||||||
- FTS search
|
- FTS search
|
||||||
- Caching
|
- Caching
|
||||||
- Blog-like functionality
|
|
||||||
- RSS/Atom feeds
|
|
||||||
|
|
||||||
## Security
|
Security
|
||||||
[exile.h](https://github.com/quitesimpleorg/exile.h) is used
|
========
|
||||||
to restrict access to the files the wiki needs. It doesn't have access to other paths
|
On Linux namespaces are used to restrict the process to only access
|
||||||
in the system and the system calls that the qswiki process can make are restricted.
|
files it needs. It doesn't have access to other paths in the system.
|
||||||
|
In addition, Seccomp is used to restrict the syscalls the qswiki process
|
||||||
As for "web security", all POST requests are centrally protected against CSRF attacks and all input is escaped against XSS
|
can call. As for "web security", all POST requests are centrally
|
||||||
|
protected against CSRF attacks and all input is escaped against XSS
|
||||||
attacks.
|
attacks.
|
||||||
|
|
||||||
## Building
|
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
|
- 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 two are header-only libraries that are already included here.
|
||||||
be installed, e. g. by using your distributions standard method.
|
|
||||||
|
|
||||||
If all dependencies are available, run:
|
If all dependencies are available, run:
|
||||||
```
|
```make release```
|
||||||
git submodule init
|
|
||||||
git submodule update
|
|
||||||
make release
|
|
||||||
```
|
|
||||||
|
|
||||||
Setup
|
Setup
|
||||||
=====
|
=====
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
#include <atomic>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include "utils.h"
|
|
||||||
#include "authenticator.h"
|
|
||||||
#include "logger.h"
|
|
||||||
struct LoginFail
|
|
||||||
{
|
|
||||||
std::mutex mutex;
|
|
||||||
std::atomic<unsigned int> count;
|
|
||||||
time_t lastfail;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::map<std::string, LoginFail> loginFails;
|
|
||||||
|
|
||||||
Authenticator::Authenticator(UserDao &userDao)
|
|
||||||
{
|
|
||||||
this->userDao = &userDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: make failure counter configurable
|
|
||||||
bool Authenticator::isBanned(std::string ip)
|
|
||||||
{
|
|
||||||
if(loginFails.contains(ip))
|
|
||||||
{
|
|
||||||
LoginFail &fl = loginFails[ip];
|
|
||||||
std::lock_guard<std::mutex> lock(fl.mutex);
|
|
||||||
return fl.count > 5 && (time(nullptr) - fl.lastfail) < 1200;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Authenticator::incFailureCount(std::string ip)
|
|
||||||
{
|
|
||||||
LoginFail &fl = loginFails[ip];
|
|
||||||
fl.count += 1;
|
|
||||||
fl.lastfail = time(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> Authenticator::pbkdf5(std::string password, const std::vector<char> &salt) const
|
|
||||||
{
|
|
||||||
unsigned char hash[32];
|
|
||||||
const EVP_MD *sha256 = EVP_sha256();
|
|
||||||
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
|
|
||||||
int ret =
|
|
||||||
PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
|
|
||||||
if(ret != 1)
|
|
||||||
{
|
|
||||||
Logger::error() << "Authenticator: pbkdf5: Failed to create hash";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::vector<char> result;
|
|
||||||
|
|
||||||
for(size_t i = 0; i < sizeof(hash); i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
result.push_back(static_cast<char>(hash[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::variant<User, AuthenticationError> Authenticator::authenticate(std::string username, std::string password)
|
|
||||||
{
|
|
||||||
std::optional<User> user = userDao->find(username);
|
|
||||||
if(user)
|
|
||||||
{
|
|
||||||
if(user->enabled)
|
|
||||||
{
|
|
||||||
auto hashresult = pbkdf5(password, user.value().salt);
|
|
||||||
if(hashresult.size() == 0)
|
|
||||||
{
|
|
||||||
return AuthenticationError::GeneralError;
|
|
||||||
}
|
|
||||||
// TODO: timing attack (even though practical relevancy questionable)
|
|
||||||
if(hashresult == user.value().password)
|
|
||||||
{
|
|
||||||
return user.value();
|
|
||||||
}
|
|
||||||
return AuthenticationError::PasswordNotMatch;
|
|
||||||
}
|
|
||||||
return AuthenticationError::UserDisabled;
|
|
||||||
}
|
|
||||||
return AuthenticationError::UserNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::variant<User, AuthenticationError> Authenticator::authenticate(std::string username, std::string password,
|
|
||||||
std::string ip)
|
|
||||||
{
|
|
||||||
if(isBanned(ip))
|
|
||||||
{
|
|
||||||
return AuthenticationError::BannedIP;
|
|
||||||
}
|
|
||||||
std::variant<User, AuthenticationError> authresult = authenticate(username, password);
|
|
||||||
if(std::holds_alternative<AuthenticationError>(authresult))
|
|
||||||
{
|
|
||||||
AuthenticationError error = std::get<AuthenticationError>(authresult);
|
|
||||||
if(error == AuthenticationError::PasswordNotMatch)
|
|
||||||
{
|
|
||||||
incFailureCount(ip);
|
|
||||||
}
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
return std::get<User>(authresult);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> Authenticator::hash(std::string password, const std::vector<char> &salt)
|
|
||||||
{
|
|
||||||
return this->pbkdf5(password, salt);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
#ifndef AUTHENTICATOR_H
|
|
||||||
#define AUTHENTICATOR_H
|
|
||||||
#include <variant>
|
|
||||||
#include "database/userdao.h"
|
|
||||||
|
|
||||||
#define AUTH_DEFAULT_SALT_SIZE 32
|
|
||||||
enum AuthenticationError
|
|
||||||
{
|
|
||||||
UserNotFound,
|
|
||||||
UserDisabled,
|
|
||||||
PasswordNotMatch,
|
|
||||||
BannedIP,
|
|
||||||
GeneralError
|
|
||||||
};
|
|
||||||
|
|
||||||
class Authenticator
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
UserDao *userDao;
|
|
||||||
bool isBanned(std::string ip);
|
|
||||||
void incFailureCount(std::string ip);
|
|
||||||
std::vector<char> pbkdf5(std::string password, const std::vector<char> &salt) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Authenticator(UserDao &userDao);
|
|
||||||
std::variant<User, AuthenticationError> authenticate(std::string username, std::string password);
|
|
||||||
std::variant<User, AuthenticationError> authenticate(std::string username, std::string password, std::string ip);
|
|
||||||
std::vector<char> hash(std::string password, const std::vector<char> &salt);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // AUTHENTICATOR_H
|
|
22
cache/fscache.cpp
vendored
22
cache/fscache.cpp
vendored
@ -7,16 +7,16 @@ FsCache::FsCache(std::string path)
|
|||||||
{
|
{
|
||||||
if(!std::filesystem::exists(path))
|
if(!std::filesystem::exists(path))
|
||||||
{
|
{
|
||||||
throw std::runtime_error{"Cache directory does not exist"};
|
throw std::runtime_error { "Cache directory does not exist" };
|
||||||
}
|
}
|
||||||
this->path = path;
|
this->path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string FsCache::getFilePath(std::string_view path) const
|
std::string FsCache::getFilePath(std::string_view path) const
|
||||||
{
|
{
|
||||||
std::filesystem::path ps{path};
|
std::filesystem::path ps { path };
|
||||||
std::string name = ps.filename();
|
std::string name = ps.filename();
|
||||||
return std::filesystem::path{this->path} / name;
|
return std::filesystem::path { this->path } / name;
|
||||||
}
|
}
|
||||||
std::optional<std::string> FsCache::get(std::string_view key) const
|
std::optional<std::string> FsCache::get(std::string_view key) const
|
||||||
{
|
{
|
||||||
@ -25,12 +25,12 @@ std::optional<std::string> FsCache::get(std::string_view key) const
|
|||||||
{
|
{
|
||||||
return utils::readCompleteFile(path);
|
return utils::readCompleteFile(path);
|
||||||
}
|
}
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
|
|
||||||
void FsCache::put(std::string_view key, std::string val)
|
void FsCache::put(std::string_view key, std::string val)
|
||||||
{
|
{
|
||||||
std::string path = std::filesystem::path{this->path} / key;
|
std::string path = std::filesystem::path { this->path } / key;
|
||||||
std::fstream f1;
|
std::fstream f1;
|
||||||
f1.open(path, std::ios::out);
|
f1.open(path, std::ios::out);
|
||||||
f1 << val;
|
f1 << val;
|
||||||
@ -38,24 +38,24 @@ void FsCache::put(std::string_view key, std::string val)
|
|||||||
|
|
||||||
void FsCache::remove(std::string_view key)
|
void FsCache::remove(std::string_view key)
|
||||||
{
|
{
|
||||||
std::filesystem::remove_all(std::filesystem::path{this->path} / key);
|
std::filesystem::remove_all(std::filesystem::path { this->path} / key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FsCache::removePrefix(std::string_view prefix)
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FsCache::clear()
|
void FsCache::clear()
|
||||||
{
|
{
|
||||||
for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path{this->path}))
|
for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path { this->path }))
|
||||||
{
|
{
|
||||||
std::filesystem::remove_all(entry);
|
std::filesystem::remove_all(entry);
|
||||||
}
|
}
|
||||||
|
9
cache/fscache.h
vendored
9
cache/fscache.h
vendored
@ -3,11 +3,10 @@
|
|||||||
#include "icache.h"
|
#include "icache.h"
|
||||||
class FsCache : public ICache
|
class FsCache : public ICache
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string getFilePath(std::string_view path) const;
|
std::string getFilePath(std::string_view path) const;
|
||||||
|
public:
|
||||||
public:
|
|
||||||
FsCache(std::string directory);
|
FsCache(std::string directory);
|
||||||
std::optional<std::string> get(std::string_view key) const;
|
std::optional<std::string> get(std::string_view key) const;
|
||||||
void put(std::string_view key, std::string val);
|
void put(std::string_view key, std::string val);
|
||||||
@ -15,9 +14,7 @@ class FsCache : public ICache
|
|||||||
void removePrefix(std::string_view prefix);
|
void removePrefix(std::string_view prefix);
|
||||||
void clear();
|
void clear();
|
||||||
using ICache::ICache;
|
using ICache::ICache;
|
||||||
~FsCache()
|
~FsCache() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FSCACHE_H
|
#endif // FSCACHE_H
|
||||||
|
6
cache/icache.h
vendored
6
cache/icache.h
vendored
@ -6,15 +6,13 @@
|
|||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
class ICache
|
class ICache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual std::optional<std::string> get(std::string_view key) const = 0;
|
virtual std::optional<std::string> get(std::string_view key) const = 0;
|
||||||
virtual void put(std::string_view key, std::string val) = 0;
|
virtual void put(std::string_view key, std::string val) = 0;
|
||||||
virtual void remove(std::string_view key) = 0;
|
virtual void remove(std::string_view key) = 0;
|
||||||
virtual void removePrefix(std::string_view prefix) = 0;
|
virtual void removePrefix(std::string_view prefix) = 0;
|
||||||
virtual void clear() = 0;
|
virtual void clear() = 0;
|
||||||
virtual ~ICache()
|
virtual ~ICache() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ICACHE_H
|
#endif // ICACHE_H
|
||||||
|
1
cache/mapcache.cpp
vendored
1
cache/mapcache.cpp
vendored
@ -1 +0,0 @@
|
|||||||
#include "mapcache.h"
|
|
87
cache/mapcache.h
vendored
87
cache/mapcache.h
vendored
@ -1,87 +0,0 @@
|
|||||||
#ifndef MAPCACHE_H
|
|
||||||
#define MAPCACHE_H
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include "icache.h"
|
|
||||||
|
|
||||||
/* Thread-Safe Key-Value store */
|
|
||||||
template <class T> class MapCache
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::unordered_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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(const std::string &key)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::shared_mutex> lock{sharedMutex};
|
|
||||||
this->cache.erase(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removePrefix(const std::string &key)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::shared_mutex> lock{sharedMutex};
|
|
||||||
std::erase_if(this->cache, [key](const auto &item)
|
|
||||||
{
|
|
||||||
auto const& [k, v] = item;
|
|
||||||
return k.starts_with(std::string_view(key));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class StringCache : public MapCache<std::string>, public ICache
|
|
||||||
{
|
|
||||||
virtual std::optional<std::string> get(std::string_view key) const override
|
|
||||||
{
|
|
||||||
return MapCache<std::string>::find(std::string(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void put(std::string_view key, std::string val) override
|
|
||||||
{
|
|
||||||
MapCache<std::string>::set(std::string(key), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void remove(std::string_view key) override
|
|
||||||
{
|
|
||||||
MapCache<std::string>::remove(std::string(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void removePrefix(std::string_view prefix)
|
|
||||||
{
|
|
||||||
MapCache<std::string>::removePrefix(std::string(prefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void clear() override
|
|
||||||
{
|
|
||||||
MapCache<std::string>::clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // MAPCACHE_H
|
|
30
cache/nocache.h
vendored
30
cache/nocache.h
vendored
@ -1,30 +0,0 @@
|
|||||||
#include "icache.h"
|
|
||||||
|
|
||||||
class NoCache : public ICache
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NoCache(std::string p)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
virtual std::optional<std::string> get(std::string_view key) const
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
virtual void put(std::string_view key, std::string val)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
virtual void remove(std::string_view key)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
virtual void removePrefix(std::string_view prefix)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
virtual void clear()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
Category::Category()
|
Category::Category()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
class Category
|
class Category
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Category();
|
Category();
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CATEGORY_H
|
#endif // CATEGORY_H
|
||||||
|
278
cli.cpp
278
cli.cpp
@ -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
94
cli.h
@ -1,94 +0,0 @@
|
|||||||
#ifndef CLI_H
|
|
||||||
#define CLI_H
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "database/database.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
class CLIHandler
|
|
||||||
{
|
|
||||||
struct cmd
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::string helptext;
|
|
||||||
unsigned int required_args;
|
|
||||||
std::vector<cmd> subCommands;
|
|
||||||
std::function<std::pair<bool, std::string>(CLIHandler *, const std::vector<std::string> &)> func;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
Database *db;
|
|
||||||
Config *conf;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::pair<bool, std::string> attach([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> cli_help([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_add([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_change_pw([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_rename([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_set_perms([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_list([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> user_show([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> page_list([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> pageperms_set_permissions([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> version([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> category_list([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> category_delete([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
std::pair<bool, std::string> category_show([[maybe_unused]] const std::vector<std::string> &args);
|
|
||||||
|
|
||||||
std::vector<struct cmd> cmds{
|
|
||||||
{{"user",
|
|
||||||
"user operations on the database",
|
|
||||||
1,
|
|
||||||
{{{"add", "[user] [password] - creates a user", 2, {}, &CLIHandler::user_add},
|
|
||||||
{"changepw", "[user] [password] - changes the password of user", 2, {}, &CLIHandler::user_change_pw},
|
|
||||||
{"rename", "[user] [new name] - renames a user", 2, {}, &CLIHandler::user_rename},
|
|
||||||
{"setperms", "[user] [perms] - sets the permissions of the user", 2, {}, &CLIHandler::user_set_perms},
|
|
||||||
{"list", "- lists users", 0, {}, &CLIHandler::user_list},
|
|
||||||
{"show", "[user] - show detailed information about user", 1, {}, &CLIHandler::user_show}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"page",
|
|
||||||
"operation on pages",
|
|
||||||
1,
|
|
||||||
{{{"list", "- lists existing pages", 0, {}, &CLIHandler::page_list}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"category",
|
|
||||||
"operation on categories",
|
|
||||||
1,
|
|
||||||
{{{"list", "- lists existing categories", 0, {}, &CLIHandler::category_list},
|
|
||||||
{"delete", " - deletes a category", 1, {}, &CLIHandler::category_delete},
|
|
||||||
{"show", " - shows pages of a category", 1, {}, &CLIHandler::category_show}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"pageperms",
|
|
||||||
"set permissions on pages",
|
|
||||||
1,
|
|
||||||
{{{"set",
|
|
||||||
"- [page] [username] [permissions] set permisisons on page",
|
|
||||||
3,
|
|
||||||
{},
|
|
||||||
&CLIHandler::pageperms_set_permissions}}},
|
|
||||||
&CLIHandler::cli_help},
|
|
||||||
{"exit",
|
|
||||||
"exit cli",
|
|
||||||
0,
|
|
||||||
{},
|
|
||||||
[](CLIHandler *, [[maybe_unused]] const std::vector<std::string> &args) -> std::pair<bool, std::string>
|
|
||||||
{
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
return {true, ""};
|
|
||||||
}},
|
|
||||||
{"help", "print this help", 0, {}, &CLIHandler::cli_help},
|
|
||||||
{"attach", "attach to running instance", 0, {}, &CLIHandler::attach},
|
|
||||||
{"version", "print verison info", 0, {}, &CLIHandler::version}}};
|
|
||||||
|
|
||||||
std::pair<bool, std::string> processCommand(const std::vector<CLIHandler::cmd> &commands, std::string cmd,
|
|
||||||
const std::vector<std::string> &args);
|
|
||||||
|
|
||||||
public:
|
|
||||||
CLIHandler(Config &config, Database &d);
|
|
||||||
std::pair<bool, std::string> processCommand(std::string cmd, const std::vector<std::string> &args);
|
|
||||||
static std::pair<std::string, std::vector<std::string>> splitCommand(std::string input);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLI_H
|
|
147
cliconsole.cpp
147
cliconsole.cpp
@ -1,147 +0,0 @@
|
|||||||
#include "cliconsole.h"
|
|
||||||
|
|
||||||
CLIConsole::CLIConsole(CLIHandler &cliHandler, std::string socketPath)
|
|
||||||
{
|
|
||||||
this->handler = &cliHandler;
|
|
||||||
this->socketPath = socketPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> CLIConsole::send(std::string input)
|
|
||||||
{
|
|
||||||
ssize_t ret =
|
|
||||||
sendto(this->sock, input.c_str(), input.size(), 0, (const sockaddr *)&this->server, sizeof(this->server));
|
|
||||||
if((size_t)ret != input.size())
|
|
||||||
{
|
|
||||||
return {false, "sendto failed: " + std::to_string(ret) + " " + std::string(strerror(errno))};
|
|
||||||
}
|
|
||||||
char buffer[1024] = {0};
|
|
||||||
ret = recvfrom(this->sock, buffer, sizeof(buffer) - 1, 0, NULL, NULL);
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
return {false, "recvfrom failed: " + std::string(strerror(errno))};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = false;
|
|
||||||
std::string_view view = buffer;
|
|
||||||
if(view[0] == '1')
|
|
||||||
{
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
view.remove_prefix(1);
|
|
||||||
std::string msg = std::string{view};
|
|
||||||
|
|
||||||
return {success, msg};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CLIConsole::attach()
|
|
||||||
{
|
|
||||||
if(attached)
|
|
||||||
{
|
|
||||||
std::cout << "Already attached" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(socketPath.size() > sizeof(this->server.sun_path) - 1)
|
|
||||||
{
|
|
||||||
std::cout << "Socket path too long" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memset(&this->server, 0, sizeof(this->server));
|
|
||||||
this->server.sun_family = AF_UNIX;
|
|
||||||
memcpy(&this->server.sun_path, socketPath.c_str(), socketPath.size());
|
|
||||||
this->server.sun_path[socketPath.size()] = 0;
|
|
||||||
|
|
||||||
int s = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
||||||
if(s == -1)
|
|
||||||
{
|
|
||||||
std::cout << "Failed to create socket" << strerror(errno) << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->sock = s;
|
|
||||||
|
|
||||||
struct sockaddr_un client;
|
|
||||||
client.sun_family = AF_UNIX;
|
|
||||||
client.sun_path[0] = '\0';
|
|
||||||
|
|
||||||
int ret = bind(this->sock, (struct sockaddr *)&client, sizeof(client));
|
|
||||||
if(ret != 0)
|
|
||||||
{
|
|
||||||
std::cout << "bind() failed: " << strerror(errno) << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto result = this->send("attach");
|
|
||||||
if(result.first)
|
|
||||||
{
|
|
||||||
std::cout << "Attached successfully: " << result.second << std::endl;
|
|
||||||
this->attached = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cout << "Attached unsuccessfully: " << result.second << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CLIConsole::startInteractive()
|
|
||||||
{
|
|
||||||
std::cout << "qswiki CLI" << std::endl;
|
|
||||||
std::cout << "not attached - use 'attach' to connect to running instance" << std::endl;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
std::string input;
|
|
||||||
std::cout << "> ";
|
|
||||||
std::getline(std::cin, input);
|
|
||||||
|
|
||||||
if(std::cin.bad() || std::cin.eof())
|
|
||||||
{
|
|
||||||
std::cout << "Exiting" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(input.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pair = CLIHandler::splitCommand(input);
|
|
||||||
if(pair.first == "exit")
|
|
||||||
{
|
|
||||||
if(attached)
|
|
||||||
{
|
|
||||||
std::cout << "You are attached. Quit attached instance too (y) or only this one(n)" << std::endl;
|
|
||||||
char response;
|
|
||||||
std::cin >> response;
|
|
||||||
if(response == 'y')
|
|
||||||
{
|
|
||||||
this->send("exit");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::cout << "Exiting CLI" << std::endl;
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
if(pair.first == "attach")
|
|
||||||
{
|
|
||||||
attach();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<bool, std::string> result;
|
|
||||||
if(!attached)
|
|
||||||
{
|
|
||||||
result = handler->processCommand(pair.first, pair.second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = this->send(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!result.second.empty())
|
|
||||||
{
|
|
||||||
std::cout << result.second << std::endl;
|
|
||||||
}
|
|
||||||
if(!result.first)
|
|
||||||
{
|
|
||||||
std::cout << "Command failed" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
27
cliconsole.h
27
cliconsole.h
@ -1,27 +0,0 @@
|
|||||||
#ifndef CLICONSOLE_H
|
|
||||||
#define CLICONSOLE_H
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include "cli.h"
|
|
||||||
|
|
||||||
class CLIConsole
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
struct sockaddr_un server;
|
|
||||||
int sock;
|
|
||||||
CLIHandler *handler;
|
|
||||||
std::string socketPath;
|
|
||||||
bool attached = false;
|
|
||||||
std::pair<bool, std::string> send(std::string input);
|
|
||||||
void attach();
|
|
||||||
|
|
||||||
public:
|
|
||||||
CLIConsole(CLIHandler &cliHandler, std::string socketPath);
|
|
||||||
void startInteractive();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLICONSOLE_H
|
|
@ -1,77 +0,0 @@
|
|||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "cliserver.h"
|
|
||||||
#include "logger.h"
|
|
||||||
CLIServer::CLIServer(CLIHandler &handler)
|
|
||||||
{
|
|
||||||
this->handler = &handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CLIServer::detachServer(std::string socketpath)
|
|
||||||
{
|
|
||||||
struct sockaddr_un name;
|
|
||||||
const int max_socket_length = sizeof(name.sun_path) - 1;
|
|
||||||
if(socketpath.size() > max_socket_length)
|
|
||||||
{
|
|
||||||
perror("socket path too long");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int s = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
||||||
if(s == -1)
|
|
||||||
{
|
|
||||||
perror("socket");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&name, 0, sizeof(name));
|
|
||||||
name.sun_family = AF_UNIX;
|
|
||||||
memcpy(&name.sun_path, socketpath.c_str(), socketpath.size());
|
|
||||||
|
|
||||||
unlink(socketpath.c_str());
|
|
||||||
int ret = bind(s, (const struct sockaddr *)&name, sizeof(name));
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
perror("bind");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto worker = [this, s]
|
|
||||||
{
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
char buffer[1024] = {0};
|
|
||||||
struct sockaddr_un peer;
|
|
||||||
socklen_t peerlen = sizeof(peer);
|
|
||||||
|
|
||||||
int ret = recvfrom(s, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &peerlen);
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
Logger::error() << "Error during recvfrom in CLI server: " << strerror(errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string input{buffer};
|
|
||||||
|
|
||||||
auto pair = CLIHandler::splitCommand(input);
|
|
||||||
|
|
||||||
auto result = handler->processCommand(pair.first, pair.second);
|
|
||||||
char resultCode = '0';
|
|
||||||
if(result.first)
|
|
||||||
{
|
|
||||||
resultCode = '1';
|
|
||||||
}
|
|
||||||
std::string resultString;
|
|
||||||
resultString += resultCode;
|
|
||||||
resultString += result.second;
|
|
||||||
ret = sendto(s, resultString.c_str(), resultString.size(), 0, (struct sockaddr *)&peer, peerlen);
|
|
||||||
if(ret == -1)
|
|
||||||
{
|
|
||||||
Logger::error() << "Error during sendto in CLI server: " << strerror(errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::thread t1{worker};
|
|
||||||
t1.detach();
|
|
||||||
return true;
|
|
||||||
}
|
|
16
cliserver.h
16
cliserver.h
@ -1,16 +0,0 @@
|
|||||||
#ifndef CLISERVER_H
|
|
||||||
#define CLISERVER_H
|
|
||||||
#include <thread>
|
|
||||||
#include "cli.h"
|
|
||||||
|
|
||||||
class CLIServer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
CLIHandler *handler = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CLIServer(CLIHandler &handler);
|
|
||||||
bool detachServer(std::string socketpath);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CLISERVER_H
|
|
35
config.cpp
35
config.cpp
@ -24,15 +24,14 @@ 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);
|
||||||
if(it != this->configmap.end())
|
if(it != this->configmap.end())
|
||||||
{
|
{
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Required config key " + key + " not found");
|
throw std::runtime_error("Required config key " + key + " not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Config::optional(const std::string &key, std::string defaultvalue)
|
std::string Config::optional(const std::string &key, std::string defaultvalue)
|
||||||
@ -67,6 +66,8 @@ uint64_t Config::optional(const std::string &key, uint64_t defaultvalue)
|
|||||||
return defaultvalue;
|
return defaultvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Config::Config(const std::map<std::string, std::string> &map)
|
Config::Config(const std::map<std::string, std::string> &map)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -78,16 +79,13 @@ Config::Config(const std::map<std::string, std::string> &map)
|
|||||||
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");
|
||||||
@ -99,11 +97,10 @@ Config::Config(const std::map<std::string, std::string> &map)
|
|||||||
this->urls.settingsurl = required("settingsurl");
|
this->urls.settingsurl = required("settingsurl");
|
||||||
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.userchangepwurl = required("userchangepwurl");
|
||||||
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);
|
||||||
this->session_max_lifetime = optional("session_max_lifetime", 3600);
|
this->session_max_lifetime = optional("session_max_lifetime", 3600);
|
||||||
this->handlersConfig.query_limit = optional("query_limit", 200);
|
this->handlersConfig.query_limit = optional("query_limit", 200);
|
||||||
@ -114,14 +111,15 @@ Config::Config(const std::map<std::string, std::string> &map)
|
|||||||
|
|
||||||
this->templateprefix = "{qswiki:";
|
this->templateprefix = "{qswiki:";
|
||||||
|
|
||||||
this->max_payload_length = optional("max_payload_length", 60 * 1024 * 1024);
|
this->max_payload_length = optional("max_payload_length", 10 *1024*1024);
|
||||||
|
|
||||||
ConfigVariableResolver resolver{this->configmap};
|
ConfigVariableResolver resolver { this->configmap };
|
||||||
this->configVarResolver = resolver;
|
this->configVarResolver = resolver;
|
||||||
|
|
||||||
Varreplacer replacer("{");
|
Varreplacer replacer("{");
|
||||||
replacer.addKeyValue("wikiname", this->handlersConfig.wikiname);
|
replacer.addKeyValue("wikiname", this->handlersConfig.wikiname);
|
||||||
this->handlersConfig.page_title_template = replacer.parse(this->handlersConfig.page_title_template);
|
this->handlersConfig.page_title_template = replacer.parse(this->handlersConfig.page_title_template);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigReader::ConfigReader(const std::string &file)
|
ConfigReader::ConfigReader(const std::string &file)
|
||||||
@ -136,16 +134,17 @@ Config ConfigReader::readConfig()
|
|||||||
std::map<std::string, std::string> configmap;
|
std::map<std::string, std::string> configmap;
|
||||||
while(getline(f1, line))
|
while(getline(f1, line))
|
||||||
{
|
{
|
||||||
if(isspace(line[0]) || line[0] == '#')
|
if(isspace(line[0]) || line[0] == '#') {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::stringstream s(line);
|
std::stringstream s(line);
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
s >> key >> value;
|
s >> key >> value;
|
||||||
|
|
||||||
configmap.insert(std::make_pair(std::move(key), std::move(value)));
|
configmap.insert(std::make_pair(std::move(key), std::move(value)));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return Config(configmap);
|
return Config(configmap);
|
||||||
}
|
}
|
||||||
|
44
config.h
44
config.h
@ -16,6 +16,7 @@ struct HandlerConfig
|
|||||||
std::string page_title_template;
|
std::string page_title_template;
|
||||||
int max_pagename_length;
|
int max_pagename_length;
|
||||||
int query_limit;
|
int query_limit;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConfigUrls
|
struct ConfigUrls
|
||||||
@ -23,19 +24,16 @@ 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;
|
|
||||||
std::string linksettings;
|
std::string linksettings;
|
||||||
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;
|
||||||
@ -43,19 +41,19 @@ struct ConfigUrls
|
|||||||
std::string deletionurl;
|
std::string deletionurl;
|
||||||
std::string linkhistorysort;
|
std::string linkhistorysort;
|
||||||
std::string adminregisterurl;
|
std::string adminregisterurl;
|
||||||
std::string usersettingsurl;
|
std::string userchangepwurl;
|
||||||
std::string rooturl;
|
|
||||||
std::string atomurl;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ConfigVariableResolver
|
class ConfigVariableResolver
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
const std::map<std::string, std::string> *configmap;
|
const std::map<std::string, std::string> *configmap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConfigVariableResolver()
|
ConfigVariableResolver()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigVariableResolver(const std::map<std::string, std::string> &configmap)
|
ConfigVariableResolver(const std::map<std::string, std::string> &configmap)
|
||||||
@ -67,20 +65,21 @@ class ConfigVariableResolver
|
|||||||
{
|
{
|
||||||
return utils::getKeyOrEmpty(*configmap, key);
|
return utils::getKeyOrEmpty(*configmap, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::map<std::string, std::string> configmap;
|
std::map<std::string, std::string> configmap;
|
||||||
std::string required(const std::string &key);
|
std::string required(const std::string &key);
|
||||||
|
|
||||||
std::string optional(const std::string &key, std::string defaultvalue = "");
|
std::string optional(const std::string &key, std::string defaultvalue = "");
|
||||||
int optional(const std::string &key, int defaulvalue);
|
int optional(const std::string &key, int defaulvalue);
|
||||||
uint64_t optional(const std::string &key, uint64_t defaultvalue);
|
uint64_t optional(const std::string &key, uint64_t defaultvalue);
|
||||||
|
public:
|
||||||
public:
|
Config(const std::map<std::string, std::string> &map );
|
||||||
Config(const std::map<std::string, std::string> &map);
|
|
||||||
|
|
||||||
ConfigUrls urls;
|
ConfigUrls urls;
|
||||||
ConfigVariableResolver configVarResolver;
|
ConfigVariableResolver configVarResolver;
|
||||||
@ -95,16 +94,21 @@ class Config
|
|||||||
int threadscount;
|
int threadscount;
|
||||||
|
|
||||||
uint64_t max_payload_length;
|
uint64_t max_payload_length;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConfigReader
|
class ConfigReader
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string path;
|
std::string path;
|
||||||
|
public:
|
||||||
public:
|
|
||||||
ConfigReader(const std::string &file);
|
ConfigReader(const std::string &file);
|
||||||
Config readConfig();
|
Config readConfig();
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
||||||
|
2
cookie.h
2
cookie.h
@ -4,7 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
class Cookie
|
class Cookie
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string key;
|
std::string key;
|
||||||
std::string value;
|
std::string value;
|
||||||
int expires;
|
int expires;
|
||||||
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
CategoryDao::CategoryDao()
|
CategoryDao::CategoryDao()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,17 +5,17 @@
|
|||||||
#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:
|
||||||
CategoryDao();
|
CategoryDao();
|
||||||
virtual void save(const Category &c) = 0;
|
virtual void save(const Category &c) = 0;
|
||||||
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;
|
||||||
virtual ~CategoryDao() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CATEGORYDAO_H
|
#endif // CATEGORYDAO_H
|
||||||
|
@ -24,12 +24,12 @@ SOFTWARE.
|
|||||||
#include "sqlitequeryoption.h"
|
#include "sqlitequeryoption.h"
|
||||||
CategoryDaoSqlite::CategoryDaoSqlite()
|
CategoryDaoSqlite::CategoryDaoSqlite()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Category> CategoryDaoSqlite::find(std::string name)
|
std::optional<Category> CategoryDaoSqlite::find(std::string name)
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
Category result;
|
Category result;
|
||||||
*db << "SELECT id, name FROM category WHERE name = ?" << name >> std::tie(result.id, result.name);
|
*db << "SELECT id, name FROM category WHERE name = ?" << name >> std::tie(result.id, result.name);
|
||||||
return result;
|
return result;
|
||||||
@ -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)
|
||||||
@ -50,9 +49,7 @@ void CategoryDaoSqlite::save(const Category &c)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
*db << "INSERT OR IGNORE INTO category (id, name) VALUES (SELECT id FROM category WHERE lower(name) = "
|
*db << "INSERT OR IGNORE INTO category (id, name) VALUES (SELECT id FROM category WHERE lower(name) = lower(?), lower(?)" <<c.name << c.name;
|
||||||
"lower(?), lower(?)"
|
|
||||||
<< c.name << c.name;
|
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
@ -65,14 +62,13 @@ 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)
|
||||||
{
|
{
|
||||||
*db << "ROLLBACK";
|
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,7 +79,8 @@ std::vector<std::string> CategoryDaoSqlite::fetchList(QueryOption o)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto queryoption = SqliteQueryOption(o).setPrependWhere(true).setOrderByColumn("name").build();
|
auto queryoption = SqliteQueryOption(o).setPrependWhere(true).setOrderByColumn("name").build();
|
||||||
*db << "SELECT name FROM category " + queryoption >> [&](std::string n) { result.push_back(n); };
|
*db << "SELECT name FROM category " + queryoption >> [&](std::string n) { result.push_back(n);};
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(const sqlite::exceptions::no_rows &e)
|
catch(const sqlite::exceptions::no_rows &e)
|
||||||
{
|
{
|
||||||
@ -95,36 +92,18 @@ 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 };
|
||||||
|
std::string queryoptions = queryOption.setOrderByColumn("name").setVisibleColumnName("page.visible").setPrependWhere(false).build();
|
||||||
|
|
||||||
SqliteQueryOption queryOption{o};
|
|
||||||
std::string queryoptions =
|
|
||||||
queryOption.setOrderByColumn("name").setListedColumnName("page.listed").setPrependWhere(false).build();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto query =
|
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 " + queryoptions << name;
|
||||||
*db
|
query >> [&](std::string p) { result.push_back(p);};
|
||||||
<< "SELECT page.id, page.name AS name, page.title, page.lastrevision, page.listed, page.feedlisted FROM "
|
|
||||||
"categorymember INNER JOIN page ON page.id = "
|
|
||||||
"categorymember.page WHERE category = (SELECT id FROM category WHERE name = ? ) AND " +
|
|
||||||
queryoptions
|
|
||||||
<< name;
|
|
||||||
query >> [&](unsigned int id, std::string name, std::string title, unsigned int lastrevision, bool listed,
|
|
||||||
bool feedlisted)
|
|
||||||
{
|
|
||||||
Page p;
|
|
||||||
p.name = name;
|
|
||||||
p.pageid = id;
|
|
||||||
p.title = title;
|
|
||||||
p.current_revision = lastrevision;
|
|
||||||
p.listed = listed;
|
|
||||||
p.feedlisted = feedlisted;
|
|
||||||
result.push_back(p);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
catch(const sqlite::exceptions::no_rows &e)
|
catch(const sqlite::exceptions::no_rows &e)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -13,19 +13,13 @@
|
|||||||
#include "permissionsdao.h"
|
#include "permissionsdao.h"
|
||||||
class Database
|
class Database
|
||||||
{
|
{
|
||||||
protected:
|
private:
|
||||||
std::string connnectionstring;
|
std::string connnectionstring;
|
||||||
|
public:
|
||||||
|
Database() { }
|
||||||
|
Database(std::string connstring) { this->connnectionstring = connstring; }
|
||||||
|
|
||||||
public:
|
virtual void beginTransaction() = 0;
|
||||||
Database()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
Database(std::string connstring)
|
|
||||||
{
|
|
||||||
this->connnectionstring = connstring;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void beginTransaction() = 0;
|
|
||||||
virtual void rollbackTransaction() = 0;
|
virtual void rollbackTransaction() = 0;
|
||||||
virtual void commitTransaction() = 0;
|
virtual void commitTransaction() = 0;
|
||||||
virtual std::unique_ptr<PageDao> createPageDao() const = 0;
|
virtual std::unique_ptr<PageDao> createPageDao() const = 0;
|
||||||
@ -34,9 +28,7 @@ class Database
|
|||||||
virtual std::unique_ptr<UserDao> createUserDao() const = 0;
|
virtual std::unique_ptr<UserDao> createUserDao() const = 0;
|
||||||
virtual std::unique_ptr<CategoryDao> createCategoryDao() const = 0;
|
virtual std::unique_ptr<CategoryDao> createCategoryDao() const = 0;
|
||||||
virtual std::unique_ptr<PermissionsDao> createPermissionsDao() const = 0;
|
virtual std::unique_ptr<PermissionsDao> createPermissionsDao() const = 0;
|
||||||
virtual ~Database()
|
virtual ~Database() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
#define EXCEPTIONS_H
|
#define EXCEPTIONS_H
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
class DatabaseException : public std::runtime_error
|
|
||||||
|
class DatabaseException : public std::runtime_error
|
||||||
{
|
{
|
||||||
using std::runtime_error::runtime_error;
|
using std::runtime_error::runtime_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DatabaseQueryException : public DatabaseException
|
class DatabaseQueryException : public DatabaseException
|
||||||
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
PageDao::PageDao()
|
PageDao::PageDao()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,26 +8,21 @@
|
|||||||
#include "../searchresult.h"
|
#include "../searchresult.h"
|
||||||
class PageDao
|
class PageDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PageDao();
|
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<std::string> getPageList(QueryOption option) = 0;
|
||||||
virtual std::vector<Page> 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;
|
||||||
// TODO: this may not be the correct place for this.
|
//TODO: this may not be the correct place for this.
|
||||||
virtual void setCategories(std::string pagename, const std::vector<std::string> &catnames) = 0;
|
virtual void setCategories(std::string pagename, const std::vector<std::string> &catnames) = 0;
|
||||||
virtual std::vector<SearchResult> search(std::string query, QueryOption option) = 0;
|
virtual std::vector<SearchResult> search(std::string query, QueryOption option) = 0;
|
||||||
|
|
||||||
virtual std::vector<std::string> getChildren(std::string pagename) = 0;
|
virtual ~PageDao() { }
|
||||||
|
|
||||||
virtual ~PageDao()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PAGEDAO_H
|
#endif // PAGEDAO_H
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2018 Albert S.
|
/* Copyright (c) 2018 Albert S.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -23,14 +23,13 @@ SOFTWARE.
|
|||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "sqlitequeryoption.h"
|
#include "sqlitequeryoption.h"
|
||||||
#include "../logger.h"
|
#include "../logger.h"
|
||||||
#include "../utils.h"
|
|
||||||
|
|
||||||
/* TODO: copied from C version mostly, review whether access to table other than page is ok */
|
/* TODO: copied from C version mostly, review whether access to table other than page is ok */
|
||||||
|
|
||||||
bool PageDaoSqlite::exists(unsigned int id) const
|
bool PageDaoSqlite::exists(unsigned int id) const
|
||||||
{
|
{
|
||||||
auto binder = *db << "SELECT 1 from page WHERE id = ?" << id;
|
auto binder = *db << "SELECT 1 from page WHERE id = ?" << id ;
|
||||||
return execBool(binder);
|
return execBool(binder);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageDaoSqlite::exists(std::string name) const
|
bool PageDaoSqlite::exists(std::string name) const
|
||||||
@ -48,53 +47,25 @@ std::optional<Page> PageDaoSqlite::find(std::string name)
|
|||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Page> PageDaoSqlite::findByTitle(std::string title)
|
|
||||||
{
|
|
||||||
Page result;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto ps =
|
|
||||||
*db
|
|
||||||
<< "SELECT id, name, title, lastrevision, listed, feedlisted, (SELECT name FROM page WHERE id = parent) "
|
|
||||||
"FROM page WHERE title = ?";
|
|
||||||
ps << title >> std::tie(result.pageid, result.name, result.title, result.current_revision, result.listed,
|
|
||||||
result.feedlisted, result.parentpage);
|
|
||||||
}
|
|
||||||
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 =
|
auto ps = *db << "SELECT name, lastrevision, visible FROM page WHERE id = ?";
|
||||||
*db
|
|
||||||
<< "SELECT name, title, lastrevision, listed, feedlisted, (SELECT name FROM page WHERE id = parent) FROM "
|
|
||||||
"page WHERE id = ?";
|
|
||||||
|
|
||||||
ps << id >> std::tie(result.name, result.title, result.current_revision, result.listed, result.feedlisted,
|
ps << id >> std::tie(result.name, result.current_revision, result.listed);
|
||||||
result.parentpage);
|
|
||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception& e)
|
||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
@ -105,7 +76,7 @@ std::optional<Page> PageDaoSqlite::find(unsigned int id)
|
|||||||
void PageDaoSqlite::deletePage(std::string page)
|
void PageDaoSqlite::deletePage(std::string page)
|
||||||
{
|
{
|
||||||
int pageId = this->fetchPageId(page);
|
int pageId = this->fetchPageId(page);
|
||||||
// TODO on delete cascade is better most certainly
|
//TODO on delete cascade is better most certainly
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
*db << "BEGIN;";
|
*db << "BEGIN;";
|
||||||
@ -114,55 +85,40 @@ void PageDaoSqlite::deletePage(std::string page)
|
|||||||
*db << "DELETE FROM permissions WHERE page = ?;" << pageId;
|
*db << "DELETE FROM permissions WHERE page = ?;" << pageId;
|
||||||
*db << "DELETE FROM page WHERE id =?;" << pageId;
|
*db << "DELETE FROM page WHERE id =?;" << pageId;
|
||||||
*db << "COMMIT;";
|
*db << "COMMIT;";
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
*db << "ROLLBACK";
|
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageDaoSqlite::save(const Page &page)
|
void PageDaoSqlite::save(const Page &page)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
*db << "INSERT OR REPLACE INTO page (id, name, title, lastrevision, listed, feedlisted, parent) VALUES((SELECT "
|
*db << "INSERT OR REPLACE INTO page (id, name, lastrevision, visible) VALUES((SELECT id FROM page WHERE name = ? OR id = ?), ?, ?, ?)" << page.name << page.pageid << page.name << page.current_revision << page.listed;
|
||||||
"id FROM page WHERE name = ? OR id = ?), ?, ?, ?, ?, ?, (SELECT id FROM page WHERE name = ?))"
|
|
||||||
<< page.name << page.pageid << page.name << page.title << page.current_revision << page.listed
|
|
||||||
<< page.feedlisted << page.parentpage;
|
|
||||||
}
|
}
|
||||||
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)
|
|
||||||
.setOrderByColumn("lower(name)")
|
std::string queryOption = SqliteQueryOption(option).setOrderByColumn("lower(name)").setVisibleColumnName("visible").setPrependWhere(true).build();
|
||||||
.setListedColumnName("listed")
|
std::string query = "SELECT name FROM page " + queryOption;
|
||||||
.setPrependWhere(true)
|
|
||||||
.build();
|
*db << query >> [&](std::string name)
|
||||||
std::string query = "SELECT id, name, title, lastrevision, listed, feedlisted, (SELECT name FROM page WHERE "
|
|
||||||
"id = parent) FROM page " +
|
|
||||||
queryOption;
|
|
||||||
*db << query >> [&](unsigned int pageid, std::string name, std::string title, unsigned int current_revision,
|
|
||||||
bool listed, bool feedlisted, std::string parent)
|
|
||||||
{
|
{
|
||||||
Page tmp;
|
result.push_back(name);
|
||||||
tmp.pageid = pageid;
|
|
||||||
tmp.name = name;
|
|
||||||
tmp.title = title;
|
|
||||||
tmp.current_revision = current_revision;
|
|
||||||
tmp.listed = listed;
|
|
||||||
tmp.feedlisted = feedlisted;
|
|
||||||
tmp.parentpage = parent;
|
|
||||||
result.push_back(tmp);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
@ -181,11 +137,9 @@ std::vector<std::string> PageDaoSqlite::fetchCategories(std::string pagename, Qu
|
|||||||
std::vector<std::string> result;
|
std::vector<std::string> result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto query = *db << "SELECT name FROM categorymember INNNER JOIN category ON category = category.id WHERE page "
|
auto query = *db << "SELECT name FROM categorymember INNNER JOIN category ON category = category.id WHERE page = (SELECT id FROM page WHERE name = ?)" << pagename;
|
||||||
"= (SELECT id FROM page WHERE name = ?)"
|
|
||||||
<< pagename;
|
|
||||||
query << " AND " << SqliteQueryOption(option).setPrependWhere(false).setOrderByColumn("name").build();
|
query << " AND " << SqliteQueryOption(option).setPrependWhere(false).setOrderByColumn("name").build();
|
||||||
query >> [&](std::string pagename) { result.push_back(pagename); };
|
query >> [&](std::string pagename) { result.push_back(pagename);};
|
||||||
}
|
}
|
||||||
catch(const sqlite::exceptions::no_rows &e)
|
catch(const sqlite::exceptions::no_rows &e)
|
||||||
{
|
{
|
||||||
@ -199,22 +153,6 @@ std::vector<std::string> PageDaoSqlite::fetchCategories(std::string pagename, Qu
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string PageDaoSqlite::ftsEscape(std::string input)
|
|
||||||
{
|
|
||||||
std::string result = "";
|
|
||||||
for(auto &str : utils::split(input, ' '))
|
|
||||||
{
|
|
||||||
std::string tmp = utils::strreplace(str, "\"", "\"\"");
|
|
||||||
tmp = "\"" + tmp + "\"" + " ";
|
|
||||||
result += tmp;
|
|
||||||
}
|
|
||||||
if(!result.empty())
|
|
||||||
{
|
|
||||||
result.pop_back();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SearchResult> PageDaoSqlite::search(std::string name, QueryOption option)
|
std::vector<SearchResult> PageDaoSqlite::search(std::string name, QueryOption option)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -222,12 +160,9 @@ std::vector<SearchResult> PageDaoSqlite::search(std::string name, QueryOption op
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string qo = SqliteQueryOption(option).setPrependWhere(false).setOrderByColumn("rank").build();
|
std::string qo = SqliteQueryOption(option).setPrependWhere(false).setOrderByColumn("rank").build();
|
||||||
|
//TODO: what is passed here, simple gets thrown to the MATCH operator without escaping or anything and this is suboptimal
|
||||||
auto query =
|
auto query = *db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? " << name;
|
||||||
*db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? "
|
query >> [&](std::string pagename) {
|
||||||
<< ftsEscape(name);
|
|
||||||
query >> [&](std::string pagename)
|
|
||||||
{
|
|
||||||
SearchResult sresult;
|
SearchResult sresult;
|
||||||
sresult.pagename = pagename;
|
sresult.pagename = pagename;
|
||||||
sresult.query = name;
|
sresult.query = name;
|
||||||
@ -254,12 +189,9 @@ void PageDaoSqlite::setCategories(std::string pagename, const std::vector<std::s
|
|||||||
*db << "DELETE FROM categorymember WHERE page = ?" << pageid;
|
*db << "DELETE FROM categorymember WHERE page = ?" << pageid;
|
||||||
for(const std::string &cat : catnames)
|
for(const std::string &cat : catnames)
|
||||||
{
|
{
|
||||||
*db << "INSERT OR IGNORE INTO category (id, name) VALUES( (SELECT id FROM category WHERE lower(name) = "
|
*db << "INSERT OR IGNORE INTO category (id, name) VALUES( (SELECT id FROM category WHERE lower(name) = lower(?)), lower(?))" << cat << cat;
|
||||||
"lower(?)), lower(?))"
|
*db << "INSERT INTO categorymember (category, page) VALUES ( (SELECT ID FROM category WHERE lower(name) = lower(?)), ?)" << cat << pageid;
|
||||||
<< cat << cat;
|
|
||||||
*db << "INSERT INTO categorymember (category, page) VALUES ( (SELECT ID FROM category WHERE lower(name) = "
|
|
||||||
"lower(?)), ?)"
|
|
||||||
<< cat << pageid;
|
|
||||||
}
|
}
|
||||||
*db << "release setcategories;";
|
*db << "release setcategories;";
|
||||||
}
|
}
|
||||||
@ -269,16 +201,9 @@ void PageDaoSqlite::setCategories(std::string pagename, const std::vector<std::s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PageDaoSqlite::fetchPageId(std::string pagename)
|
int PageDaoSqlite::fetchPageId(std::string pagename)
|
||||||
{
|
{
|
||||||
auto binder = *db << "SELECT id FROM page WHERE name = ?" << pagename;
|
auto binder = *db << "SELECT id FROM page WHERE name = ?" << pagename;
|
||||||
return execInt(binder);
|
return execInt(binder);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> PageDaoSqlite::getChildren(std::string pagename)
|
|
||||||
{
|
|
||||||
std::vector<std::string> result;
|
|
||||||
auto query = *db << "SELECT name FROM page WHERE parent = (SELECT id FROM page WHERE name = ?)" << pagename;
|
|
||||||
query >> [&](std::string page) { result.push_back(page); };
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -8,27 +8,20 @@
|
|||||||
#include "sqlitedao.h"
|
#include "sqlitedao.h"
|
||||||
class PageDaoSqlite : public PageDao, protected SqliteDao
|
class PageDaoSqlite : public PageDao, protected SqliteDao
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
std::string ftsEscape(std::string input);
|
PageDaoSqlite() { }
|
||||||
|
|
||||||
public:
|
|
||||||
PageDaoSqlite()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void deletePage(std::string page) override;
|
void deletePage(std::string page) override;
|
||||||
bool exists(unsigned int id) const override;
|
bool exists(unsigned int id) const override;
|
||||||
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<std::string> getPageList(QueryOption option) override;
|
||||||
std::vector<Page> 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);
|
||||||
std::vector<SearchResult> search(std::string query, QueryOption option) override;
|
std::vector<SearchResult> search(std::string query, QueryOption option) override;
|
||||||
void setCategories(std::string pagename, const std::vector<std::string> &catnames) override;
|
void setCategories(std::string pagename, const std::vector<std::string> &catnames) override;
|
||||||
std::vector<std::string> getChildren(std::string pagename) override;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
PermissionsDao::PermissionsDao()
|
PermissionsDao::PermissionsDao()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
#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
|
||||||
{
|
{
|
||||||
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;
|
|
||||||
virtual void clearForPage(std::string pagename) = 0;
|
|
||||||
|
|
||||||
virtual ~PermissionsDao() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PERMISSIONSDAO_H
|
#endif // PERMISSIONSDAO_H
|
||||||
|
@ -22,12 +22,12 @@ SOFTWARE.
|
|||||||
|
|
||||||
PermissionsDaoSqlite::PermissionsDaoSqlite()
|
PermissionsDaoSqlite::PermissionsDaoSqlite()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Permissions> PermissionsDaoSqlite::find(std::string pagename, std::string username)
|
std::optional<Permissions> PermissionsDaoSqlite::find(std::string pagename, std::string username)
|
||||||
{
|
{
|
||||||
auto query = *db << "SELECT permissions FROM permissions WHERE page = (SELECT id FROM page WHERE name = ?) AND "
|
auto query = *db << "SELECT permissions FROM permissions WHERE page = (SELECT id FROM page WHERE name = ?) AND userid = (SELECT id FROM user WHERE username = ?)";
|
||||||
"userid = (SELECT id FROM user WHERE username = ?)";
|
|
||||||
query << pagename << username;
|
query << pagename << username;
|
||||||
int permissions = 0;
|
int permissions = 0;
|
||||||
try
|
try
|
||||||
@ -36,39 +36,8 @@ std::optional<Permissions> PermissionsDaoSqlite::find(std::string pagename, std:
|
|||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PermissionsDaoSqlite::clearForPage(std::string pagename)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto stmt = *db << "DELETE FROM permissions WHERE page = (SELECT id FROM page WHERE name = ?)" << pagename;
|
|
||||||
stmt.execute();
|
|
||||||
}
|
|
||||||
catch(sqlite::sqlite_exception &e)
|
|
||||||
{
|
|
||||||
throwFrom(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
|
|
||||||
class PermissionsDaoSqlite : public PermissionsDao, protected SqliteDao
|
class PermissionsDaoSqlite : public PermissionsDao, protected SqliteDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
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;
|
|
||||||
virtual void clearForPage(std::string pagename) override;
|
|
||||||
using SqliteDao::SqliteDao;
|
using SqliteDao::SqliteDao;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,3 +19,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "queryoption.h"
|
#include "queryoption.h"
|
||||||
|
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
enum SORT_ORDER
|
enum SORT_ORDER
|
||||||
{
|
{
|
||||||
ASCENDING = 0,
|
ASCENDING=0,
|
||||||
DESCENDING
|
DESCENDING
|
||||||
};
|
};
|
||||||
|
|
||||||
class QueryOption
|
class QueryOption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
unsigned int limit = 0;
|
unsigned int limit = 0;
|
||||||
SORT_ORDER order = ASCENDING;
|
SORT_ORDER order = ASCENDING;
|
||||||
bool includeUnlisted = true;
|
bool includeInvisible = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QUERYOPTION_H
|
#endif // QUERYOPTION_H
|
||||||
|
@ -19,3 +19,4 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include "revisiondao.h"
|
#include "revisiondao.h"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include "queryoption.h"
|
#include "queryoption.h"
|
||||||
class RevisionDao
|
class RevisionDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void save(const Revision &revision) = 0;
|
virtual void save(const Revision &revision) = 0;
|
||||||
virtual std::vector<Revision> getAllRevisions(QueryOption &options) = 0;
|
virtual std::vector<Revision> getAllRevisions(QueryOption &options) = 0;
|
||||||
virtual std::vector<Revision> getAllRevisionsForPage(std::string pagename, QueryOption &option) = 0;
|
virtual std::vector<Revision> getAllRevisionsForPage(std::string pagename, QueryOption &option) = 0;
|
||||||
@ -15,9 +15,8 @@ class RevisionDao
|
|||||||
virtual unsigned int countTotalRevisions() = 0;
|
virtual unsigned int countTotalRevisions() = 0;
|
||||||
virtual unsigned int countTotalRevisions(std::string pagename) = 0;
|
virtual unsigned int countTotalRevisions(std::string pagename) = 0;
|
||||||
|
|
||||||
virtual ~RevisionDao()
|
virtual ~RevisionDao() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // REVISIONDAO_H
|
#endif // REVISIONDAO_H
|
||||||
|
@ -24,17 +24,18 @@ SOFTWARE.
|
|||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
RevisionDaoSqlite::RevisionDaoSqlite()
|
RevisionDaoSqlite::RevisionDaoSqlite()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void RevisionDaoSqlite::save(const Revision &revision)
|
void RevisionDaoSqlite::save(const Revision &revision)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
*db << "savepoint revisionsubmit;";
|
*db << "savepoint revisionsubmit;";
|
||||||
*db << "INSERT INTO revision(author, comment, content, creationtime, page, revisionid) VALUES((SELECT id FROM "
|
*db << "INSERT INTO revision(author, comment, content, creationtime, page, revisionid) VALUES((SELECT id FROM user WHERE username = ?), ?, ?, DATETIME(), (SELECT id FROM page WHERE name = ?), (SELECT lastrevision+1 FROM page WHERE id = (SELECT id FROM page WHERE name = ?)));" <<
|
||||||
"user WHERE username = ?), ?, ?, DATETIME(), (SELECT id FROM page WHERE name = ?), (SELECT "
|
revision.author << revision.comment << revision.content << revision.page << revision.page;
|
||||||
"lastrevision+1 FROM page WHERE id = (SELECT id FROM page WHERE name = ?)));"
|
|
||||||
<< revision.author << revision.comment << revision.content << revision.page << revision.page;
|
|
||||||
*db << "UPDATE page SET lastrevision=lastrevision+1 WHERE name = ?; " << revision.page;
|
*db << "UPDATE page SET lastrevision=lastrevision+1 WHERE name = ?; " << revision.page;
|
||||||
*db << "release revisionsubmit;";
|
*db << "release revisionsubmit;";
|
||||||
}
|
}
|
||||||
@ -42,6 +43,8 @@ void RevisionDaoSqlite::save(const Revision &revision)
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Revision> RevisionDaoSqlite::getAllRevisions(QueryOption &options)
|
std::vector<Revision> RevisionDaoSqlite::getAllRevisions(QueryOption &options)
|
||||||
@ -50,18 +53,10 @@ std::vector<Revision> RevisionDaoSqlite::getAllRevisions(QueryOption &options)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SqliteQueryOption queryOption{options};
|
SqliteQueryOption queryOption { options };
|
||||||
std::string queryOptionSql = queryOption.setPrependWhere(true)
|
std::string queryOptionSql = queryOption.setPrependWhere(true).setVisibleColumnName("page.visible").setOrderByColumn("creationtime").build();
|
||||||
.setListedColumnName("page.listed")
|
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), page.name, revisionid FROM revision INNER JOIN page ON revision.page = page.id " + queryOptionSql;
|
||||||
.setOrderByColumn("creationtime")
|
query >> [&](std::string author, std::string comment, std::string content, time_t creationtime, std::string page, unsigned int revisionid)
|
||||||
.build();
|
|
||||||
auto query =
|
|
||||||
*db
|
|
||||||
<< "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), "
|
|
||||||
"page.name, revisionid FROM revision INNER JOIN page ON revision.page = page.id " +
|
|
||||||
queryOptionSql;
|
|
||||||
query >> [&](std::string author, std::string comment, std::string content, time_t creationtime,
|
|
||||||
std::string page, unsigned int revisionid)
|
|
||||||
{
|
{
|
||||||
Revision r;
|
Revision r;
|
||||||
r.author = author;
|
r.author = author;
|
||||||
@ -90,19 +85,11 @@ std::vector<Revision> RevisionDaoSqlite::getAllRevisionsForPage(std::string page
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SqliteQueryOption queryOption{option};
|
SqliteQueryOption queryOption { option };
|
||||||
std::string queryOptionSql = queryOption.setPrependWhere(false)
|
std::string queryOptionSql = queryOption.setPrependWhere(false).setVisibleColumnName("page.visible").setOrderByColumn("creationtime").build();
|
||||||
.setListedColumnName("page.listed")
|
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), page.name, revisionid FROM revision INNER JOIN page ON revision.page = page.id WHERE page.name = ? AND " + queryOptionSql << pagename;
|
||||||
.setOrderByColumn("creationtime")
|
|
||||||
.build();
|
|
||||||
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, "
|
|
||||||
"strftime('%s',creationtime), page.name, revisionid FROM revision INNER JOIN page ON "
|
|
||||||
"revision.page = page.id WHERE page.name = ? AND " +
|
|
||||||
queryOptionSql
|
|
||||||
<< pagename;
|
|
||||||
|
|
||||||
query >> [&](std::string author, std::string comment, std::string content, time_t creationtime,
|
query >> [&](std::string author, std::string comment, std::string content, time_t creationtime, std::string page, unsigned int revisionid)
|
||||||
std::string page, unsigned int revisionid)
|
|
||||||
{
|
{
|
||||||
Revision r;
|
Revision r;
|
||||||
r.author = author;
|
r.author = author;
|
||||||
@ -123,6 +110,7 @@ std::vector<Revision> RevisionDaoSqlite::getAllRevisionsForPage(std::string page
|
|||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Revision> RevisionDaoSqlite::getCurrentForPage(std::string pagename)
|
std::optional<Revision> RevisionDaoSqlite::getCurrentForPage(std::string pagename)
|
||||||
@ -130,16 +118,13 @@ std::optional<Revision> RevisionDaoSqlite::getCurrentForPage(std::string pagenam
|
|||||||
Revision result;
|
Revision result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, "
|
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), page, revisionid FROM revision WHERE page = (SELECT id FROM page WHERE name = ? ) AND revisionid = (SELECT lastrevision FROM page WHERE name = ?)";
|
||||||
"strftime('%s',creationtime), page.name, revisionid FROM revision INNER JOIN page ON "
|
query << pagename << pagename;
|
||||||
"revision.page = page.id WHERE page.name = ? AND page.lastrevision = revision.revisionid";
|
query >> std::tie(result.author, result.comment, result.content, result.timestamp, result.page, result.revision);
|
||||||
query << pagename;
|
|
||||||
query >>
|
|
||||||
std::tie(result.author, result.comment, result.content, result.timestamp, result.page, result.revision);
|
|
||||||
}
|
}
|
||||||
catch(const sqlite::errors::no_rows &e)
|
catch(const sqlite::errors::no_rows &e)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
@ -148,24 +133,21 @@ std::optional<Revision> RevisionDaoSqlite::getCurrentForPage(std::string pagenam
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::optional<Revision> RevisionDaoSqlite::getRevisionForPage(std::string pagename, unsigned int revision)
|
std::optional<Revision> RevisionDaoSqlite::getRevisionForPage(std::string pagename, unsigned int revision)
|
||||||
{
|
{
|
||||||
Revision result;
|
Revision result;
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto query =
|
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), page, revisionid FROM revision WHERE page = (SELECT id FROM page WHERE name = ? ) AND revisionid = ?";
|
||||||
*db
|
|
||||||
<< "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), "
|
|
||||||
"page.name, revisionid FROM revision INNER JOIN page ON revision.page = page.id WHERE page.name = ? AND "
|
|
||||||
"revisionid = ? ";
|
|
||||||
query << pagename << revision;
|
query << pagename << revision;
|
||||||
query >>
|
query >> std::tie(result.author, result.comment, result.content, result.timestamp, result.page, result.revision);
|
||||||
std::tie(result.author, result.comment, result.content, result.timestamp, result.page, result.revision);
|
|
||||||
}
|
}
|
||||||
catch(const sqlite::exceptions::no_rows &e)
|
catch(const sqlite::exceptions::no_rows &e)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -181,3 +163,4 @@ unsigned int RevisionDaoSqlite::countTotalRevisions(std::string page)
|
|||||||
auto query = *db << "SELECT COUNT(ROWID) FROM revision WHERE page = (SELECT id FROM page WHERE name = ?)" << page;
|
auto query = *db << "SELECT COUNT(ROWID) FROM revision WHERE page = (SELECT id FROM page WHERE name = ?)" << page;
|
||||||
return static_cast<unsigned int>(execInt(query));
|
return static_cast<unsigned int>(execInt(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,16 +6,18 @@
|
|||||||
|
|
||||||
class RevisionDaoSqlite : public RevisionDao, protected SqliteDao
|
class RevisionDaoSqlite : public RevisionDao, protected SqliteDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RevisionDaoSqlite();
|
RevisionDaoSqlite();
|
||||||
void save(const Revision &revision) override;
|
void save(const Revision &revision) override;
|
||||||
std::vector<Revision> getAllRevisions(QueryOption &options) override;
|
std::vector<Revision> getAllRevisions(QueryOption &options) override;
|
||||||
std::vector<Revision> getAllRevisionsForPage(std::string pagename, QueryOption &option) override;
|
std::vector<Revision> getAllRevisionsForPage(std::string pagename, QueryOption &option) override;
|
||||||
std::optional<Revision> getCurrentForPage(std::string pagename) override;
|
std::optional<Revision> getCurrentForPage(std::string pagename) override;
|
||||||
std::optional<Revision> getRevisionForPage(std::string pagnename, unsigned int revision) override;
|
std::optional<Revision> getRevisionForPage(std::string pagnename, unsigned int revision) override;
|
||||||
unsigned int countTotalRevisions() override;
|
unsigned int countTotalRevisions() override;
|
||||||
unsigned int countTotalRevisions(std::string pagename) override;
|
unsigned int countTotalRevisions(std::string pagename) override;
|
||||||
using SqliteDao::SqliteDao;
|
using SqliteDao::SqliteDao;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // REVISIONDAOSQLITE_H
|
#endif // REVISIONDAOSQLITE_H
|
||||||
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
SessionDao::SessionDao()
|
SessionDao::SessionDao()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,12 @@
|
|||||||
#include "../session.h"
|
#include "../session.h"
|
||||||
class SessionDao
|
class SessionDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SessionDao();
|
SessionDao();
|
||||||
virtual void save(const Session &session) = 0;
|
virtual void save(const Session &session) = 0;
|
||||||
virtual std::optional<Session> find(std::string token) = 0;
|
virtual std::optional<Session> find(std::string token) = 0;
|
||||||
virtual void deleteSession(std::string token) = 0;
|
virtual void deleteSession(std::string token) = 0;
|
||||||
virtual std::vector<Session> fetch() = 0;
|
virtual ~SessionDao() { }
|
||||||
virtual ~SessionDao()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SESSIONDAO_H
|
#endif // SESSIONDAO_H
|
||||||
|
72
database/sessiondaosqlite.cpp
Executable file → Normal file
72
database/sessiondaosqlite.cpp
Executable file → Normal file
@ -25,9 +25,8 @@ void SessionDaoSqlite::save(const Session &session)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// TODO: we do not store creationtime
|
//TODO: we do not store creationtime
|
||||||
auto q = *db << "INSERT OR REPLACE INTO session(id, token, csrf_token, creationtime, userid) VALUES((SELECT id "
|
auto q = *db << "INSERT OR REPLACE INTO session(id, token, csrf_token, creationtime, userid) VALUES((SELECT id FROM session WHERE token = ?), ?, ?, DATETIME(), (SELECT id FROM user WHERE username = ?))";
|
||||||
"FROM session WHERE token = ?), ?, ?, DATETIME(), (SELECT id FROM user WHERE username = ?))";
|
|
||||||
q << session.token << session.token << session.csrf_token << session.user.login;
|
q << session.token << session.token << session.csrf_token << session.user.login;
|
||||||
q.execute();
|
q.execute();
|
||||||
}
|
}
|
||||||
@ -48,29 +47,7 @@ void SessionDaoSqlite::deleteSession(std::string token)
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void SessionDaoSqlite::fillSession(int userid, Session &sess)
|
|
||||||
{
|
|
||||||
if(userid > -1)
|
|
||||||
{
|
|
||||||
UserDaoSqlite userDao{*this->db};
|
|
||||||
auto u = userDao.find(userid);
|
|
||||||
if(u)
|
|
||||||
{
|
|
||||||
sess.user = *u;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::error() << "Session for non existent user";
|
|
||||||
throw DatabaseQueryException("Session for non existent user");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sess.user = User::Anonymous();
|
|
||||||
}
|
|
||||||
sess.loggedIn = userid != -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Session> SessionDaoSqlite::find(std::string token)
|
std::optional<Session> SessionDaoSqlite::find(std::string token)
|
||||||
@ -80,40 +57,41 @@ std::optional<Session> SessionDaoSqlite::find(std::string token)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string username;
|
std::string username;
|
||||||
auto q = *db << "SELECT userid, token, csrf_token, strftime('%s', creationtime) FROM session WHERE token = ?"
|
auto q = *db << "SELECT userid, token, csrf_token, strftime('%s', creationtime) FROM session WHERE token = ?" << token;
|
||||||
<< token;
|
|
||||||
int userid;
|
int userid;
|
||||||
q >> std::tie(userid, result.token, result.csrf_token, result.creation_time);
|
q >> std::tie(userid, result.token, result.csrf_token, result.creation_time);
|
||||||
|
|
||||||
fillSession(userid, result);
|
if(userid > -1)
|
||||||
|
{
|
||||||
|
UserDaoSqlite userDao { this-> db };
|
||||||
|
auto u = userDao.find(userid);
|
||||||
|
if(u)
|
||||||
|
{
|
||||||
|
result.user = *u;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::error() << "Session for non existent user";
|
||||||
|
throw DatabaseQueryException("Session for non existent user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.user = User::Anonymous();
|
||||||
|
}
|
||||||
|
result.loggedIn = userid != -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(const sqlite::exceptions::no_rows &e)
|
catch(const sqlite::exceptions::no_rows &e)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Session> SessionDaoSqlite::fetch()
|
|
||||||
{
|
|
||||||
std::vector<Session> result;
|
|
||||||
|
|
||||||
*db << "SELECT userid, token, csrf_token, strftime('%s', creationtime) FROM session" >>
|
|
||||||
[this, &result](int userid, std::string token, std::string csrf_token, time_t creationtime)
|
|
||||||
{
|
|
||||||
Session tmp;
|
|
||||||
tmp.csrf_token = csrf_token;
|
|
||||||
tmp.token = token;
|
|
||||||
tmp.creation_time = creationtime;
|
|
||||||
|
|
||||||
fillSession(userid, tmp);
|
|
||||||
|
|
||||||
result.push_back(tmp);
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -4,18 +4,17 @@
|
|||||||
#include "../session.h"
|
#include "../session.h"
|
||||||
#include "sqlitedao.h"
|
#include "sqlitedao.h"
|
||||||
|
|
||||||
|
|
||||||
class SessionDaoSqlite : public SessionDao, protected SqliteDao
|
class SessionDaoSqlite : public SessionDao, protected SqliteDao
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
void fillSession(int userid, Session &sess);
|
|
||||||
|
|
||||||
public:
|
|
||||||
SessionDaoSqlite();
|
SessionDaoSqlite();
|
||||||
void save(const Session &session) override;
|
void save(const Session &session) override;
|
||||||
std::optional<Session> find(std::string token) override;
|
std::optional<Session> find(std::string token) override;
|
||||||
void deleteSession(std::string token) override;
|
void deleteSession(std::string token) override;
|
||||||
std::vector<Session> fetch() override;
|
|
||||||
using SqliteDao::SqliteDao;
|
using SqliteDao::SqliteDao;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SESSIONDAOSQLITE_H
|
#endif // SESSIONDAOSQLITE_H
|
||||||
|
@ -18,43 +18,26 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
#include <atomic>
|
|
||||||
#include "sqlite.h"
|
#include "sqlite.h"
|
||||||
|
#include "../logger.h"
|
||||||
#include "pagedaosqlite.h"
|
#include "pagedaosqlite.h"
|
||||||
#include "revisiondaosqlite.h"
|
#include "revisiondaosqlite.h"
|
||||||
#include "sessiondaosqlite.h"
|
#include "sessiondaosqlite.h"
|
||||||
#include "sqlite_modern_cpp.h"
|
|
||||||
#include "userdaosqlite.h"
|
#include "userdaosqlite.h"
|
||||||
#include "categorydaosqlite.h"
|
#include "categorydaosqlite.h"
|
||||||
|
#include "exceptions.h"
|
||||||
#include "permissionsdaosqlite.h"
|
#include "permissionsdaosqlite.h"
|
||||||
|
|
||||||
thread_local sqlite::database *Sqlite::db = nullptr;
|
|
||||||
|
|
||||||
std::atomic<int> instances = 0;
|
|
||||||
Sqlite::Sqlite(std::string path) : Database(path)
|
Sqlite::Sqlite(std::string path) : Database(path)
|
||||||
{
|
{
|
||||||
instances++;
|
this->db = std::make_shared<sqlite::database>(path);
|
||||||
if(instances.load() > 1)
|
|
||||||
{
|
*db << "PRAGMA journal_mode=WAL;";
|
||||||
std::cerr << "temporal (yeah, right) HACK... only one instance allowed" << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mutex dbmutex;
|
|
||||||
sqlite::database &Sqlite::database() const
|
|
||||||
{
|
|
||||||
if(Sqlite::db == nullptr)
|
|
||||||
{
|
|
||||||
sqlite::sqlite_config config;
|
|
||||||
config.flags = config.flags | sqlite::OpenFlags::FULLMUTEX;
|
|
||||||
std::lock_guard<std::mutex> dbguard(dbmutex);
|
|
||||||
Sqlite::db = new sqlite::database(this->connnectionstring, config);
|
|
||||||
*Sqlite::db << "PRAGMA journal_mode=WAL;";
|
|
||||||
*Sqlite::db << "PRAGMA busy_timeout=10000;";
|
|
||||||
}
|
|
||||||
return *Sqlite::db;
|
|
||||||
}
|
|
||||||
std::unique_ptr<RevisionDao> Sqlite::createRevisionDao() const
|
std::unique_ptr<RevisionDao> Sqlite::createRevisionDao() const
|
||||||
{
|
{
|
||||||
return create<RevisionDaoSqlite>();
|
return create<RevisionDaoSqlite>();
|
||||||
@ -70,6 +53,7 @@ std::unique_ptr<UserDao> Sqlite::createUserDao() const
|
|||||||
return create<UserDaoSqlite>();
|
return create<UserDaoSqlite>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<SessionDao> Sqlite::createSessionDao() const
|
std::unique_ptr<SessionDao> Sqlite::createSessionDao() const
|
||||||
{
|
{
|
||||||
return create<SessionDaoSqlite>();
|
return create<SessionDaoSqlite>();
|
||||||
@ -80,6 +64,7 @@ std::unique_ptr<CategoryDao> Sqlite::createCategoryDao() const
|
|||||||
return create<CategoryDaoSqlite>();
|
return create<CategoryDaoSqlite>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<PermissionsDao> Sqlite::createPermissionsDao() const
|
std::unique_ptr<PermissionsDao> Sqlite::createPermissionsDao() const
|
||||||
{
|
{
|
||||||
return create<PermissionsDaoSqlite>();
|
return create<PermissionsDaoSqlite>();
|
||||||
@ -87,20 +72,28 @@ std::unique_ptr<PermissionsDao> Sqlite::createPermissionsDao() const
|
|||||||
|
|
||||||
void Sqlite::beginTransaction()
|
void Sqlite::beginTransaction()
|
||||||
{
|
{
|
||||||
|
if(!inTransaction)
|
||||||
|
{
|
||||||
*db << "begin;";
|
*db << "begin;";
|
||||||
|
inTransaction = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sqlite::rollbackTransaction()
|
void Sqlite::rollbackTransaction()
|
||||||
{
|
{
|
||||||
*db << "rollback;";
|
if(inTransaction)
|
||||||
|
{
|
||||||
|
*db << "rollback;";
|
||||||
|
inTransaction = false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sqlite::commitTransaction()
|
void Sqlite::commitTransaction()
|
||||||
{
|
{
|
||||||
*db << "commit;";
|
if(inTransaction)
|
||||||
}
|
{
|
||||||
|
*db << "commit;";
|
||||||
Sqlite::~Sqlite()
|
inTransaction = false;
|
||||||
{
|
}
|
||||||
delete this->db;
|
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,15 @@
|
|||||||
|
|
||||||
class Sqlite : public Database
|
class Sqlite : public Database
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static thread_local sqlite::database *db;
|
bool inTransaction = false;
|
||||||
|
std::shared_ptr<sqlite::database> db;
|
||||||
|
|
||||||
template <class T> std::unique_ptr<T> create() const
|
template<class T> std::unique_ptr<T> create() const
|
||||||
{
|
{
|
||||||
return std::make_unique<T>(database());
|
return std::make_unique<T>(db);
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
sqlite::database &database() const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Sqlite(std::string path);
|
Sqlite(std::string path);
|
||||||
std::unique_ptr<PageDao> createPageDao() const;
|
std::unique_ptr<PageDao> createPageDao() const;
|
||||||
std::unique_ptr<RevisionDao> createRevisionDao() const;
|
std::unique_ptr<RevisionDao> createRevisionDao() const;
|
||||||
@ -28,7 +26,7 @@ class Sqlite : public Database
|
|||||||
void beginTransaction();
|
void beginTransaction();
|
||||||
void commitTransaction();
|
void commitTransaction();
|
||||||
void rollbackTransaction();
|
void rollbackTransaction();
|
||||||
virtual ~Sqlite();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SQLITE_H
|
#endif // SQLITE_H
|
||||||
|
@ -20,19 +20,21 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include "sqlitedao.h"
|
#include "sqlitedao.h"
|
||||||
|
|
||||||
|
|
||||||
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
|
{
|
||||||
result = false;
|
//TODO: well, we may want to check whether rows have found or not and thus log this here
|
||||||
}
|
return false;
|
||||||
return result;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SqliteDao::execInt(sqlite::database_binder &binder) const
|
int SqliteDao::execInt(sqlite::database_binder &binder) const
|
||||||
@ -51,5 +53,4 @@ int SqliteDao::execInt(sqlite::database_binder &binder) const
|
|||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -11,22 +11,13 @@
|
|||||||
|
|
||||||
class SqliteDao
|
class SqliteDao
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
sqlite::database *db = nullptr;
|
std::shared_ptr<sqlite::database> db;
|
||||||
|
public:
|
||||||
|
SqliteDao() { }
|
||||||
|
|
||||||
public:
|
SqliteDao(std::shared_ptr<sqlite::database> db) { this->db = db; }
|
||||||
SqliteDao()
|
void setDb(std::shared_ptr<sqlite::database> db) { this->db = db; }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SqliteDao(sqlite::database &db)
|
|
||||||
{
|
|
||||||
this->db = &db;
|
|
||||||
}
|
|
||||||
void setDb(sqlite::database &db)
|
|
||||||
{
|
|
||||||
this->db = &db;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void throwFrom(const sqlite::sqlite_exception &e) const
|
inline void throwFrom(const sqlite::sqlite_exception &e) const
|
||||||
{
|
{
|
||||||
@ -38,7 +29,8 @@ class SqliteDao
|
|||||||
bool execBool(sqlite::database_binder &binder) const;
|
bool execBool(sqlite::database_binder &binder) const;
|
||||||
int execInt(sqlite::database_binder &binder) const;
|
int execInt(sqlite::database_binder &binder) const;
|
||||||
|
|
||||||
virtual ~SqliteDao() = default;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SQLITEDAO_H
|
#endif // SQLITEDAO_H
|
||||||
|
@ -20,10 +20,8 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include "sqlitequeryoption.h"
|
#include "sqlitequeryoption.h"
|
||||||
|
|
||||||
SqliteQueryOption::SqliteQueryOption(const QueryOption &o)
|
|
||||||
{
|
SqliteQueryOption::SqliteQueryOption(const QueryOption &o) { this->o = o; }
|
||||||
this->o = o;
|
|
||||||
}
|
|
||||||
|
|
||||||
SqliteQueryOption &SqliteQueryOption::setOrderByColumn(std::string name)
|
SqliteQueryOption &SqliteQueryOption::setOrderByColumn(std::string name)
|
||||||
{
|
{
|
||||||
@ -31,33 +29,26 @@ SqliteQueryOption &SqliteQueryOption::setOrderByColumn(std::string name)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
SqliteQueryOption &SqliteQueryOption::setListedColumnName(std::string name)
|
SqliteQueryOption &SqliteQueryOption::setVisibleColumnName(std::string name)
|
||||||
{
|
{
|
||||||
this->listedColumnName = name;
|
this->visibleColumnName = name;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
SqliteQueryOption &SqliteQueryOption::setPrependWhere(bool b)
|
SqliteQueryOption &SqliteQueryOption::setPrependWhere(bool b) { this->prependWhere = b; return *this; }
|
||||||
{
|
|
||||||
this->prependWhere = b;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SqliteQueryOption::build()
|
std::string SqliteQueryOption::build()
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
if(this->prependWhere)
|
if(!o.includeInvisible && ! this->visibleColumnName.empty())
|
||||||
{
|
{
|
||||||
result += "WHERE ";
|
if(this->prependWhere)
|
||||||
}
|
{
|
||||||
if(!o.includeUnlisted && !this->listedColumnName.empty())
|
result += "WHERE ";
|
||||||
{
|
}
|
||||||
result += this->listedColumnName + " = 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)
|
||||||
{
|
{
|
||||||
@ -67,10 +58,9 @@ std::string SqliteQueryOption::build()
|
|||||||
{
|
{
|
||||||
result += " DESC";
|
result += " DESC";
|
||||||
}
|
}
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
#ifndef SQLITEQUERYOPTION_H
|
#ifndef SQLITEQUERYOPTION_H
|
||||||
#define SQLITEQUERYOPTION_H
|
#define SQLITEQUERYOPTION_H
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "queryoption.h"
|
#include "queryoption.h"
|
||||||
|
|
||||||
class SqliteQueryOption
|
class SqliteQueryOption
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
QueryOption o;
|
QueryOption o;
|
||||||
std::string listedColumnName;
|
std::string visibleColumnName;
|
||||||
std::string orderByColumnName;
|
std::string orderByColumnName;
|
||||||
|
|
||||||
bool prependWhere;
|
bool prependWhere;
|
||||||
|
public:
|
||||||
public:
|
|
||||||
SqliteQueryOption(const QueryOption &o);
|
SqliteQueryOption(const QueryOption &o);
|
||||||
|
|
||||||
SqliteQueryOption &setOrderByColumn(std::string name);
|
SqliteQueryOption &setOrderByColumn(std::string name);
|
||||||
|
|
||||||
SqliteQueryOption &setListedColumnName(std::string name);
|
SqliteQueryOption &setVisibleColumnName(std::string name);
|
||||||
|
|
||||||
SqliteQueryOption &setPrependWhere(bool b);
|
SqliteQueryOption &setPrependWhere(bool b);
|
||||||
|
|
||||||
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
UserDao::UserDao()
|
UserDao::UserDao()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "../user.h"
|
#include "../user.h"
|
||||||
#include "queryoption.h"
|
|
||||||
class UserDao
|
class UserDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UserDao();
|
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() { };
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // USERDAO_H
|
#endif // USERDAO_H
|
||||||
|
@ -23,10 +23,10 @@ SOFTWARE.
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "userdaosqlite.h"
|
#include "userdaosqlite.h"
|
||||||
#include "sqlitequeryoption.h"
|
|
||||||
|
|
||||||
UserDaoSqlite::UserDaoSqlite()
|
UserDaoSqlite::UserDaoSqlite()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UserDaoSqlite::exists(std::string username)
|
bool UserDaoSqlite::exists(std::string username)
|
||||||
@ -37,27 +37,26 @@ 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;
|
||||||
auto stmt = *db << "SELECT username, password, salt, permissions, enabled FROM user WHERE username = ?"
|
auto stmt = *db << "SELECT username, password, salt, permissions, enabled FROM user WHERE username = ?" << username;
|
||||||
<< username;
|
|
||||||
|
|
||||||
int perms = 0;
|
int perms = 0;
|
||||||
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)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<User> UserDaoSqlite::find(int id)
|
std::optional<User> UserDaoSqlite::find(int id)
|
||||||
@ -69,67 +68,33 @@ std::optional<User> UserDaoSqlite::find(int id)
|
|||||||
|
|
||||||
int perms = 0;
|
int perms = 0;
|
||||||
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)
|
||||||
{
|
{
|
||||||
return {};
|
return { };
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
throwFrom(e);
|
throwFrom(e);
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<User> UserDaoSqlite::list(QueryOption o)
|
void UserDaoSqlite::deleteUser(std::string username)
|
||||||
{
|
{
|
||||||
std::vector<User> result;
|
//What to do with the contributions of the user?
|
||||||
|
|
||||||
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?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserDaoSqlite::save(const User &u)
|
void UserDaoSqlite::save(const User &u)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto q = *db << "INSERT OR REPLACE INTO user(id, username, password, salt, permissions, enabled) "
|
auto q = *db << "INSERT OR REPLACE INTO user(id, username, password, salt, permissions, enabled) VALUES((SELECT id FROM user WHERE username = ?), ?,?,?,?,?)";
|
||||||
"VALUES((SELECT id FROM user WHERE username = ?), ?,?,?,?,?)";
|
|
||||||
q << u.login << u.login << u.password << u.salt << u.permissions.getPermissions() << u.enabled;
|
q << u.login << u.login << u.password << u.salt << u.permissions.getPermissions() << u.enabled;
|
||||||
q.execute();
|
q.execute();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(sqlite::sqlite_exception &e)
|
catch(sqlite::sqlite_exception &e)
|
||||||
{
|
{
|
||||||
|
@ -8,12 +8,11 @@
|
|||||||
|
|
||||||
class UserDaoSqlite : public UserDao, protected SqliteDao
|
class UserDaoSqlite : public UserDao, protected SqliteDao
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool exists(std::string username);
|
bool exists(std::string username);
|
||||||
std::optional<User> find(std::string username);
|
std::optional<User> find(std::string username);
|
||||||
std::optional<User> find(int id);
|
std::optional<User> find(int id);
|
||||||
|
|
||||||
std::vector<User> list(QueryOption o);
|
|
||||||
void deleteUser(std::string username);
|
void deleteUser(std::string username);
|
||||||
void save(const User &u);
|
void save(const User &u);
|
||||||
using SqliteDao::SqliteDao;
|
using SqliteDao::SqliteDao;
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#include "dynamiccontent.h"
|
|
||||||
|
|
||||||
DynamicContent::DynamicContent(Template &templ, Database &database, UrlProvider &provider, Session &session)
|
|
||||||
{
|
|
||||||
this->templ = &templ;
|
|
||||||
this->database = &database;
|
|
||||||
this->urlProvider = &provider;
|
|
||||||
this->userSession = &session;
|
|
||||||
}
|
|
@ -1,29 +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;
|
|
||||||
Session *userSession;
|
|
||||||
|
|
||||||
std::string argument;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DynamicContent(Template &templ, Database &database, UrlProvider &urlProvider, Session &session);
|
|
||||||
virtual std::string render() = 0;
|
|
||||||
virtual void setArgument(std::string argument)
|
|
||||||
{
|
|
||||||
this->argument = argument;
|
|
||||||
}
|
|
||||||
virtual ~DynamicContent()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DYNAMICCONTENT_H
|
|
@ -1,30 +0,0 @@
|
|||||||
#ifndef DYNAMICCONTENTFACTORY_H
|
|
||||||
#define DYNAMICCONTENTFACTORY_H
|
|
||||||
|
|
||||||
#include "dynamiccontent.h"
|
|
||||||
|
|
||||||
class DynamicContentFactory
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Template *templ;
|
|
||||||
Database *db;
|
|
||||||
UrlProvider *urlProvider;
|
|
||||||
Session *session;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DynamicContentFactory(Template &templ, Database &db, UrlProvider &urlProvider, Session &session)
|
|
||||||
{
|
|
||||||
this->templ = &templ;
|
|
||||||
this->db = &db;
|
|
||||||
this->urlProvider = &urlProvider;
|
|
||||||
this->session = &session;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T> inline std::shared_ptr<T> createDynamicContent()
|
|
||||||
{
|
|
||||||
return std::make_shared<T>(*this->templ, *this->db, *this->urlProvider, *this->session);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
#endif // DYNAMICCONTENTFACTORY_H_INCLUDED
|
|
@ -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 = ↦
|
|
||||||
}
|
|
@ -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
|
|
@ -1,16 +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)
|
|
||||||
{
|
|
||||||
/* Quite dirty */
|
|
||||||
if(rev->content.find("[cmd:allowinclude]1[") != std::string::npos)
|
|
||||||
{
|
|
||||||
return rev->content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
@ -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
|
|
@ -1,46 +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();
|
|
||||||
auto permissionDao = this->database->createPermissionsDao();
|
|
||||||
|
|
||||||
QueryOption option;
|
|
||||||
option.includeUnlisted = false;
|
|
||||||
auto members = categoryDao->fetchMembers(this->argument, option);
|
|
||||||
std::vector<std::pair<std::string, time_t>> pageList;
|
|
||||||
for(const Page &member : members)
|
|
||||||
{
|
|
||||||
Permissions perms = permissionDao->find(member.name, this->userSession->user.login)
|
|
||||||
.value_or(this->userSession->user.permissions);
|
|
||||||
if(perms.canRead()) /* TODO: Maybe add canList() */
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
@ -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
|
|
@ -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 = ↦
|
|
||||||
}
|
|
@ -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
|
|
@ -1,73 +0,0 @@
|
|||||||
#include <chrono>
|
|
||||||
#include "dynamicpostrenderer.h"
|
|
||||||
#include "../parser.h"
|
|
||||||
#include "../utils.h"
|
|
||||||
|
|
||||||
void DynamicPostRenderer::setArgument(std::string argument)
|
|
||||||
{
|
|
||||||
auto splitted = utils::split(argument, '|');
|
|
||||||
this->category = splitted[0];
|
|
||||||
if(splitted.size() >= 2)
|
|
||||||
{
|
|
||||||
this->templatepartname = splitted[1];
|
|
||||||
}
|
|
||||||
if(splitted.size() >= 3)
|
|
||||||
{
|
|
||||||
this->customlinkurl = splitted[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DynamicPostRenderer::linkToPage(std::string page)
|
|
||||||
{
|
|
||||||
if(this->customlinkurl.empty())
|
|
||||||
{
|
|
||||||
return this->urlProvider->page(page);
|
|
||||||
}
|
|
||||||
return utils::strreplace(this->customlinkurl, "{page}", page);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DynamicPostRenderer::render()
|
|
||||||
{
|
|
||||||
auto categoryDao = this->database->createCategoryDao();
|
|
||||||
auto pageDao = this->database->createPageDao();
|
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
|
||||||
auto permissionDao = this->database->createPermissionsDao();
|
|
||||||
|
|
||||||
QueryOption option;
|
|
||||||
option.includeUnlisted = true;
|
|
||||||
auto members = categoryDao->fetchMembers(this->category, option);
|
|
||||||
std::vector<std::pair<std::string, time_t>> pageList;
|
|
||||||
for(const Page &member : members)
|
|
||||||
{
|
|
||||||
Permissions perms = permissionDao->find(member.name, this->userSession->user.login)
|
|
||||||
.value_or(this->userSession->user.permissions);
|
|
||||||
if(perms.canRead())
|
|
||||||
{
|
|
||||||
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 entry = this->templ->loadResolvedPart(this->templatepartname);
|
|
||||||
std::stringstream stream;
|
|
||||||
for(auto &pair : pageList)
|
|
||||||
{
|
|
||||||
std::optional<Revision> revision = revisionDao->getCurrentForPage(pair.first);
|
|
||||||
if(revision)
|
|
||||||
{
|
|
||||||
std::string link = linkToPage(pair.first);
|
|
||||||
Parser parser;
|
|
||||||
|
|
||||||
std::string date = utils::toISODateTime(revision->timestamp);
|
|
||||||
Varreplacer replacer{"{"};
|
|
||||||
replacer.addKeyValue("url", link);
|
|
||||||
replacer.addKeyValue("date", date);
|
|
||||||
replacer.addKeyValue("content", parser.parse(*pageDao, *this->urlProvider,
|
|
||||||
parser.extractFirstTag("content", revision->content)));
|
|
||||||
stream << replacer.parse(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stream.str();
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#ifndef DYNAMICPOSTRENDERER_H
|
|
||||||
#define DYNAMICPOSTRENDERER_H
|
|
||||||
#include "dynamiccontent.h"
|
|
||||||
class DynamicPostRenderer : public DynamicContent
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string category;
|
|
||||||
std::string customlinkurl;
|
|
||||||
std::string templatepartname = "dynamic/categoryrendererentry";
|
|
||||||
|
|
||||||
public:
|
|
||||||
using DynamicContent::DynamicContent;
|
|
||||||
std::string render() override;
|
|
||||||
void setArgument(std::string argument) override;
|
|
||||||
std::string linkToPage(std::string page);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DYNAMICPOSTRENDERER_H
|
|
@ -43,7 +43,7 @@ Request Cgi::readRequest()
|
|||||||
throw std::runtime_error("REQUEST_URI is empty");
|
throw std::runtime_error("REQUEST_URI is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
Request result{request_uri};
|
Request result { request_uri };
|
||||||
|
|
||||||
std::string method = utils::getenv("REQUEST_METHOD");
|
std::string method = utils::getenv("REQUEST_METHOD");
|
||||||
if(method == "POST")
|
if(method == "POST")
|
||||||
@ -55,10 +55,14 @@ Request Cgi::readRequest()
|
|||||||
}
|
}
|
||||||
std::string content_length = utils::getenv("CONTENT_LENGTH");
|
std::string content_length = utils::getenv("CONTENT_LENGTH");
|
||||||
int cl = std::stoi(content_length);
|
int cl = std::stoi(content_length);
|
||||||
std::unique_ptr<char[]> ptr(new char[cl + 1]);
|
std::unique_ptr<char[]> ptr(new char[cl+1]);
|
||||||
std::cin.get(ptr.get(), cl + 1);
|
std::cin.get(ptr.get(), cl+1);
|
||||||
|
|
||||||
|
std::string post_data { ptr.get() };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string post_data{ptr.get()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.initCookies(utils::getenv("HTTP_COOKIE"));
|
result.initCookies(utils::getenv("HTTP_COOKIE"));
|
||||||
@ -66,6 +70,7 @@ Request Cgi::readRequest()
|
|||||||
result.setUseragent(utils::getenv("HTTP_USER_AGENT"));
|
result.setUseragent(utils::getenv("HTTP_USER_AGENT"));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cgi::work(RequestWorker &worker)
|
void Cgi::work(RequestWorker &worker)
|
||||||
@ -80,7 +85,7 @@ void Cgi::work(RequestWorker &worker)
|
|||||||
void Cgi::sendResponse(const Response &r)
|
void Cgi::sendResponse(const Response &r)
|
||||||
{
|
{
|
||||||
std::cout << "Status: " << r.getStatus() << "\r\n";
|
std::cout << "Status: " << r.getStatus() << "\r\n";
|
||||||
std::cout << "Content-Type: " << r.getContentType() << "\r\n";
|
std::cout << "Content-Type: " << r.getContentType() <<"\r\n";
|
||||||
for(auto header : r.getResponseHeaders())
|
for(auto header : r.getResponseHeaders())
|
||||||
{
|
{
|
||||||
std::string key = header.first;
|
std::string key = header.first;
|
||||||
@ -106,4 +111,5 @@ void Cgi::sendResponse(const Response &r)
|
|||||||
|
|
||||||
Cgi::~Cgi()
|
Cgi::~Cgi()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,12 @@
|
|||||||
#include "../requestworker.h"
|
#include "../requestworker.h"
|
||||||
class Cgi : public GatewayInterface
|
class Cgi : public GatewayInterface
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bool responseSent = false;
|
bool responseSent = false;
|
||||||
const Config *config;
|
const Config *config;
|
||||||
Request readRequest();
|
Request readRequest();
|
||||||
void sendResponse(const Response &r);
|
void sendResponse(const Response &r);
|
||||||
|
public:
|
||||||
public:
|
|
||||||
Cgi(const Config &c);
|
Cgi(const Config &c);
|
||||||
bool keepReading() override;
|
bool keepReading() override;
|
||||||
void work(RequestWorker &worker) override;
|
void work(RequestWorker &worker) override;
|
||||||
|
@ -35,5 +35,7 @@ std::unique_ptr<GatewayInterface> createGateway(const Config &c)
|
|||||||
throw new std::runtime_error("No http.listenport in config file");
|
throw new std::runtime_error("No http.listenport in config file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return std::make_unique<HttpGateway>(listenaddr, std::stoi(listenport), c.max_payload_length);
|
return std::make_unique<HttpGateway>(listenaddr, std::stoi(listenport), c.max_payload_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,4 +22,5 @@ SOFTWARE.
|
|||||||
|
|
||||||
GatewayInterface::GatewayInterface()
|
GatewayInterface::GatewayInterface()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,11 @@
|
|||||||
#include "../requestworker.h"
|
#include "../requestworker.h"
|
||||||
class GatewayInterface
|
class GatewayInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GatewayInterface();
|
GatewayInterface();
|
||||||
virtual bool keepReading() = 0;
|
virtual bool keepReading() = 0;
|
||||||
virtual void work(RequestWorker &worker) = 0;
|
virtual void work(RequestWorker &worker) = 0;
|
||||||
virtual ~GatewayInterface()
|
virtual ~GatewayInterface() { }
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GATEWAYINTERFACE_H
|
#endif // GATEWAYINTERFACE_H
|
||||||
|
@ -38,10 +38,10 @@ Request HttpGateway::convertRequest(httplib::Request request)
|
|||||||
result.setRequestMethod(request.method);
|
result.setRequestMethod(request.method);
|
||||||
result.setUrl(request.target);
|
result.setUrl(request.target);
|
||||||
|
|
||||||
// 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")
|
||||||
{
|
{
|
||||||
@ -69,7 +69,7 @@ httplib::Response HttpGateway::convertResponse(Response response)
|
|||||||
result.status = response.getStatus();
|
result.status = response.getStatus();
|
||||||
for(auto &header : response.getResponseHeaders())
|
for(auto &header : response.getResponseHeaders())
|
||||||
{
|
{
|
||||||
result.set_header(header.first.c_str(), header.second.c_str());
|
result.set_header(header.first.c_str(), header.second.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const Cookie &cookie : response.getCookies())
|
for(const Cookie &cookie : response.getCookies())
|
||||||
@ -79,12 +79,12 @@ httplib::Response HttpGateway::convertResponse(Response response)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void HttpGateway::work(RequestWorker &worker)
|
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);
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#ifndef HTTPGATEWAY_H
|
#ifndef HTTPGATEWAY_H
|
||||||
#define HTTPGATEWAY_H
|
#define HTTPGATEWAY_H
|
||||||
|
|
||||||
#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 65536
|
|
||||||
|
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
#include "gatewayinterface.h"
|
#include "gatewayinterface.h"
|
||||||
#include "../requestworker.h"
|
#include "../requestworker.h"
|
||||||
@ -11,17 +8,16 @@
|
|||||||
#include "../utils.h"
|
#include "../utils.h"
|
||||||
class HttpGateway : public GatewayInterface
|
class HttpGateway : public GatewayInterface
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Response convertResponse(httplib::Response response);
|
Response convertResponse(httplib::Response response);
|
||||||
httplib::Response convertResponse(Response response);
|
httplib::Response convertResponse(Response response);
|
||||||
Request convertRequest(httplib::Request request);
|
Request convertRequest(httplib::Request request);
|
||||||
// void worker(const httplib::Request& req, httplib::Response& res);
|
// void worker(const httplib::Request& req, httplib::Response& res);
|
||||||
|
|
||||||
std::string listenaddr;
|
std::string listenaddr;
|
||||||
int listenport;
|
int listenport;
|
||||||
uint64_t maxPayloadLength;
|
uint64_t maxPayloadLength;
|
||||||
|
public:
|
||||||
public:
|
|
||||||
HttpGateway(std::string listenaddr, int port, uint64_t maxPayloadLength);
|
HttpGateway(std::string listenaddr, int port, uint64_t maxPayloadLength);
|
||||||
bool keepReading() override;
|
bool keepReading() override;
|
||||||
void work(RequestWorker &worker) override;
|
void work(RequestWorker &worker) override;
|
||||||
|
26
grouper.h
26
grouper.h
@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
@ -20,6 +20,7 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include "handler.h"
|
#include "handler.h"
|
||||||
|
|
||||||
|
|
||||||
void Handler::setGeneralVars(TemplatePage &page)
|
void Handler::setGeneralVars(TemplatePage &page)
|
||||||
{
|
{
|
||||||
if(userSession->loggedIn)
|
if(userSession->loggedIn)
|
||||||
@ -28,19 +29,19 @@ void Handler::setGeneralVars(TemplatePage &page)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
page.setVar("loginstatus", "not logged in");
|
page.setVar("loginstatus", "not logged in");
|
||||||
}
|
}
|
||||||
page.setVar("csrf_token", utils::toString(this->userSession->csrf_token));
|
page.setVar("csrf_token", utils::toString(this->userSession->csrf_token));
|
||||||
}
|
}
|
||||||
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);
|
||||||
// TODO: log?
|
//TODO: log?
|
||||||
setGeneralVars(error);
|
setGeneralVars(error);
|
||||||
return {status, error.render()};
|
return { status, error.render()};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Handler::createPageTitle(std::string title)
|
std::string Handler::createPageTitle(std::string title)
|
||||||
@ -50,13 +51,14 @@ std::string Handler::createPageTitle(std::string title)
|
|||||||
return replacer.parse(this->handlersConfig->page_title_template);
|
return replacer.parse(this->handlersConfig->page_title_template);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueryOption Handler::queryOption(const Request &r, SORT_ORDER defaultSort) const
|
QueryOption Handler::queryOption(const Request &r) const
|
||||||
{
|
{
|
||||||
QueryOption result;
|
QueryOption result;
|
||||||
result.includeUnlisted = false;
|
result.includeInvisible = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result.limit = utils::toUInt(r.get("limit"));
|
result.limit = utils::toUInt(r.get("limit"));
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
@ -65,23 +67,20 @@ QueryOption Handler::queryOption(const Request &r, SORT_ORDER defaultSort) const
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
result.offset = utils::toUInt(r.get("offset"));
|
result.offset = utils::toUInt(r.get("offset"));
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
result.offset = 0;
|
result.offset = 0;
|
||||||
}
|
}
|
||||||
std::string order = r.get("sort");
|
std::string order = r.get("sort");
|
||||||
result.order = defaultSort;
|
if(order == "0")
|
||||||
if(order != "")
|
|
||||||
{
|
{
|
||||||
if(order == "1")
|
result.order = ASCENDING;
|
||||||
{
|
}
|
||||||
result.order = DESCENDING;
|
else
|
||||||
}
|
{
|
||||||
else
|
result.order = DESCENDING;
|
||||||
{
|
|
||||||
result.order = ASCENDING;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -98,10 +97,6 @@ Response Handler::handle(const Request &r)
|
|||||||
|
|
||||||
Permissions Handler::effectivePermissions(std::string page)
|
Permissions Handler::effectivePermissions(std::string page)
|
||||||
{
|
{
|
||||||
Permissions &userPerms = this->userSession->user.permissions;
|
return this->database->createPermissionsDao()->find(page, this->userSession->user.login).value_or(this->userSession->user.permissions);
|
||||||
if(userPerms.isAdmin())
|
|
||||||
{
|
|
||||||
return userPerms;
|
|
||||||
}
|
|
||||||
return this->database->createPermissionsDao()->find(page, this->userSession->user.login).value_or(userPerms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,11 +9,9 @@
|
|||||||
#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"
|
|
||||||
|
|
||||||
class Handler
|
class Handler
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
ICache *cache;
|
ICache *cache;
|
||||||
Template *templ;
|
Template *templ;
|
||||||
Database *database;
|
Database *database;
|
||||||
@ -22,13 +19,11 @@ class Handler
|
|||||||
UrlProvider *urlProvider;
|
UrlProvider *urlProvider;
|
||||||
HandlerConfig *handlersConfig;
|
HandlerConfig *handlersConfig;
|
||||||
|
|
||||||
// TODO: may not to find a better place for this method
|
//TODO: may not to find a better place for this method
|
||||||
Permissions effectivePermissions(std::string page);
|
Permissions effectivePermissions(std::string page);
|
||||||
QueryOption queryOption(const Request &r, SORT_ORDER defaultSort = ASCENDING) const;
|
QueryOption queryOption(const Request &r) const;
|
||||||
|
public:
|
||||||
public:
|
Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider, ICache &cache)
|
||||||
Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider,
|
|
||||||
ICache &cache)
|
|
||||||
{
|
{
|
||||||
this->handlersConfig = &handlersConfig;
|
this->handlersConfig = &handlersConfig;
|
||||||
this->templ = &templ;
|
this->templ = &templ;
|
||||||
@ -39,29 +34,19 @@ class Handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
virtual std::string accessErrorMessage()
|
virtual std::string accessErrorMessage() {
|
||||||
{
|
|
||||||
return "No permission for this action";
|
return "No permission for this action";
|
||||||
}
|
}
|
||||||
void setGeneralVars(TemplatePage &page);
|
void setGeneralVars(TemplatePage &page);
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
@ -28,13 +28,12 @@ Response HandlerAllCategories::handleRequest(const Request &r)
|
|||||||
auto resultList = categoryDao->fetchList(qo);
|
auto resultList = categoryDao->fetchList(qo);
|
||||||
if(resultList.size() == 0)
|
if(resultList.size() == 0)
|
||||||
{
|
{
|
||||||
return errorResponse(
|
return errorResponse("No categories", "This wiki does not have any categories defined yet or your query options did not yield any results");
|
||||||
"No categories",
|
|
||||||
"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) {
|
||||||
this->templ->renderSearch(resultList, [&](std::string str) { return this->urlProvider->category(str); });
|
return this->urlProvider->category(str);
|
||||||
|
});
|
||||||
searchPage.setVar("categorylist", body);
|
searchPage.setVar("categorylist", body);
|
||||||
searchPage.setVar("title", createPageTitle("All categories"));
|
searchPage.setVar("title", createPageTitle("All categories"));
|
||||||
setGeneralVars(searchPage);
|
setGeneralVars(searchPage);
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#ifndef HANDLERALLCATEGORIES_H
|
#ifndef HANDLERALLCATEGORIES_H
|
||||||
#define HANDLERALLCATEGORIES_H
|
#define HANDLERALLCATEGORIES_H
|
||||||
|
|
||||||
|
|
||||||
#include "handler.h"
|
#include "handler.h"
|
||||||
|
|
||||||
class HandlerAllCategories : public Handler
|
class HandlerAllCategories : public Handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HandlerAllCategories();
|
HandlerAllCategories();
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
@ -52,6 +46,7 @@ Response HandlerAllPages::handleRequest(const Request &r)
|
|||||||
Logger::error() << "Error during allpages Handler" << e.what();
|
Logger::error() << "Error during allpages Handler" << e.what();
|
||||||
return errorResponse("Error", "An unknown error occured");
|
return errorResponse("Error", "An unknown error occured");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string HandlerAllPages::accessErrorMessage()
|
std::string HandlerAllPages::accessErrorMessage()
|
||||||
@ -63,3 +58,4 @@ bool HandlerAllPages::canAccess(const Permissions &perms)
|
|||||||
{
|
{
|
||||||
return perms.canSeePageList();
|
return perms.canSeePageList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "handler.h"
|
#include "handler.h"
|
||||||
class HandlerAllPages : public Handler
|
class HandlerAllPages : public Handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HandlerAllPages();
|
HandlerAllPages();
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
|
@ -19,7 +19,7 @@ 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)
|
||||||
{
|
{
|
||||||
@ -28,19 +28,15 @@ Response HandlerCategory::handleRequest(const Request &r)
|
|||||||
Response response;
|
Response response;
|
||||||
std::string categoryname = r.get("category");
|
std::string categoryname = r.get("category");
|
||||||
auto categoryDao = this->database->createCategoryDao();
|
auto categoryDao = this->database->createCategoryDao();
|
||||||
if(!categoryDao->find(categoryname))
|
if(! categoryDao->find(categoryname))
|
||||||
{
|
{
|
||||||
return this->errorResponse("No such category", "A category with the provided name does not exist", 404);
|
return this->errorResponse("No such category", "A category with the provided name does not exist", 404);
|
||||||
}
|
}
|
||||||
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));
|
||||||
@ -62,5 +58,5 @@ std::string HandlerCategory::accessErrorMessage()
|
|||||||
|
|
||||||
bool HandlerCategory::canAccess(const Permissions &perms)
|
bool HandlerCategory::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return perms.canRead(); // TODO: we may need a more specific permission
|
return perms.canRead(); //TODO: we may need a more specific permission
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
class HandlerCategory : public Handler
|
class HandlerCategory : public Handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HandlerCategory();
|
HandlerCategory();
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
|
@ -20,16 +20,18 @@ 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
HandlerDefault::~HandlerDefault()
|
HandlerDefault::~HandlerDefault()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerDefault::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerDefault::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "handler.h"
|
#include "handler.h"
|
||||||
class HandlerDefault : public Handler
|
class HandlerDefault : public Handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
~HandlerDefault() override;
|
~HandlerDefault() override;
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright (c) 2018 Albert S.
|
/* Copyright (c) 2018 Albert S.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -32,63 +32,51 @@ SOFTWARE.
|
|||||||
#include "handlercategory.h"
|
#include "handlercategory.h"
|
||||||
#include "handlerhistory.h"
|
#include "handlerhistory.h"
|
||||||
#include "handlerpagedelete.h"
|
#include "handlerpagedelete.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")
|
||||||
{
|
{
|
||||||
return produce<HandlerDefault>(userSession);
|
return produce<HandlerDefault>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "show")
|
if(action == "show")
|
||||||
{
|
{
|
||||||
return produce<HandlerPageView>(userSession);
|
return produce<HandlerPageView>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "edit")
|
if(action == "edit")
|
||||||
{
|
{
|
||||||
return produce<HandlerPageEdit>(userSession);
|
return produce<HandlerPageEdit>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "login")
|
if(action == "login")
|
||||||
{
|
{
|
||||||
return produce<HandlerLogin>(userSession);
|
return produce<HandlerLogin>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "search")
|
if(action == "search")
|
||||||
{
|
{
|
||||||
return produce<HandlerSearch>(userSession);
|
return produce<HandlerSearch>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "delete")
|
if(action == "delete")
|
||||||
{
|
{
|
||||||
return produce<HandlerPageDelete>(userSession);
|
return produce<HandlerPageDelete>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "allpages")
|
if(action == "allpages")
|
||||||
{
|
{
|
||||||
return produce<HandlerAllPages>(userSession);
|
return produce<HandlerAllPages>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "allcategories")
|
if(action == "allcategories")
|
||||||
{
|
{
|
||||||
return produce<HandlerAllCategories>(userSession);
|
return produce<HandlerAllCategories>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "showcat")
|
if(action == "showcat")
|
||||||
{
|
{
|
||||||
return produce<HandlerCategory>(userSession);
|
return produce<HandlerCategory>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "recent")
|
if(action == "recent")
|
||||||
{
|
{
|
||||||
return produce<HandlerHistory>(userSession);
|
return produce<HandlerHistory>(userSession);
|
||||||
}
|
}
|
||||||
if(action == "usersettings")
|
|
||||||
{
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,18 @@ class HandlerFactory
|
|||||||
UrlProvider &urlProvider;
|
UrlProvider &urlProvider;
|
||||||
ICache &cache;
|
ICache &cache;
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
|
|
||||||
public:
|
HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache) : handlerConfig(handlerConfig), templ(templ) ,db(db), urlProvider(urlprovider), cache(cache) { }
|
||||||
HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache)
|
|
||||||
: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession);
|
std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // HANDLERFACTORY_H
|
#endif // HANDLERFACTORY_H
|
||||||
|
@ -1,157 +0,0 @@
|
|||||||
#include "handlerfeedgenerator.h"
|
|
||||||
#include "../revisionrenderer.h"
|
|
||||||
std::vector<HandlerFeedGenerator::EntryRevisionPair> HandlerFeedGenerator::fetchEntries(
|
|
||||||
std::vector<std::string> categories)
|
|
||||||
{
|
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
|
||||||
auto pageDao = this->database->createPageDao();
|
|
||||||
auto permissionDao = this->database->createPermissionsDao();
|
|
||||||
|
|
||||||
std::vector<EntryRevisionPair> result;
|
|
||||||
QueryOption option;
|
|
||||||
option.includeUnlisted = true;
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
if(member.feedlisted)
|
|
||||||
{
|
|
||||||
Permissions perms = permissionDao->find(member.name, this->userSession->user.login)
|
|
||||||
.value_or(this->userSession->user.permissions);
|
|
||||||
if(perms.canRead())
|
|
||||||
{
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
RevisionRenderer revisionRenderer{*this->templ, *this->database, *this->urlProvider, *this->userSession};
|
|
||||||
|
|
||||||
for(const EntryRevisionPair &entry : entries)
|
|
||||||
{
|
|
||||||
const Page &page = entry.first;
|
|
||||||
const Revision &initialRevision = entry.second;
|
|
||||||
Revision current = revisionDao->getCurrentForPage(page.name).value();
|
|
||||||
std::string url = this->urlProvider->rootUrl() + this->urlProvider->page(page.name);
|
|
||||||
if(initialRevision.timestamp > newestPublished)
|
|
||||||
{
|
|
||||||
newestPublished = initialRevision.timestamp;
|
|
||||||
}
|
|
||||||
std::string entryPublished = utils::formatLocalDate(initialRevision.timestamp, dateformat) + "Z";
|
|
||||||
std::string entryurl =
|
|
||||||
this->urlProvider->combine({this->urlProvider->rootUrl(), this->urlProvider->page(page.name)});
|
|
||||||
TemplatePage atomentry = this->templ->getPage("feeds/atomentry");
|
|
||||||
atomentry.setVar("entrytitle", page.title);
|
|
||||||
atomentry.setVar("entryurl", utils::html_xss(entryurl));
|
|
||||||
atomentry.setVar("entryid", utils::html_xss(entryurl));
|
|
||||||
atomentry.setVar("entrypublished", entryPublished);
|
|
||||||
atomentry.setVar("entryupdated", entryPublished);
|
|
||||||
atomentry.setVar("entrycontent", utils::html_xss(revisionRenderer.renderContent(current, page.title)));
|
|
||||||
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;
|
|
||||||
}
|
|
@ -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
|
|
@ -25,7 +25,7 @@ SOFTWARE.
|
|||||||
#include "../database/exceptions.h"
|
#include "../database/exceptions.h"
|
||||||
Response HandlerHistory::handleRequest(const Request &r)
|
Response HandlerHistory::handleRequest(const Request &r)
|
||||||
{
|
{
|
||||||
QueryOption qo = queryOption(r, DESCENDING);
|
QueryOption qo = queryOption(r);
|
||||||
std::string page = r.get("page");
|
std::string page = r.get("page");
|
||||||
std::string title;
|
std::string title;
|
||||||
if(page.empty())
|
if(page.empty())
|
||||||
@ -46,6 +46,7 @@ Response HandlerHistory::handleRequest(const Request &r)
|
|||||||
title = "History: " + page;
|
title = "History: " + page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned int count = 0;
|
unsigned int count = 0;
|
||||||
std::vector<Revision> resultList;
|
std::vector<Revision> resultList;
|
||||||
auto revisionDao = this->database->createRevisionDao();
|
auto revisionDao = this->database->createRevisionDao();
|
||||||
@ -71,6 +72,7 @@ Response HandlerHistory::handleRequest(const Request &r)
|
|||||||
count = revisionDao->countTotalRevisions(page);
|
count = revisionDao->countTotalRevisions(page);
|
||||||
resultList = revisionDao->getAllRevisionsForPage(page, qo);
|
resultList = revisionDao->getAllRevisionsForPage(page, qo);
|
||||||
templatename = "page_history";
|
templatename = "page_history";
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -90,7 +92,7 @@ Response HandlerHistory::handleRequest(const Request &r)
|
|||||||
TemplatePage historyPage = this->templ->getPage(templatename);
|
TemplatePage historyPage = this->templ->getPage(templatename);
|
||||||
setGeneralVars(historyPage);
|
setGeneralVars(historyPage);
|
||||||
|
|
||||||
if((qo.offset + (unsigned int)resultList.size()) < count)
|
if( (qo.offset + (unsigned int)resultList.size()) < count)
|
||||||
{
|
{
|
||||||
HtmlLink link;
|
HtmlLink link;
|
||||||
link.href = makeSortedLink(qo.limit, qo.offset + qo.limit, qo.order);
|
link.href = makeSortedLink(qo.limit, qo.offset + qo.limit, qo.order);
|
||||||
@ -113,7 +115,7 @@ Response HandlerHistory::handleRequest(const Request &r)
|
|||||||
historyPage.setVar("prevpage", link.render());
|
historyPage.setVar("prevpage", link.render());
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int neworder = (qo.order == DESCENDING) ? ASCENDING : DESCENDING;
|
unsigned int neworder = ( qo.order == DESCENDING ) ? ASCENDING : DESCENDING ;
|
||||||
historyPage.setVar("linkrecentsort", makeSortedLink(qo.limit, qo.offset, neworder));
|
historyPage.setVar("linkrecentsort", makeSortedLink(qo.limit, qo.offset, neworder));
|
||||||
historyPage.setVar("revisionlist", this->templ->renderRevisionList(resultList, page.empty()));
|
historyPage.setVar("revisionlist", this->templ->renderRevisionList(resultList, page.empty()));
|
||||||
historyPage.setVar("title", createPageTitle(title));
|
historyPage.setVar("title", createPageTitle(title));
|
||||||
@ -123,7 +125,9 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
class HandlerHistory : public Handler
|
class HandlerHistory : public Handler
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HandlerHistory();
|
HandlerHistory();
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
|
@ -20,12 +20,13 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,12 @@
|
|||||||
|
|
||||||
class HandlerInvalidAction : public Handler
|
class HandlerInvalidAction : public Handler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
~HandlerInvalidAction() override
|
~HandlerInvalidAction() override { }
|
||||||
{
|
|
||||||
}
|
|
||||||
bool canAccess(const Permissions &perms) override;
|
bool canAccess(const Permissions &perms) override;
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // HANDLERINVALIDACTION_H
|
#endif // HANDLERINVALIDACTION_H
|
||||||
|
@ -20,53 +20,118 @@ SOFTWARE.
|
|||||||
*/
|
*/
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <openssl/evp.h>
|
||||||
#include "handlerlogin.h"
|
#include "handlerlogin.h"
|
||||||
#include "../logger.h"
|
#include "../logger.h"
|
||||||
#include "../authenticator.h"
|
struct LoginFail
|
||||||
|
{
|
||||||
|
std::mutex mutex;
|
||||||
|
std::atomic<unsigned int> count;
|
||||||
|
time_t lastfail;
|
||||||
|
};
|
||||||
|
static std::map<std::string, LoginFail> loginFails;
|
||||||
|
|
||||||
|
//TODO: make configurable
|
||||||
|
bool HandlerLogin::isBanned(std::string ip)
|
||||||
|
{
|
||||||
|
if(utils::hasKey(loginFails, ip))
|
||||||
|
{
|
||||||
|
LoginFail &fl = loginFails[ip];
|
||||||
|
std::lock_guard<std::mutex> lock(fl.mutex);
|
||||||
|
return fl.count > 5 && (time(nullptr) - fl.lastfail) < 1200;
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlerLogin::incFailureCount(std::string ip)
|
||||||
|
{
|
||||||
|
LoginFail &fl = loginFails[ip];
|
||||||
|
fl.count+=1;
|
||||||
|
fl.lastfail = time(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> HandlerLogin::pbkdf5(std::string password, const std::vector<char> &salt)
|
||||||
|
{
|
||||||
|
unsigned char hash[32];
|
||||||
|
const EVP_MD *sha256 = EVP_sha256();
|
||||||
|
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
|
||||||
|
PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
|
||||||
|
|
||||||
|
std::vector<char> result;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < sizeof(hash); i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
result.push_back(static_cast<char>(hash[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Response HandlerLogin::handleRequest(const Request &r)
|
Response HandlerLogin::handleRequest(const Request &r)
|
||||||
{
|
{
|
||||||
|
auto createErrorReesponse = [&]() { return errorResponse("Login error", "The supplied credenetials are incorrect"); };
|
||||||
|
|
||||||
|
if(isBanned(r.getIp()))
|
||||||
|
{
|
||||||
|
return errorResponse("Banned", "You have been banned for too many login attempts. Try again later");
|
||||||
|
}
|
||||||
if(r.param("submit") == "1")
|
if(r.param("submit") == "1")
|
||||||
{
|
{
|
||||||
std::string password = r.post("password");
|
std::string password = r.post("password");
|
||||||
std::string username = r.post("user");
|
std::string username = r.post("user");
|
||||||
|
|
||||||
auto userDao = this->database->createUserDao();
|
auto userDao = this->database->createUserDao();
|
||||||
Authenticator authenticator(*userDao);
|
std::optional<User> user = userDao->find(username);
|
||||||
|
if(!user)
|
||||||
std::variant<User, AuthenticationError> authresult = authenticator.authenticate(username, password, r.getIp());
|
|
||||||
if(std::holds_alternative<User>(authresult))
|
|
||||||
{
|
{
|
||||||
User user = std::get<User>(authresult);
|
return createErrorReesponse();
|
||||||
*(this->userSession) = Session(user);
|
|
||||||
Response r = Response::redirectTemporarily(urlProvider->index());
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
AuthenticationError error = std::get<AuthenticationError>(authresult);
|
if(!user->enabled)
|
||||||
if(error == AuthenticationError::UserDisabled)
|
|
||||||
{
|
{
|
||||||
return errorResponse("Login failed", "The user account has been disabled");
|
return errorResponse("Login failed", "The user account has been disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorResponse("Login failed", "Login with supplied credentials failed");
|
auto hashresult = pbkdf5(password, user.value().salt);
|
||||||
|
//TODO: timing attack
|
||||||
|
if(hashresult == user.value().password)
|
||||||
|
{
|
||||||
|
loginFails.erase(r.getIp());
|
||||||
|
Response r = Response::redirectTemporarily(urlProvider->index());
|
||||||
|
*(this->userSession) = Session(user.value());
|
||||||
|
return r;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//TODO: only if wanted by config
|
||||||
|
incFailureCount(r.getIp());
|
||||||
|
return createErrorReesponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// auto pbkdf5 = pbkdf5(password, user->)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
std::string page = r.get("page");
|
std::string page = r.get("page");
|
||||||
if(page.empty())
|
if(page.empty())
|
||||||
{
|
|
||||||
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"));
|
||||||
Response result;
|
Response result;
|
||||||
result.setStatus(200);
|
result.setStatus(200);
|
||||||
result.setBody(loginTemplatePage.render());
|
result.setBody(loginTemplatePage.render());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandlerLogin::canAccess([[maybe_unused]] const Permissions &perms)
|
bool HandlerLogin::canAccess(const Permissions &perms)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
|
|
||||||
class HandlerLogin : public Handler
|
class HandlerLogin : public Handler
|
||||||
{
|
{
|
||||||
public:
|
private:
|
||||||
|
bool isBanned(std::string ip);
|
||||||
|
void incFailureCount(std::string ip);
|
||||||
|
std::vector<char> pbkdf5(std::string password, const std::vector<char> &salt);
|
||||||
|
public:
|
||||||
HandlerLogin();
|
HandlerLogin();
|
||||||
Response handleRequest(const Request &r) override;
|
Response handleRequest(const Request &r) override;
|
||||||
~HandlerLogin() override
|
~HandlerLogin() override { }
|
||||||
{
|
|
||||||
}
|
|
||||||
bool canAccess(const Permissions &perms) override;
|
bool canAccess(const Permissions &perms) override;
|
||||||
using Handler::Handler;
|
using Handler::Handler;
|
||||||
};
|
};
|
||||||
|
@ -21,32 +21,20 @@ SOFTWARE.
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include "handlerpage.h"
|
#include "handlerpage.h"
|
||||||
|
|
||||||
|
|
||||||
Response HandlerPage::handle(const Request &r)
|
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");
|
return errorResponse("No page given", "No page given to request");
|
||||||
if(title.empty())
|
|
||||||
{
|
|
||||||
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))
|
||||||
{
|
{
|
||||||
std::string createlink = this->urlProvider->editPage(pagename);
|
std::string createlink = this->urlProvider->editPage(pagename);
|
||||||
return errorResponse(
|
return errorResponse("Page not found", "The requested page was not found. Do you want to <a href=\"" + createlink + "\">create</a> it?", 404);
|
||||||
"Page not found",
|
|
||||||
"The requested page was not found. Do you want to <a href=\"" + createlink + "\">create</a> it?", 404);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!canAccess(pagename))
|
if(!canAccess(pagename))
|
||||||
@ -74,7 +62,7 @@ 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);
|
||||||
@ -101,4 +89,10 @@ void HandlerPage::setPageVars(TemplatePage &page, std::string pagename)
|
|||||||
page.setVar("page", pagename);
|
page.setVar("page", pagename);
|
||||||
page.setVar("title", createPageTitle(pagename));
|
page.setVar("title", createPageTitle(pagename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user