Commitok összehasonlítása
	
		
			9 Commit-ok
		
	
	
		
			172129179e
			...
			feature/ma
		
	
	| Szerző | SHA1 | Dátum | |
|---|---|---|---|
| ebd4adf22f | |||
| 3745a778e5 | |||
| cec56d5a73 | |||
| 534d9f74e7 | |||
| 5f674cfe6f | |||
| ea7476a882 | |||
| ab9e5fb0bd | |||
| 7ca04fbf45 | |||
| 8b44dd4e94 | 
							
								
								
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,9 @@ | |||||||
| [submodule "submodules/cpp-httplib"] | [submodule "submodules/cpp-httplib"] | ||||||
| 	path = submodules/cpp-httplib | 	path = submodules/cpp-httplib | ||||||
| 	url = https://github.com/yhirose/cpp-httplib | 	url = https://github.com/yhirose/cpp-httplib | ||||||
| [submodule "submodules/exile.h"] | [submodule "submodules/qssb.h"] | ||||||
| 	path = submodules/exile.h | 	path = submodules/qssb.h | ||||||
| 	url = https://gitea.quitesimple.org/crtxcr/exile.h.git | 	url = https://gitea.quitesimple.org/crtxcr/qssb.h.git | ||||||
|  | [submodule "submodules/qsmaddy"] | ||||||
|  | 	path = submodules/qsmaddy | ||||||
|  | 	url = https://gitea.quitesimple.org/crtxcr/qsmaddy | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,9 +1,9 @@ | |||||||
|  |  | ||||||
| CPPSTD=c++20 |  | ||||||
| CXXFLAGS=-std=$(CPPSTD) -O0 -g -no-pie -pipe -MMD -Wall -Wextra | CXXFLAGS=-std=c++17 -O0 -g -no-pie -pipe -MMD -Wall -Wextra | ||||||
| RELEASE_CXXFLAGS=-std=$(CPPSTD) -O3 -pipe -MMD -Wall -Wextra | RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra | ||||||
| LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs | LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs -lseccomp | ||||||
| INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/exile.h | INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h -I submodules/qsmaddy/include/ | ||||||
|  |  | ||||||
| CXX=g++ | CXX=g++ | ||||||
|  |  | ||||||
| @@ -14,7 +14,6 @@ SOURCES+=$(wildcard handlers/*.cpp) | |||||||
| SOURCES+=$(wildcard database/*.cpp) | SOURCES+=$(wildcard database/*.cpp) | ||||||
| SOURCES+=$(wildcard cache/*.cpp) | SOURCES+=$(wildcard cache/*.cpp) | ||||||
| SOURCES+=$(wildcard sandbox/*.cpp) | SOURCES+=$(wildcard sandbox/*.cpp) | ||||||
| SOURCES+=$(wildcard dynamic/*.cpp) |  | ||||||
|  |  | ||||||
| HEADERS=$(wildcard *.h) | HEADERS=$(wildcard *.h) | ||||||
| HEADERS+=$(wildcard gateway/*.h) | HEADERS+=$(wildcard gateway/*.h) | ||||||
| @@ -22,7 +21,7 @@ HEADERS+=$(wildcard handlers/*.h) | |||||||
| HEADERS+=$(wildcard database/*.h) | HEADERS+=$(wildcard database/*.h) | ||||||
| HEADERS+=$(wildcard cache/*.h) | HEADERS+=$(wildcard cache/*.h) | ||||||
| HEADERS+=$(wildcard sandbox/*.h) | HEADERS+=$(wildcard sandbox/*.h) | ||||||
| HEADERS+=$(wildcard dynamic/*.h) |  | ||||||
|  |  | ||||||
| OBJECTS=$(patsubst %.cpp, %.o, $(SOURCES)) | OBJECTS=$(patsubst %.cpp, %.o, $(SOURCES)) | ||||||
| WIKIOBJECTS=$(filter-out test.o, $(OBJECTS)) | WIKIOBJECTS=$(filter-out test.o, $(OBJECTS)) | ||||||
| @@ -36,25 +35,16 @@ GTEST_DIR = /home/data/SOURCES/gtest/googletest | |||||||
|  |  | ||||||
| GTESTS_TESTDIR = ./tests/ | GTESTS_TESTDIR = ./tests/ | ||||||
|  |  | ||||||
| GTEST_CXXFLAGS=-std=$(CPPSTD) -isystem $(GTEST_DIR)/include -I$(GTEST_DIR) -g -O0 -pipe -Wall -Wextra | GTEST_CXXFLAGS=-std=c++17 -isystem $(GTEST_DIR)/include -I$(GTEST_DIR) -g -O0 -pipe -Wall -Wextra | ||||||
| GTEST_LDFLAGS=-lsqlite3 -g -O0 -lpthread -lcrypto -lstdc++fs | GTEST_LDFLAGS=-lsqlite3 -g -O0 -lpthread -lcrypto -lstdc++fs | ||||||
| GTEST_OBJECTS=$(filter-out qswiki.o, $(WIKIOBJECTS)) | GTEST_OBJECTS=$(filter-out qswiki.o, $(WIKIOBJECTS)) | ||||||
|  |  | ||||||
| .DEFAULT_GOAL := qswiki | .DEFAULT_GOAL := qswiki | ||||||
|  |  | ||||||
| release: CXXFLAGS=$(RELEASE_CXXFLAGS) | release: CXXFLAGS=$(RELEASE_CXXFLAGS) | ||||||
| profile: CXXFLAGS=$(RELEASE_CXXFLAGS) -pg |  | ||||||
| profile: LDFLAGS+= -pg |  | ||||||
|  |  | ||||||
| release: qswiki | release: qswiki | ||||||
| profile: qswiki | qswiki: $(WIKIOBJECTS) | ||||||
|  | 	$(CXX) $(WIKIOBJECTS) ${LDFLAGS} ${INCLUDEFLAGS}  -o qswiki | ||||||
|  |  | ||||||
| exile.o: submodules/exile.h/exile.c |  | ||||||
| 	$(CC) -std=c99 -DHAVE_LANDLOCK=0 -c submodules/exile.h/exile.c -o exile.o |  | ||||||
|  |  | ||||||
| qswiki: $(WIKIOBJECTS) exile.o |  | ||||||
| 	$(CXX) $(WIKIOBJECTS) exile.o ${LDFLAGS} ${INCLUDEFLAGS}  -o qswiki |  | ||||||
|  |  | ||||||
| test: $(TESTOBJECTS) | test: $(TESTOBJECTS) | ||||||
| 	$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test | 	$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test | ||||||
| @@ -63,11 +53,9 @@ gtest: $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) | |||||||
| 	$(CXX) -o gtest $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) $(GTEST_CXXFLAGS) $(GTEST_DIR)/src/gtest_main.cc $(GTEST_DIR)/src/gtest-all.cc $(GTEST_LDFLAGS) | 	$(CXX) -o gtest $(GTESTS_TESTDIR)/*.cpp $(GTEST_OBJECTS) $(GTEST_CXXFLAGS) $(GTEST_DIR)/src/gtest_main.cc $(GTEST_DIR)/src/gtest-all.cc $(GTEST_LDFLAGS) | ||||||
|  |  | ||||||
| %.o:%.cpp | %.o:%.cpp | ||||||
| 	$(CXX) ${CXXFLAGS} ${INCLUDEFLAGS} -c -o $@ $< | 	$(CXX) ${CXXFLAGS} ${LDFLAGS} ${INCLUDEFLAGS} -c -o $@ $< | ||||||
|  |  | ||||||
| version.o:version.cpp |  | ||||||
| 	$(CXX) ${CXXFLAGS} ${INCLUDEFLAGS} -DGITCOMMIT=\"$(shell git rev-parse --short HEAD)\" -c -o $@ $< |  | ||||||
| clean: | clean: | ||||||
| 	rm -f exile.o $(OBJECTS) $(DEPENDS) | 	rm -f $(OBJECTS) $(DEPENDS) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										95
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,84 +1,81 @@ | |||||||
| # 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. | pi. However, the distribution I used back then did not have a PHP package  | ||||||
|  |  | ||||||
| ### 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 |  | ||||||
| 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. | ||||||
|  |  | ||||||
| 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 |   - qssb.h: https://gitea.quitesimple.org/crtxcr/qssb.h | ||||||
|  |   - libseccomp: https://github.com/seccomp/libseccomp | ||||||
|   - sqlite3: https://sqlite.org/index.html |   - sqlite3: https://sqlite.org/index.html | ||||||
|  |      | ||||||
| The first three are header-only libraries that are included as a git submodule. The others must | The first three are header-only libraries that are included as a git submodule. The others must | ||||||
| be installed, e. g. by using your distributions standard method. | be installed, e. g. by using your distributions standard method. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ Authenticator::Authenticator(UserDao &userDao) | |||||||
| // TODO: make failure counter configurable | // TODO: make failure counter configurable | ||||||
| bool Authenticator::isBanned(std::string ip) | bool Authenticator::isBanned(std::string ip) | ||||||
| { | { | ||||||
| 	if(loginFails.contains(ip)) | 	if(utils::hasKey(loginFails, ip)) | ||||||
| 	{ | 	{ | ||||||
| 		LoginFail &fl = loginFails[ip]; | 		LoginFail &fl = loginFails[ip]; | ||||||
| 		std::lock_guard<std::mutex> lock(fl.mutex); | 		std::lock_guard<std::mutex> lock(fl.mutex); | ||||||
| @@ -42,12 +42,11 @@ std::vector<char> Authenticator::pbkdf5(std::string password, const std::vector< | |||||||
| 	unsigned char hash[32]; | 	unsigned char hash[32]; | ||||||
| 	const EVP_MD *sha256 = EVP_sha256(); | 	const EVP_MD *sha256 = EVP_sha256(); | ||||||
| 	const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data()); | 	const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data()); | ||||||
| 	int ret = | 	int ret = PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash); | ||||||
| 		PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash); |  | ||||||
| 	if(ret != 1) | 	if(ret != 1) | ||||||
| 	{ | 	{ | ||||||
| 		Logger::error() << "Authenticator: pbkdf5: Failed to create hash"; | 		Logger::error() << "Authenticator: pbkdf5: Failed to create hash"; | ||||||
| 		return {}; | 		return { }; | ||||||
| 	} | 	} | ||||||
| 	std::vector<char> result; | 	std::vector<char> result; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
| #include <variant> | #include <variant> | ||||||
| #include "database/userdao.h" | #include "database/userdao.h" | ||||||
|  |  | ||||||
| #define AUTH_DEFAULT_SALT_SIZE 32 |  | ||||||
| enum AuthenticationError | enum AuthenticationError | ||||||
| { | { | ||||||
| 	UserNotFound, | 	UserNotFound, | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								cache/fscache.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								cache/fscache.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -46,7 +46,7 @@ void FsCache::removePrefix(std::string_view prefix) | |||||||
| 	// TODO: lock dir | 	// TODO: lock dir | ||||||
| 	for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path{this->path})) | 	for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path{this->path})) | ||||||
| 	{ | 	{ | ||||||
| 		if(std::string_view(entry.path().filename().c_str()).starts_with(prefix)) | 		if(static_cast<std::string>(entry.path().filename()).find(prefix) == 0) | ||||||
| 		{ | 		{ | ||||||
| 			std::filesystem::remove_all(entry); | 			std::filesystem::remove_all(entry); | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								cache/mapcache.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								cache/mapcache.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| #include "mapcache.h" |  | ||||||
							
								
								
									
										38
									
								
								cache/mapcache.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								cache/mapcache.h
									
									
									
									
										vendored
									
									
								
							| @@ -1,38 +0,0 @@ | |||||||
| #ifndef MAPCACHE_H |  | ||||||
| #define MAPCACHE_H |  | ||||||
| #include <map> |  | ||||||
| #include <set> |  | ||||||
| #include <shared_mutex> |  | ||||||
| #include <optional> |  | ||||||
|  |  | ||||||
| /* Thread-Safe Key-Value store */ |  | ||||||
| template <class T> class MapCache |  | ||||||
| { |  | ||||||
|   private: |  | ||||||
| 	std::map<std::string, T> cache; |  | ||||||
| 	mutable std::shared_mutex sharedMutex; |  | ||||||
|  |  | ||||||
|   public: |  | ||||||
| 	std::optional<T> find(const std::string &key) const |  | ||||||
| 	{ |  | ||||||
| 		std::shared_lock<std::shared_mutex> lock(this->sharedMutex); |  | ||||||
| 		auto it = this->cache.find(key); |  | ||||||
| 		if(it != this->cache.end()) |  | ||||||
| 		{ |  | ||||||
| 			return it->second; |  | ||||||
| 		} |  | ||||||
| 		return {}; |  | ||||||
| 	} |  | ||||||
| 	void set(const std::string &key, const T &val) |  | ||||||
| 	{ |  | ||||||
| 		std::lock_guard<std::shared_mutex> lock{sharedMutex}; |  | ||||||
| 		this->cache[key] = val; |  | ||||||
| 	} |  | ||||||
| 	void clear() |  | ||||||
| 	{ |  | ||||||
| 		std::lock_guard<std::shared_mutex> lock{sharedMutex}; |  | ||||||
| 		this->cache.clear(); |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // MAPCACHE_H |  | ||||||
							
								
								
									
										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 |  | ||||||
| @@ -24,7 +24,6 @@ SOFTWARE. | |||||||
| #include "config.h" | #include "config.h" | ||||||
| #include "permissions.h" | #include "permissions.h" | ||||||
| #include "varreplacer.h" | #include "varreplacer.h" | ||||||
|  |  | ||||||
| std::string Config::required(const std::string &key) | std::string Config::required(const std::string &key) | ||||||
| { | { | ||||||
| 	auto it = this->configmap.find(key); | 	auto it = this->configmap.find(key); | ||||||
| @@ -72,22 +71,20 @@ Config::Config(const std::map<std::string, std::string> &map) | |||||||
|  |  | ||||||
| 	this->configmap = map; | 	this->configmap = map; | ||||||
| 	this->wikipath = optional("wikipath", "/"); | 	this->wikipath = optional("wikipath", "/"); | ||||||
|  | 	this->parser = optional("parser", "markdown"); | ||||||
| 	this->handlersConfig.anon_username = optional("anon_username", "anonymouse"); | 	this->handlersConfig.anon_username = optional("anon_username", "anonymouse"); | ||||||
| 	this->handlersConfig.wikiname = required("wikiname"); | 	this->handlersConfig.wikiname = required("wikiname"); | ||||||
| 	this->logfile = required("logfile"); | 	this->logfile = required("logfile"); | ||||||
| 	this->templatepath = required("templatepath"); | 	this->templatepath = required("templatepath"); | ||||||
| 	this->urls.linkallcats = required("linkallcats"); | 	this->urls.linkallcats = required("linkallcats"); | ||||||
| 	this->urls.linkallpages = required("linkallpages"); | 	this->urls.linkallpages = required("linkallpages"); | ||||||
| 	this->urls.linkallpagesrendertype = required ("linkallpagesrendertype"); |  | ||||||
| 	this->urls.linkcategory = required("linkcategory"); | 	this->urls.linkcategory = required("linkcategory"); | ||||||
| 	this->urls.linkcategoryrendertype = required("linkcategoryrendertype"); |  | ||||||
| 	this->urls.linkdelete = required("linkdelete"); | 	this->urls.linkdelete = required("linkdelete"); | ||||||
| 	this->urls.linkedit = required("linkedit"); | 	this->urls.linkedit = required("linkedit"); | ||||||
| 	this->urls.linkhistory = required("linkhistory"); | 	this->urls.linkhistory = required("linkhistory"); | ||||||
| 	this->urls.linkindex = required("linkindex"); | 	this->urls.linkindex = required("linkindex"); | ||||||
| 	this->urls.linklogout = required("linklogout"); | 	this->urls.linklogout = required("linklogout"); | ||||||
| 	this->urls.linkpage = required("linkpage"); | 	this->urls.linkpage = required("linkpage"); | ||||||
| 	this->urls.linkpagebytitle = required("linkpagebytitle"); |  | ||||||
| 	this->urls.linkrecent = required("linkrecent"); | 	this->urls.linkrecent = required("linkrecent"); | ||||||
| 	this->urls.linkrevision = required("linkrevision"); | 	this->urls.linkrevision = required("linkrevision"); | ||||||
| 	this->urls.linksettings = required("linksettings"); | 	this->urls.linksettings = required("linksettings"); | ||||||
| @@ -100,8 +97,6 @@ Config::Config(const std::map<std::string, std::string> &map) | |||||||
| 	this->urls.deletionurl = required("deletionurl"); | 	this->urls.deletionurl = required("deletionurl"); | ||||||
| 	this->urls.adminregisterurl = required("adminregisterurl"); | 	this->urls.adminregisterurl = required("adminregisterurl"); | ||||||
| 	this->urls.usersettingsurl = required("usersettingsurl"); | 	this->urls.usersettingsurl = required("usersettingsurl"); | ||||||
| 	this->urls.rooturl = required("rooturl"); |  | ||||||
| 	this->urls.atomurl = required("atomurl"); |  | ||||||
| 	this->connectionstring = required("connectionstring"); | 	this->connectionstring = required("connectionstring"); | ||||||
|  |  | ||||||
| 	this->handlersConfig.max_pagename_length = optional("max_pagename_length", 256); | 	this->handlersConfig.max_pagename_length = optional("max_pagename_length", 256); | ||||||
| @@ -114,7 +109,7 @@ 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; | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								config.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								config.h
									
									
									
									
									
								
							| @@ -23,11 +23,9 @@ struct ConfigUrls | |||||||
| 	std::string linkindex; | 	std::string linkindex; | ||||||
| 	std::string linkrecent; | 	std::string linkrecent; | ||||||
| 	std::string linkallpages; | 	std::string linkallpages; | ||||||
| 	std::string linkallpagesrendertype; |  | ||||||
| 	std::string linkallcats; | 	std::string linkallcats; | ||||||
| 	std::string linkshere; | 	std::string linkshere; | ||||||
| 	std::string linkpage; | 	std::string linkpage; | ||||||
| 	std::string linkpagebytitle; |  | ||||||
| 	std::string linkrevision; | 	std::string linkrevision; | ||||||
| 	std::string linkhistory; | 	std::string linkhistory; | ||||||
| 	std::string linkedit; | 	std::string linkedit; | ||||||
| @@ -35,7 +33,6 @@ struct ConfigUrls | |||||||
| 	std::string linkdelete; | 	std::string linkdelete; | ||||||
| 	std::string linklogout; | 	std::string linklogout; | ||||||
| 	std::string linkcategory; | 	std::string linkcategory; | ||||||
| 	std::string linkcategoryrendertype; |  | ||||||
| 	std::string loginurl; | 	std::string loginurl; | ||||||
| 	std::string linkrecentsort; | 	std::string linkrecentsort; | ||||||
| 	std::string actionurl; | 	std::string actionurl; | ||||||
| @@ -44,8 +41,6 @@ struct ConfigUrls | |||||||
| 	std::string linkhistorysort; | 	std::string linkhistorysort; | ||||||
| 	std::string adminregisterurl; | 	std::string adminregisterurl; | ||||||
| 	std::string usersettingsurl; | 	std::string usersettingsurl; | ||||||
| 	std::string rooturl; |  | ||||||
| 	std::string atomurl; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class ConfigVariableResolver | class ConfigVariableResolver | ||||||
| @@ -91,6 +86,7 @@ class Config | |||||||
| 	std::string templateprefix; | 	std::string templateprefix; | ||||||
| 	std::string logfile; | 	std::string logfile; | ||||||
| 	std::string connectionstring; | 	std::string connectionstring; | ||||||
|  | 	std::string parser; | ||||||
| 	int session_max_lifetime; | 	int session_max_lifetime; | ||||||
| 	int threadscount; | 	int threadscount; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| #include <optional> | #include <optional> | ||||||
| #include "queryoption.h" | #include "queryoption.h" | ||||||
| #include "../category.h" | #include "../category.h" | ||||||
| #include "../page.h" |  | ||||||
| class CategoryDao | class CategoryDao | ||||||
| { | { | ||||||
|   public: |   public: | ||||||
| @@ -14,8 +14,7 @@ class CategoryDao | |||||||
| 	virtual std::vector<std::string> fetchList(QueryOption o) = 0; | 	virtual std::vector<std::string> fetchList(QueryOption o) = 0; | ||||||
| 	virtual std::optional<Category> find(std::string name) = 0; | 	virtual std::optional<Category> find(std::string name) = 0; | ||||||
| 	virtual void deleteCategory(std::string name) = 0; | 	virtual void deleteCategory(std::string name) = 0; | ||||||
| 	virtual std::vector<Page> fetchMembers(std::string name, QueryOption o) = 0; | 	virtual std::vector<std::string> fetchMembers(std::string name, QueryOption o) = 0; | ||||||
| 	virtual ~CategoryDao() = default; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // CATEGORYDAO_H | #endif // CATEGORYDAO_H | ||||||
|   | |||||||
| @@ -42,7 +42,6 @@ std::optional<Category> CategoryDaoSqlite::find(std::string name) | |||||||
| 	{ | 	{ | ||||||
| 		throwFrom(e); | 		throwFrom(e); | ||||||
| 	} | 	} | ||||||
| 	return {}; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void CategoryDaoSqlite::save(const Category &c) | void CategoryDaoSqlite::save(const Category &c) | ||||||
| @@ -65,9 +64,9 @@ void CategoryDaoSqlite::deleteCategory(std::string name) | |||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
|  |  | ||||||
| 		*db << "BEGIN;"; | 		*db << "BEGIN"; | ||||||
| 		*db << "DELETE FROM categorymember WHERE category = (SELECT id FROM category WHERE name = ?);" << name; | 		*db << "DELETE FROM categorymember WHERE catid = (SELECT id FROM category WHERE name = ?)" << name; | ||||||
| 		*db << "DELETE FROM category WHERE name = ?;" << name; | 		*db << "DELETE FROM category WHERE name = ?" << name; | ||||||
| 		*db << "COMMIT;"; | 		*db << "COMMIT;"; | ||||||
| 	} | 	} | ||||||
| 	catch(sqlite::sqlite_exception &e) | 	catch(sqlite::sqlite_exception &e) | ||||||
| @@ -94,10 +93,9 @@ std::vector<std::string> CategoryDaoSqlite::fetchList(QueryOption o) | |||||||
| 	} | 	} | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  | std::vector<std::string> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption o) | ||||||
| std::vector<Page> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption o) |  | ||||||
| { | { | ||||||
| 	std::vector<Page> result; | 	std::vector<std::string> result; | ||||||
|  |  | ||||||
| 	SqliteQueryOption queryOption{o}; | 	SqliteQueryOption queryOption{o}; | ||||||
| 	std::string queryoptions = | 	std::string queryoptions = | ||||||
| @@ -105,18 +103,11 @@ std::vector<Page> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption | |||||||
|  |  | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		auto query = *db << "SELECT  page.id, page.name AS name, page.title, page.lastrevision, page.visible FROM categorymember INNER JOIN page ON page.id = " | 		auto query = *db << "SELECT page.name AS name FROM categorymember INNER JOIN page ON page.id = " | ||||||
| 							"categorymember.page WHERE category = (SELECT id FROM category WHERE name = ? ) AND " + | 							"categorymember.page WHERE category = (SELECT id FROM category WHERE name = ? ) AND " + | ||||||
| 								queryoptions | 								queryoptions | ||||||
| 						 << name; | 						 << name; | ||||||
| 		query >> [&](unsigned int id, std::string name, std::string title, unsigned int lastrevision, bool visible) { | 		query >> [&](std::string p) { result.push_back(p); }; | ||||||
| 			Page p; |  | ||||||
| 			p.name = name; |  | ||||||
| 			p.pageid = id; |  | ||||||
| 			p.title = title; |  | ||||||
| 			p.current_revision = lastrevision; |  | ||||||
| 			p.listed = visible; |  | ||||||
| 			result.push_back(p); }; |  | ||||||
| 	} | 	} | ||||||
| 	catch(const sqlite::exceptions::no_rows &e) | 	catch(const sqlite::exceptions::no_rows &e) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -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,9 +13,8 @@ class PageDao | |||||||
| 	virtual bool exists(std::string page) const = 0; | 	virtual bool exists(std::string page) const = 0; | ||||||
| 	virtual bool exists(unsigned int id) const = 0; | 	virtual bool exists(unsigned int id) const = 0; | ||||||
| 	virtual std::optional<Page> find(std::string name) = 0; | 	virtual std::optional<Page> find(std::string name) = 0; | ||||||
| 	virtual std::optional<Page> findByTitle(std::string title) = 0; |  | ||||||
| 	virtual std::optional<Page> find(unsigned int id) = 0; | 	virtual std::optional<Page> find(unsigned int id) = 0; | ||||||
| 	virtual std::vector<Page> getPageList(QueryOption option) = 0; | 	virtual std::vector<std::string> getPageList(QueryOption option) = 0; | ||||||
| 	virtual std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) = 0; | 	virtual std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) = 0; | ||||||
| 	virtual void deletePage(std::string page) = 0; | 	virtual void deletePage(std::string page) = 0; | ||||||
| 	virtual void save(const Page &page) = 0; | 	virtual void save(const Page &page) = 0; | ||||||
|   | |||||||
| @@ -52,35 +52,15 @@ std::optional<Page> PageDaoSqlite::find(std::string name) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| std::optional<Page> PageDaoSqlite::findByTitle(std::string title) |  | ||||||
| { |  | ||||||
| 	Page result; |  | ||||||
| 	try |  | ||||||
| 	{ |  | ||||||
| 		auto ps = *db << "SELECT id, name, title, lastrevision, visible FROM page WHERE title = ?"; |  | ||||||
| 		ps << title >> std::tie(result.pageid, result.name, result.title, result.current_revision, result.listed); |  | ||||||
| 	} |  | ||||||
| 	catch(const sqlite::errors::no_rows &e) |  | ||||||
| 	{ |  | ||||||
| 		return {}; |  | ||||||
| 	} |  | ||||||
| 	catch(sqlite::sqlite_exception &e) |  | ||||||
| 	{ |  | ||||||
| 		throwFrom(e); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::optional<Page> PageDaoSqlite::find(unsigned int id) | std::optional<Page> PageDaoSqlite::find(unsigned int id) | ||||||
| { | { | ||||||
| 	Page result; | 	Page result; | ||||||
| 	result.pageid = id; | 	result.pageid = id; | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		auto ps = *db << "SELECT name, title, lastrevision, visible FROM page WHERE id = ?"; | 		auto ps = *db << "SELECT name, lastrevision, visible FROM page WHERE id = ?"; | ||||||
|  |  | ||||||
| 		ps << id >> std::tie(result.name, result.title, result.current_revision, result.listed); | 		ps << id >> std::tie(result.name, result.current_revision, result.listed); | ||||||
| 	} | 	} | ||||||
| 	catch(const sqlite::errors::no_rows &e) | 	catch(const sqlite::errors::no_rows &e) | ||||||
| 	{ | 	{ | ||||||
| @@ -117,38 +97,30 @@ void PageDaoSqlite::save(const Page &page) | |||||||
| { | { | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		*db << "INSERT OR REPLACE INTO page (id, name, title, lastrevision, visible) VALUES((SELECT id FROM page WHERE " | 		*db << "INSERT OR REPLACE INTO page (id, name, lastrevision, visible) VALUES((SELECT id FROM page WHERE name = " | ||||||
| 			   "name = " | 			   "? OR id = ?), ?, ?, ?)" | ||||||
| 			   "? OR id = ?), ?, ?, ?, ?)" | 			<< page.name << page.pageid << page.name << page.current_revision << page.listed; | ||||||
| 			<< page.name << page.pageid << page.name << page.title << page.current_revision << page.listed; |  | ||||||
| 	} | 	} | ||||||
| 	catch(sqlite::sqlite_exception &e) | 	catch(sqlite::sqlite_exception &e) | ||||||
| 	{ | 	{ | ||||||
| 		throwFrom(e); | 		throwFrom(e); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | std::vector<std::string> PageDaoSqlite::getPageList(QueryOption option) | ||||||
| std::vector<Page> PageDaoSqlite::getPageList(QueryOption option) |  | ||||||
| { | { | ||||||
| 	std::vector<Page> result; | 	std::vector<std::string> result; | ||||||
|  |  | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
|  |  | ||||||
| 		std::string queryOption = SqliteQueryOption(option) | 		std::string queryOption = SqliteQueryOption(option) | ||||||
| 									  .setOrderByColumn("lower(name)") | 									  .setOrderByColumn("lower(name)") | ||||||
| 									  .setVisibleColumnName("visible") | 									  .setVisibleColumnName("visible") | ||||||
| 									  .setPrependWhere(true) | 									  .setPrependWhere(true) | ||||||
| 									  .build(); | 									  .build(); | ||||||
| 		std::string query = "SELECT id, name, title, lastrevision, visible FROM page " + queryOption; | 		std::string query = "SELECT name FROM page " + queryOption; | ||||||
| 		*db << query >> [&](unsigned int pageid, std::string name, std::string title,unsigned int current_revision, bool visible ) { |  | ||||||
|  |  | ||||||
| 			Page tmp; | 		*db << query >> [&](std::string name) { result.push_back(name); }; | ||||||
| 			tmp.pageid = pageid; |  | ||||||
| 			tmp.name = name; |  | ||||||
| 			tmp.title = title; |  | ||||||
| 			tmp.current_revision = current_revision; |  | ||||||
| 			tmp.listed = visible; |  | ||||||
| 			result.push_back(tmp); }; |  | ||||||
| 	} | 	} | ||||||
| 	catch(const sqlite::errors::no_rows &e) | 	catch(const sqlite::errors::no_rows &e) | ||||||
| 	{ | 	{ | ||||||
| @@ -211,8 +183,7 @@ std::vector<SearchResult> PageDaoSqlite::search(std::string name, QueryOption op | |||||||
| 		auto query = | 		auto query = | ||||||
| 			*db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? " | 			*db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? " | ||||||
| 				<< ftsEscape(name); | 				<< ftsEscape(name); | ||||||
| 		query >> [&](std::string pagename) | 		query >> [&](std::string pagename) { | ||||||
| 		{ |  | ||||||
| 			SearchResult sresult; | 			SearchResult sresult; | ||||||
| 			sresult.pagename = pagename; | 			sresult.pagename = pagename; | ||||||
| 			sresult.query = name; | 			sresult.query = name; | ||||||
|   | |||||||
| @@ -20,9 +20,8 @@ class PageDaoSqlite : public PageDao, protected SqliteDao | |||||||
| 	bool exists(std::string name) const override; | 	bool exists(std::string name) const override; | ||||||
| 	void save(const Page &page) override; | 	void save(const Page &page) override; | ||||||
| 	std::optional<Page> find(std::string name) override; | 	std::optional<Page> find(std::string name) override; | ||||||
| 	std::optional<Page> findByTitle(std::string title) override; |  | ||||||
| 	std::optional<Page> find(unsigned int id) override; | 	std::optional<Page> find(unsigned int id) override; | ||||||
| 	std::vector<Page> getPageList(QueryOption option) override; | 	std::vector<std::string> getPageList(QueryOption option) override; | ||||||
| 	std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) override; | 	std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) override; | ||||||
| 	using SqliteDao::SqliteDao; | 	using SqliteDao::SqliteDao; | ||||||
| 	int fetchPageId(std::string pagename); | 	int fetchPageId(std::string pagename); | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| #ifndef PERMISSIONSDAO_H | #ifndef PERMISSIONSDAO_H | ||||||
| #define PERMISSIONSDAO_H | #define PERMISSIONSDAO_H | ||||||
| #include <optional> |  | ||||||
| #include "../permissions.h" | #include "../permissions.h" | ||||||
| #include "../user.h" | #include "../user.h" | ||||||
| class PermissionsDao | class PermissionsDao | ||||||
| @@ -8,8 +7,6 @@ class PermissionsDao | |||||||
|   public: |   public: | ||||||
| 	PermissionsDao(); | 	PermissionsDao(); | ||||||
| 	virtual std::optional<Permissions> find(std::string pagename, std::string username) = 0; | 	virtual std::optional<Permissions> find(std::string pagename, std::string username) = 0; | ||||||
| 	virtual void save(std::string pagename, std::string username, Permissions perms) = 0; |  | ||||||
| 	virtual ~PermissionsDao() = default; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // PERMISSIONSDAO_H | #endif // PERMISSIONSDAO_H | ||||||
|   | |||||||
| @@ -41,21 +41,3 @@ std::optional<Permissions> PermissionsDaoSqlite::find(std::string pagename, std: | |||||||
|  |  | ||||||
| 	return Permissions{permissions}; | 	return Permissions{permissions}; | ||||||
| } | } | ||||||
|  |  | ||||||
| void PermissionsDaoSqlite::save(std::string pagename, std::string username, Permissions perms) |  | ||||||
| { |  | ||||||
| 	try |  | ||||||
| 	{ |  | ||||||
| 		auto query = |  | ||||||
| 			*db |  | ||||||
| 			<< "INSERT OR REPLACE INTO permissions (id, permissions, userid, page) VALUES((SELECT id FROM permissions " |  | ||||||
| 			   "WHERE page = (SELECT id FROM page WHERE name = ?) AND userid = (SELECT id FROM user WHERE username = " |  | ||||||
| 			   "?)), ?, (SELECT id FROM user WHERE username = ?), (SELECT id FROM page WHERE name = ?))"; |  | ||||||
| 		query << pagename << username << perms.getPermissions() << username << pagename; |  | ||||||
| 		query.execute(); |  | ||||||
| 	} |  | ||||||
| 	catch(const sqlite::errors::no_rows &e) |  | ||||||
| 	{ |  | ||||||
| 		throwFrom(e); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ class PermissionsDaoSqlite : public PermissionsDao, protected SqliteDao | |||||||
| 	PermissionsDaoSqlite(); | 	PermissionsDaoSqlite(); | ||||||
|  |  | ||||||
| 	std::optional<Permissions> find(std::string pagename, std::string username) override; | 	std::optional<Permissions> find(std::string pagename, std::string username) override; | ||||||
| 	virtual void save(std::string pagename, std::string username, Permissions perms) override; |  | ||||||
| 	using SqliteDao::SqliteDao; | 	using SqliteDao::SqliteDao; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -129,8 +129,9 @@ std::optional<Revision> RevisionDaoSqlite::getCurrentForPage(std::string pagenam | |||||||
| 	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.name, revisionid FROM revision INNER JOIN page ON revision.page = page.id WHERE page.name = ? AND page.lastrevision = revision.revisionid"; | 							"strftime('%s',creationtime), page, revisionid FROM revision WHERE page = (SELECT id FROM " | ||||||
| 		query << pagename; | 							"page WHERE name = ? ) AND revisionid = (SELECT lastrevision FROM page WHERE name = ?)"; | ||||||
|  | 		query << pagename << pagename; | ||||||
| 		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); | ||||||
| 	} | 	} | ||||||
| @@ -154,7 +155,7 @@ std::optional<Revision> RevisionDaoSqlite::getRevisionForPage(std::string pagena | |||||||
| 		auto query = | 		auto query = | ||||||
| 			*db | 			*db | ||||||
| 			<< "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), " | 			<< "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 = ?  "; | 			   "page, revisionid FROM revision WHERE page = (SELECT id FROM page WHERE 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); | ||||||
|   | |||||||
| @@ -22,17 +22,18 @@ SOFTWARE. | |||||||
|  |  | ||||||
| bool SqliteDao::execBool(sqlite::database_binder &binder) const | bool SqliteDao::execBool(sqlite::database_binder &binder) const | ||||||
| { | { | ||||||
| 	bool result = false; | 	bool result; | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
|  | 		bool result; | ||||||
| 		binder >> result; | 		binder >> result; | ||||||
|  | 		return result; | ||||||
| 	} | 	} | ||||||
| 	catch(sqlite::sqlite_exception &e) | 	catch(sqlite::sqlite_exception &e) | ||||||
| 	{ | 	{ | ||||||
| 		// TODO: well, we may want to check whether rows have found or not and thus log this here | 		// TODO: well, we may want to check whether rows have found or not and thus log this here | ||||||
| 		result = false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	return result; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int SqliteDao::execInt(sqlite::database_binder &binder) const | int SqliteDao::execInt(sqlite::database_binder &binder) const | ||||||
| @@ -51,5 +52,4 @@ int SqliteDao::execInt(sqlite::database_binder &binder) const | |||||||
| 	{ | 	{ | ||||||
| 		throwFrom(e); | 		throwFrom(e); | ||||||
| 	} | 	} | ||||||
| 	return 0; |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -37,8 +37,6 @@ 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 | ||||||
|   | |||||||
| @@ -46,18 +46,15 @@ SqliteQueryOption &SqliteQueryOption::setPrependWhere(bool b) | |||||||
| std::string SqliteQueryOption::build() | std::string SqliteQueryOption::build() | ||||||
| { | { | ||||||
| 	std::string result; | 	std::string result; | ||||||
| 	if(this->prependWhere) |  | ||||||
| 	{ |  | ||||||
| 		result += "WHERE "; |  | ||||||
| 	} |  | ||||||
| 	if(!o.includeInvisible && !this->visibleColumnName.empty()) | 	if(!o.includeInvisible && !this->visibleColumnName.empty()) | ||||||
| 	{ | 	{ | ||||||
|  | 		if(this->prependWhere) | ||||||
|  | 		{ | ||||||
|  | 			result += "WHERE "; | ||||||
|  | 		} | ||||||
| 		result += this->visibleColumnName + " = 1"; | 		result += this->visibleColumnName + " = 1"; | ||||||
| 	} | 	} | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		result += " 1 = 1"; |  | ||||||
| 	} |  | ||||||
| 	result += " ORDER BY " + orderByColumnName; | 	result += " ORDER BY " + orderByColumnName; | ||||||
| 	if(o.order == ASCENDING) | 	if(o.order == ASCENDING) | ||||||
| 	{ | 	{ | ||||||
| @@ -69,8 +66,7 @@ std::string SqliteQueryOption::build() | |||||||
| 	} | 	} | ||||||
| 	// TODO: limits for offset? | 	// TODO: limits for offset? | ||||||
| 	if(o.limit > 0) | 	if(o.limit > 0) | ||||||
| 	{ |  | ||||||
| 		result += " LIMIT " + std::to_string(o.limit) + " OFFSET " + std::to_string(o.offset); | 		result += " LIMIT " + std::to_string(o.limit) + " OFFSET " + std::to_string(o.offset); | ||||||
| 	} |  | ||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include <optional> | #include <optional> | ||||||
| #include "../user.h" | #include "../user.h" | ||||||
| #include "queryoption.h" |  | ||||||
| class UserDao | class UserDao | ||||||
| { | { | ||||||
|   public: |   public: | ||||||
| @@ -11,7 +10,6 @@ class UserDao | |||||||
| 	virtual bool exists(std::string username) = 0; | 	virtual bool exists(std::string username) = 0; | ||||||
| 	virtual std::optional<User> find(std::string username) = 0; | 	virtual std::optional<User> find(std::string username) = 0; | ||||||
| 	virtual std::optional<User> find(int id) = 0; | 	virtual std::optional<User> find(int id) = 0; | ||||||
| 	virtual std::vector<User> list(QueryOption o) = 0; |  | ||||||
| 	virtual void deleteUser(std::string username) = 0; | 	virtual void deleteUser(std::string username) = 0; | ||||||
| 	virtual void save(const User &u) = 0; | 	virtual void save(const User &u) = 0; | ||||||
| 	virtual ~UserDao(){}; | 	virtual ~UserDao(){}; | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ SOFTWARE. | |||||||
| #include <memory> | #include <memory> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include "userdaosqlite.h" | #include "userdaosqlite.h" | ||||||
| #include "sqlitequeryoption.h" |  | ||||||
|  |  | ||||||
| UserDaoSqlite::UserDaoSqlite() | UserDaoSqlite::UserDaoSqlite() | ||||||
| { | { | ||||||
| @@ -37,6 +36,7 @@ bool UserDaoSqlite::exists(std::string username) | |||||||
|  |  | ||||||
| std::optional<User> UserDaoSqlite::find(std::string username) | std::optional<User> UserDaoSqlite::find(std::string username) | ||||||
| { | { | ||||||
|  |  | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		User user; | 		User user; | ||||||
| @@ -47,7 +47,7 @@ std::optional<User> UserDaoSqlite::find(std::string username) | |||||||
| 		stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled); | 		stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled); | ||||||
| 		user.permissions = Permissions{perms}; | 		user.permissions = Permissions{perms}; | ||||||
|  |  | ||||||
| 		return user; | 		return std::move(user); | ||||||
| 	} | 	} | ||||||
| 	catch(const sqlite::errors::no_rows &e) | 	catch(const sqlite::errors::no_rows &e) | ||||||
| 	{ | 	{ | ||||||
| @@ -57,7 +57,6 @@ std::optional<User> UserDaoSqlite::find(std::string username) | |||||||
| 	{ | 	{ | ||||||
| 		throwFrom(e); | 		throwFrom(e); | ||||||
| 	} | 	} | ||||||
| 	return {}; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::optional<User> UserDaoSqlite::find(int id) | std::optional<User> UserDaoSqlite::find(int id) | ||||||
| @@ -71,7 +70,7 @@ std::optional<User> UserDaoSqlite::find(int id) | |||||||
| 		stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled); | 		stmt >> std::tie(user.login, user.password, user.salt, perms, user.enabled); | ||||||
| 		user.permissions = Permissions{perms}; | 		user.permissions = Permissions{perms}; | ||||||
|  |  | ||||||
| 		return user; | 		return std::move(user); | ||||||
| 	} | 	} | ||||||
| 	catch(const sqlite::errors::no_rows &e) | 	catch(const sqlite::errors::no_rows &e) | ||||||
| 	{ | 	{ | ||||||
| @@ -81,43 +80,9 @@ std::optional<User> UserDaoSqlite::find(int id) | |||||||
| 	{ | 	{ | ||||||
| 		throwFrom(e); | 		throwFrom(e); | ||||||
| 	} | 	} | ||||||
| 	return {}; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::vector<User> UserDaoSqlite::list(QueryOption o) | void UserDaoSqlite::deleteUser(std::string username) | ||||||
| { |  | ||||||
| 	std::vector<User> result; |  | ||||||
|  |  | ||||||
| 	try |  | ||||||
| 	{ |  | ||||||
|  |  | ||||||
| 		std::string queryOption = SqliteQueryOption(o).setOrderByColumn("username").setPrependWhere(true).build(); |  | ||||||
| 		std::string query = "SELECT username, password, salt, permissions, enabled FROM user " + queryOption; |  | ||||||
|  |  | ||||||
| 		*db << query >> |  | ||||||
| 			[&](std::string username, std::vector<char> pw, std::vector<char> salt, int permisisons, bool enabled) |  | ||||||
| 		{ |  | ||||||
| 			User tmp; |  | ||||||
| 			tmp.login = username; |  | ||||||
| 			tmp.password = pw; |  | ||||||
| 			tmp.salt = salt; |  | ||||||
| 			tmp.permissions = Permissions{permisisons}; |  | ||||||
| 			tmp.enabled = enabled; |  | ||||||
| 			result.push_back(tmp); |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 	catch(const sqlite::errors::no_rows &e) |  | ||||||
| 	{ |  | ||||||
| 		return result; |  | ||||||
| 	} |  | ||||||
| 	catch(sqlite::sqlite_exception &e) |  | ||||||
| 	{ |  | ||||||
| 		throwFrom(e); |  | ||||||
| 	} |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void UserDaoSqlite::deleteUser([[maybe_unused]] std::string username) |  | ||||||
| { | { | ||||||
| 	// What to do with the contributions of the user? | 	// What to do with the contributions of the user? | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ class UserDaoSqlite : public UserDao, protected SqliteDao | |||||||
| 	std::optional<User> find(std::string username); | 	std::optional<User> find(std::string username); | ||||||
| 	std::optional<User> find(int id); | 	std::optional<User> find(int id); | ||||||
|  |  | ||||||
| 	std::vector<User> list(QueryOption o); |  | ||||||
| 	void deleteUser(std::string username); | 	void deleteUser(std::string username); | ||||||
| 	void save(const User &u); | 	void save(const User &u); | ||||||
| 	using SqliteDao::SqliteDao; | 	using SqliteDao::SqliteDao; | ||||||
|   | |||||||
| @@ -1,8 +0,0 @@ | |||||||
| #include "dynamiccontent.h" |  | ||||||
|  |  | ||||||
| DynamicContent::DynamicContent(Template &templ, Database &database, UrlProvider &provider) |  | ||||||
| { |  | ||||||
| 	this->templ = &templ; |  | ||||||
| 	this->database = &database; |  | ||||||
| 	this->urlProvider = &provider; |  | ||||||
| } |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| #ifndef DYNAMICCONTENT_H |  | ||||||
| #define DYNAMICCONTENT_H |  | ||||||
| #include <string> |  | ||||||
| #include "../database/database.h" |  | ||||||
| #include "../template.h" |  | ||||||
| #include "../urlprovider.h" |  | ||||||
| class DynamicContent |  | ||||||
| { |  | ||||||
|   protected: |  | ||||||
| 	Template *templ; |  | ||||||
| 	Database *database; |  | ||||||
| 	UrlProvider *urlProvider; |  | ||||||
|  |  | ||||||
| 	std::string argument; |  | ||||||
|  |  | ||||||
|   public: |  | ||||||
| 	DynamicContent(Template &templ, Database &database, UrlProvider &urlProvider); |  | ||||||
| 	virtual std::string render() = 0; |  | ||||||
| 	virtual void setArgument(std::string argument) |  | ||||||
| 	{ |  | ||||||
| 		this->argument = argument; |  | ||||||
| 	} |  | ||||||
| 	virtual ~DynamicContent() |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // DYNAMICCONTENT_H |  | ||||||
| @@ -1,28 +0,0 @@ | |||||||
| #ifndef DYNAMICCONTENTFACTORY_H |  | ||||||
| #define DYNAMICCONTENTFACTORY_H |  | ||||||
|  |  | ||||||
| #include "dynamiccontent.h" |  | ||||||
|  |  | ||||||
| class DynamicContentFactory |  | ||||||
| { |  | ||||||
| private: |  | ||||||
| 	Template *templ; |  | ||||||
| 	Database *db; |  | ||||||
| 	UrlProvider *urlProvider; |  | ||||||
| 	 |  | ||||||
| public: |  | ||||||
| 	DynamicContentFactory(Template &templ, Database &db, UrlProvider &urlProvider) |  | ||||||
| 	{ |  | ||||||
| 		this->templ = &templ; |  | ||||||
| 		this->db = &db; |  | ||||||
| 		this->urlProvider = &urlProvider; |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	template <class T> inline std::shared_ptr<T> createDynamicContent() |  | ||||||
| 	{ |  | ||||||
| 		return std::make_shared<T>(*this->templ, *this->db, *this->urlProvider); |  | ||||||
| 	} |  | ||||||
| 	 |  | ||||||
| 	 |  | ||||||
| }; |  | ||||||
| #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,39 +0,0 @@ | |||||||
| #include <chrono> |  | ||||||
| #include "dynamiccontentpostlist.h" |  | ||||||
|  |  | ||||||
| std::string DynamicContentPostList::render() |  | ||||||
| { |  | ||||||
| 	auto categoryDao = this->database->createCategoryDao(); |  | ||||||
| 	auto pageDao = this->database->createPageDao(); |  | ||||||
| 	auto revisionDao = this->database->createRevisionDao(); |  | ||||||
| 	QueryOption option; |  | ||||||
| 	option.includeInvisible = false; |  | ||||||
| 	auto members = categoryDao->fetchMembers(this->argument, option); |  | ||||||
| 	std::vector<std::pair<std::string, time_t>> pageList; |  | ||||||
| 	for(const Page &member : members) |  | ||||||
| 	{ |  | ||||||
| 		auto revision = revisionDao->getRevisionForPage(member.name, 1); |  | ||||||
| 		pageList.push_back({member.name, revision->timestamp}); |  | ||||||
| 	} |  | ||||||
| 	std::sort(pageList.begin(), pageList.end(), |  | ||||||
| 			  [](std::pair<std::string, time_t> &a, std::pair<std::string, time_t> &b) { return a.second > b.second; }); |  | ||||||
|  |  | ||||||
| 	std::string postListBegin = this->templ->loadResolvedPart("dynamic/postlistbegin"); |  | ||||||
| 	std::string postListEnd = this->templ->loadResolvedPart("dynamic/postlistend"); |  | ||||||
| 	std::string postLink = this->templ->loadResolvedPart("dynamic/postlistlink"); |  | ||||||
| 	std::stringstream stream; |  | ||||||
| 	stream << postListBegin; |  | ||||||
| 	for(auto &pair : pageList) |  | ||||||
| 	{ |  | ||||||
| 		std::string link = this->urlProvider->page(pair.first); |  | ||||||
| 		std::string date = utils::toISODate(pair.second); |  | ||||||
| 		Varreplacer replacer{"{"}; |  | ||||||
| 		replacer.addKeyValue("url", link); |  | ||||||
| 		replacer.addKeyValue("date", date); |  | ||||||
| 		replacer.addKeyValue("title", pageDao->find(pair.first)->title); |  | ||||||
|  |  | ||||||
| 		stream << replacer.parse(postLink); |  | ||||||
| 	} |  | ||||||
| 	stream << postListEnd; |  | ||||||
| 	return stream.str(); |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| @@ -41,7 +41,7 @@ Request HttpGateway::convertRequest(httplib::Request request) | |||||||
| 	// TODO: this eats resources, where perhaps it does not need to. move it to request? | 	// TODO: this eats resources, where perhaps it does not need to. move it to request? | ||||||
| 	for(auto &it : request.params) | 	for(auto &it : request.params) | ||||||
| 	{ | 	{ | ||||||
| 		it.second = utils::html_xss(it.second); | 		it.second = utils::html_xss(std::string{it.second}); | ||||||
| 	} | 	} | ||||||
| 	if(request.method == "GET") | 	if(request.method == "GET") | ||||||
| 	{ | 	{ | ||||||
| @@ -83,8 +83,7 @@ void HttpGateway::work(RequestWorker &worker) | |||||||
| { | { | ||||||
| 	httplib::Server server; | 	httplib::Server server; | ||||||
| 	server.set_payload_max_length(this->maxPayloadLength); | 	server.set_payload_max_length(this->maxPayloadLength); | ||||||
| 	auto handler = [&](const httplib::Request &req, httplib::Response &res) | 	auto handler = [&](const httplib::Request &req, httplib::Response &res) { | ||||||
| 	{ |  | ||||||
| 		Request wikiRequest = convertRequest(req); | 		Request wikiRequest = convertRequest(req); | ||||||
| 		Logger::debug() << "httpgateway: received request " << wikiRequest; | 		Logger::debug() << "httpgateway: received request " << wikiRequest; | ||||||
| 		Response wikiresponse = worker.processRequest(wikiRequest); | 		Response wikiresponse = worker.processRequest(wikiRequest); | ||||||
|   | |||||||
| @@ -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" | ||||||
|   | |||||||
							
								
								
									
										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; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| @@ -34,7 +34,7 @@ void Handler::setGeneralVars(TemplatePage &page) | |||||||
| } | } | ||||||
| Response Handler::errorResponse(std::string errortitle, std::string errormessage, int status) | Response Handler::errorResponse(std::string errortitle, std::string errormessage, int status) | ||||||
| { | { | ||||||
| 	TemplatePage error = this->templ->getPage("error"); | 	TemplatePage &error = this->templ->getPage("error"); | ||||||
| 	error.setVar("title", createPageTitle(errortitle)); | 	error.setVar("title", createPageTitle(errortitle)); | ||||||
| 	error.setVar("errortitle", errortitle); | 	error.setVar("errortitle", errortitle); | ||||||
| 	error.setVar("errormessage", errormessage); | 	error.setVar("errormessage", errormessage); | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| #ifndef HANDLER_H | #ifndef HANDLER_H | ||||||
| #define HANDLER_H | #define HANDLER_H | ||||||
| #include <memory> |  | ||||||
| #include "../config.h" | #include "../config.h" | ||||||
| #include "../response.h" | #include "../response.h" | ||||||
| #include "../request.h" | #include "../request.h" | ||||||
| @@ -10,12 +9,13 @@ | |||||||
| #include "../database/queryoption.h" | #include "../database/queryoption.h" | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "../cache/icache.h" | #include "../cache/icache.h" | ||||||
| #include "../dynamic/dynamiccontent.h" | #include "../iparser.h" | ||||||
|  |  | ||||||
| class Handler | class Handler | ||||||
| { | { | ||||||
|   protected: |   protected: | ||||||
| 	ICache *cache; | 	ICache *cache; | ||||||
|  | 	IParser *parser; | ||||||
| 	Template *templ; | 	Template *templ; | ||||||
| 	Database *database; | 	Database *database; | ||||||
| 	Session *userSession; | 	Session *userSession; | ||||||
| @@ -28,7 +28,7 @@ class Handler | |||||||
|  |  | ||||||
|   public: |   public: | ||||||
| 	Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider, | 	Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider, | ||||||
| 			ICache &cache) | 			ICache &cache, IParser &parser) | ||||||
| 	{ | 	{ | ||||||
| 		this->handlersConfig = &handlersConfig; | 		this->handlersConfig = &handlersConfig; | ||||||
| 		this->templ = &templ; | 		this->templ = &templ; | ||||||
| @@ -36,15 +36,16 @@ class Handler | |||||||
| 		this->userSession = &userSession; | 		this->userSession = &userSession; | ||||||
| 		this->urlProvider = &provider; | 		this->urlProvider = &provider; | ||||||
| 		this->cache = &cache; | 		this->cache = &cache; | ||||||
|  | 		this->parser = &parser; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	virtual Response handle(const Request &r); | 	virtual Response handle(const Request &r); | ||||||
| 	virtual Response handleRequest([[maybe_unused]] const Request &r) | 	virtual Response handleRequest(const Request &r) | ||||||
| 	{ | 	{ | ||||||
| 		return this->errorResponse("Invalid action", "This action is not implemented yet"); | 		return this->errorResponse("Invalid action", "This action is not implemented yet"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	virtual bool canAccess([[maybe_unused]] const Permissions &perms) | 	virtual bool canAccess(const Permissions &perms) | ||||||
| 	{ | 	{ | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| @@ -56,12 +57,6 @@ class Handler | |||||||
| 	virtual ~Handler() | 	virtual ~Handler() | ||||||
| 	{ | 	{ | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	template <class T> inline std::shared_ptr<T> createDynamic() |  | ||||||
| 	{ |  | ||||||
| 		return std::make_shared<T>(*this->templ, *this->database, *this->urlProvider); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Response errorResponse(std::string errortitle, std::string errormessage, int status = 200); | 	Response errorResponse(std::string errortitle, std::string errormessage, int status = 200); | ||||||
| 	std::string createPageTitle(std::string append); | 	std::string createPageTitle(std::string append); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ Response HandlerAllCategories::handleRequest(const Request &r) | |||||||
| 			"No categories", | 			"No categories", | ||||||
| 			"This wiki does not have any categories defined yet or your query options did not yield any results"); | 			"This wiki does not have any categories defined yet or your query options did not yield any results"); | ||||||
| 	} | 	} | ||||||
| 	TemplatePage searchPage = this->templ->getPage("allcategories"); | 	TemplatePage &searchPage = this->templ->getPage("allcategories"); | ||||||
| 	std::string body = | 	std::string body = | ||||||
| 		this->templ->renderSearch(resultList, [&](std::string str) { return this->urlProvider->category(str); }); | 		this->templ->renderSearch(resultList, [&](std::string str) { return this->urlProvider->category(str); }); | ||||||
| 	searchPage.setVar("categorylist", body); | 	searchPage.setVar("categorylist", body); | ||||||
|   | |||||||
| @@ -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; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||||
| SOFTWARE. | SOFTWARE. | ||||||
| */ | */ | ||||||
| #include "handlercategory.h" | #include "handlercategory.h" | ||||||
| #include "../pagelistrenderer.h" |  | ||||||
|  |  | ||||||
| Response HandlerCategory::handleRequest(const Request &r) | Response HandlerCategory::handleRequest(const Request &r) | ||||||
| { | { | ||||||
| @@ -34,13 +33,9 @@ Response HandlerCategory::handleRequest(const Request &r) | |||||||
| 		} | 		} | ||||||
| 		QueryOption qo = queryOption(r); | 		QueryOption qo = queryOption(r); | ||||||
| 		auto resultList = categoryDao->fetchMembers(categoryname, qo); | 		auto resultList = categoryDao->fetchMembers(categoryname, qo); | ||||||
| 		TemplatePage searchPage = this->templ->getPage("show_category"); | 		TemplatePage &searchPage = this->templ->getPage("show_category"); | ||||||
| 		PageListRenderer pagelistrender(*this->templ, *this->urlProvider, *this->database); | 		std::string body = this->templ->renderSearch(resultList); | ||||||
|  | 		searchPage.setVar("pagelist", body); | ||||||
| 		std::string body = pagelistrender.render(resultList, r.get("rendertype")); |  | ||||||
| 		searchPage.setVar("pagelistcontent", body); |  | ||||||
| 		searchPage.setVar("pagelistletterlink", this->urlProvider->category(categoryname, PageListRenderer::RENDER_GROUP_BY_LETTER)); |  | ||||||
| 		searchPage.setVar("pagelistcreationdatelink",  this->urlProvider->category(categoryname, PageListRenderer::RENDER_GROUP_BY_CREATIONDATE)); |  | ||||||
| 		searchPage.setVar("categoryname", categoryname); | 		searchPage.setVar("categoryname", categoryname); | ||||||
| 		setGeneralVars(searchPage); | 		setGeneralVars(searchPage); | ||||||
| 		searchPage.setVar("title", createPageTitle("Category: " + categoryname)); | 		searchPage.setVar("title", createPageTitle("Category: " + categoryname)); | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ SOFTWARE. | |||||||
| */ | */ | ||||||
| #include "handlerdefault.h" | #include "handlerdefault.h" | ||||||
|  |  | ||||||
| Response HandlerDefault::handleRequest([[maybe_unused]] const Request &r) | Response HandlerDefault::handleRequest(const Request &r) | ||||||
| { | { | ||||||
| 	return Response::redirectTemporarily(this->urlProvider->index()); | 	return Response::redirectTemporarily(this->urlProvider->index()); | ||||||
| } | } | ||||||
| @@ -29,7 +29,7 @@ HandlerDefault::~HandlerDefault() | |||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
| bool HandlerDefault::canAccess([[maybe_unused]] const Permissions &perms) | bool HandlerDefault::canAccess(const Permissions &perms) | ||||||
| { | { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -33,8 +33,7 @@ SOFTWARE. | |||||||
| #include "handlerhistory.h" | #include "handlerhistory.h" | ||||||
| #include "handlerpagedelete.h" | #include "handlerpagedelete.h" | ||||||
| #include "handlerusersettings.h" | #include "handlerusersettings.h" | ||||||
| #include "handlerversion.h" |  | ||||||
| #include "handlerfeedgenerator.h" |  | ||||||
| std::unique_ptr<Handler> HandlerFactory::createHandler(const std::string &action, Session &userSession) | std::unique_ptr<Handler> HandlerFactory::createHandler(const std::string &action, Session &userSession) | ||||||
| { | { | ||||||
| 	if(action == "" || action == "index") | 	if(action == "" || action == "index") | ||||||
| @@ -81,14 +80,6 @@ std::unique_ptr<Handler> HandlerFactory::createHandler(const std::string &action | |||||||
| 	{ | 	{ | ||||||
| 		return produce<HandlerUserSettings>(userSession); | 		return produce<HandlerUserSettings>(userSession); | ||||||
| 	} | 	} | ||||||
| 	if(action == "version") |  | ||||||
| 	{ |  | ||||||
| 		return produce<HandlerVersion>(userSession); |  | ||||||
| 	} |  | ||||||
| 	if(action == "feed") |  | ||||||
| 	{ |  | ||||||
| 		return produce<HandlerFeedGenerator>(userSession); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return produce<HandlerInvalidAction>(userSession); | 	return produce<HandlerInvalidAction>(userSession); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,15 +10,16 @@ class HandlerFactory | |||||||
| 	Database &db; | 	Database &db; | ||||||
| 	UrlProvider &urlProvider; | 	UrlProvider &urlProvider; | ||||||
| 	ICache &cache; | 	ICache &cache; | ||||||
|  | 	IParser &parser; | ||||||
|  |  | ||||||
| 	template <class T> inline std::unique_ptr<T> produce(Session &userSession) | 	template <class T> inline std::unique_ptr<T> produce(Session &userSession) | ||||||
| 	{ | 	{ | ||||||
| 		return std::make_unique<T>(handlerConfig, templ, db, userSession, urlProvider, cache); | 		return std::make_unique<T>(handlerConfig, templ, db, userSession, urlProvider, cache, parser); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   public: |   public: | ||||||
| 	HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache) | 	HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache, IParser &parser) | ||||||
| 		: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache) | 		: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache), parser(parser) | ||||||
| 	{ | 	{ | ||||||
| 	} | 	} | ||||||
| 	std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession); | 	std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession); | ||||||
|   | |||||||
| @@ -1,150 +0,0 @@ | |||||||
| #include "handlerfeedgenerator.h" |  | ||||||
| #include "../parser.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(); |  | ||||||
|  |  | ||||||
| 	std::vector<EntryRevisionPair> result; |  | ||||||
| 	QueryOption option; |  | ||||||
| 	option.includeInvisible = false; |  | ||||||
| 	// option.limit = 20; |  | ||||||
| 	 |  | ||||||
| 	auto comppage = [](const Page &a, const Page &b) { return a.name < b.name; }; |  | ||||||
| 	std::set<Page, decltype(comppage)> members (comppage); |  | ||||||
| 	if(categories.empty()) |  | ||||||
| 	{ |  | ||||||
| 		auto pages = pageDao->getPageList(option); |  | ||||||
| 		std::copy(pages.begin(), pages.end(), std::inserter(members, members.end())); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		auto categoryDao = this->database->createCategoryDao(); |  | ||||||
| 		for(std::string cat : categories) |  | ||||||
| 		{ |  | ||||||
| 			if(!categoryDao->find(cat)) |  | ||||||
| 			{ |  | ||||||
| 				throw std::runtime_error("No such category"); |  | ||||||
| 			} |  | ||||||
| 			auto catmembers = categoryDao->fetchMembers(cat, option); |  | ||||||
| 			std::copy(catmembers.begin(), catmembers.end(), std::inserter(members, members.end())); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	for(const Page &member : members) |  | ||||||
| 	{ |  | ||||||
| 		auto revision = revisionDao->getRevisionForPage(member.name, 1).value(); |  | ||||||
| 		result.push_back({member, revision}); |  | ||||||
| 	} |  | ||||||
| 	std::sort(result.begin(), result.end(), |  | ||||||
| 			  [](EntryRevisionPair &a, EntryRevisionPair &b) { return a.second.timestamp > b.second.timestamp; }); |  | ||||||
|  |  | ||||||
| 	const int maxResults = 20; |  | ||||||
| 	if(result.size() > maxResults) |  | ||||||
| 	{ |  | ||||||
| 		result.erase(result.begin() + maxResults - 1, result.end()); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string HandlerFeedGenerator::generateAtom(const std::vector<HandlerFeedGenerator::EntryRevisionPair> &entries, |  | ||||||
| 											   std::string filter) |  | ||||||
| { |  | ||||||
|  |  | ||||||
| 	std::stringstream stream; |  | ||||||
| 	// don't care about offset for now especially since "%z" does not return what we need exactly (with ':') |  | ||||||
| 	const std::string dateformat = "%Y-%m-%dT%T"; |  | ||||||
|  |  | ||||||
| 	time_t newestPublished = 0; |  | ||||||
| 	std::string atomfooter = this->templ->loadResolvedPart("feeds/atomfooter"); |  | ||||||
| 	auto revisionDao = this->database->createRevisionDao(); |  | ||||||
| 	auto pageDao = this->database->createPageDao(); |  | ||||||
|  |  | ||||||
| 	std::string subtitle = filter; |  | ||||||
| 	if(utils::trim(filter).empty()) |  | ||||||
| 	{ |  | ||||||
| 		subtitle = "All pages"; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	RevisionRenderer revisionRenderer { *this->templ, *this->database, *this->urlProvider }; |  | ||||||
|  |  | ||||||
| 	for(const EntryRevisionPair &entry : entries) |  | ||||||
| 	{ |  | ||||||
| 		const Page &page = entry.first; |  | ||||||
| 		const Revision &initialRevision = entry.second; |  | ||||||
| 		Revision current = revisionDao->getCurrentForPage(page.name).value(); |  | ||||||
| 		std::string url = this->urlProvider->rootUrl() + this->urlProvider->page(page.name); |  | ||||||
| 		if(initialRevision.timestamp > newestPublished) |  | ||||||
| 		{ |  | ||||||
| 			newestPublished = initialRevision.timestamp; |  | ||||||
| 		} |  | ||||||
| 		std::string entryPublished = utils::formatLocalDate(initialRevision.timestamp, dateformat) + "Z"; |  | ||||||
| 		std::string entryUpdated = utils::formatLocalDate(current.timestamp, dateformat) + "Z"; |  | ||||||
| 		std::string entryurl = |  | ||||||
| 			this->urlProvider->combine({this->urlProvider->rootUrl(), this->urlProvider->page(page.name)}); |  | ||||||
| 		TemplatePage atomentry = this->templ->getPage("feeds/atomentry"); |  | ||||||
| 		atomentry.setVar("entrytitle", page.title); |  | ||||||
| 		atomentry.setVar("entryurl", utils::html_xss(entryurl)); |  | ||||||
| 		atomentry.setVar("entryid", utils::html_xss(entryurl)); |  | ||||||
| 		atomentry.setVar("entrypublished", entryPublished); |  | ||||||
| 		atomentry.setVar("entryupdated", entryUpdated); |  | ||||||
| 		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 |  | ||||||
| @@ -50,8 +50,7 @@ Response HandlerHistory::handleRequest(const Request &r) | |||||||
| 	std::vector<Revision> resultList; | 	std::vector<Revision> resultList; | ||||||
| 	auto revisionDao = this->database->createRevisionDao(); | 	auto revisionDao = this->database->createRevisionDao(); | ||||||
|  |  | ||||||
| 	auto makeSortedLink = [&](unsigned int limit, unsigned int offset, unsigned int order) | 	auto makeSortedLink = [&](unsigned int limit, unsigned int offset, unsigned int order) { | ||||||
| 	{ |  | ||||||
| 		if(!page.empty()) | 		if(!page.empty()) | ||||||
| 		{ | 		{ | ||||||
| 			return this->urlProvider->pageHistorySort(page, limit, offset, order); | 			return this->urlProvider->pageHistorySort(page, limit, offset, order); | ||||||
| @@ -123,7 +122,7 @@ Response HandlerHistory::handleRequest(const Request &r) | |||||||
| 	return response; | 	return response; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool HandlerHistory::canAccess([[maybe_unused]] const Permissions &perms) | bool HandlerHistory::canAccess(const Permissions &perms) | ||||||
| { | { | ||||||
| 	return true; // This is a lie but we need to this a little more fine grained here, which we do in the handleRequest | 	return true; // This is a lie but we need to this a little more fine grained here, which we do in the handleRequest | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,12 +20,12 @@ SOFTWARE. | |||||||
| */ | */ | ||||||
| #include "handlerinvalidaction.h" | #include "handlerinvalidaction.h" | ||||||
|  |  | ||||||
| Response HandlerInvalidAction::handleRequest([[maybe_unused]] const Request &r) | Response HandlerInvalidAction::handleRequest(const Request &r) | ||||||
| { | { | ||||||
| 	return errorResponse("Invalid action", "No action defined for this action"); | 	return errorResponse("Invalid action", "No action defined for this action"); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool HandlerInvalidAction::canAccess([[maybe_unused]] const Permissions &perms) | bool HandlerInvalidAction::canAccess(const Permissions &perms) | ||||||
| { | { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -55,7 +55,7 @@ Response HandlerLogin::handleRequest(const Request &r) | |||||||
| 	{ | 	{ | ||||||
| 		page = "index"; | 		page = "index"; | ||||||
| 	} | 	} | ||||||
| 	TemplatePage loginTemplatePage = this->templ->getPage("login"); | 	TemplatePage &loginTemplatePage = this->templ->getPage("login"); | ||||||
| 	setGeneralVars(loginTemplatePage); | 	setGeneralVars(loginTemplatePage); | ||||||
| 	loginTemplatePage.setVar("loginurl", urlProvider->login(page)); | 	loginTemplatePage.setVar("loginurl", urlProvider->login(page)); | ||||||
| 	loginTemplatePage.setVar("title", createPageTitle("Login")); | 	loginTemplatePage.setVar("title", createPageTitle("Login")); | ||||||
| @@ -66,7 +66,7 @@ Response HandlerLogin::handleRequest(const Request &r) | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool HandlerLogin::canAccess([[maybe_unused]] const Permissions &perms) | bool HandlerLogin::canAccess(const Permissions &perms) | ||||||
| { | { | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -27,18 +27,7 @@ Response HandlerPage::handle(const Request &r) | |||||||
| 	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)) | ||||||
| @@ -74,9 +63,8 @@ void HandlerPage::setPageVars(TemplatePage &page, std::string pagename) | |||||||
| 	if(!pagename.empty()) | 	if(!pagename.empty()) | ||||||
| 	{ | 	{ | ||||||
| 		std::string headerlinks; | 		std::string headerlinks; | ||||||
| 		TemplatePage headerlink = this->templ->getPage("_headerlink"); | 		TemplatePage &headerlink = this->templ->getPage("_headerlink"); | ||||||
| 		auto addHeaderLink = [&headerlinks, &headerlink](std::string href, std::string value) | 		auto addHeaderLink = [&headerlinks, &headerlink](std::string href, std::string value) { | ||||||
| 		{ |  | ||||||
| 			headerlink.setVar("href", href); | 			headerlink.setVar("href", href); | ||||||
| 			headerlink.setVar("value", value); | 			headerlink.setVar("value", value); | ||||||
| 			headerlinks += headerlink.render(); | 			headerlinks += headerlink.render(); | ||||||
|   | |||||||
| @@ -45,7 +45,6 @@ Response HandlerPageDelete::handleRequest(PageDao &pageDao, std::string pagename | |||||||
| 		{ | 		{ | ||||||
| 			pageDao.deletePage(pagename); | 			pageDao.deletePage(pagename); | ||||||
| 			this->cache->removePrefix("page:"); // TODO: overkill? | 			this->cache->removePrefix("page:"); // TODO: overkill? | ||||||
| 			this->cache->removePrefix("feed:"); |  | ||||||
| 			return Response::redirectTemporarily(this->urlProvider->index()); | 			return Response::redirectTemporarily(this->urlProvider->index()); | ||||||
| 		} | 		} | ||||||
| 		TemplatePage delPage = this->templ->getPage("page_deletion"); | 		TemplatePage delPage = this->templ->getPage("page_deletion"); | ||||||
|   | |||||||
| @@ -21,12 +21,11 @@ SOFTWARE. | |||||||
| #include "handlerpageedit.h" | #include "handlerpageedit.h" | ||||||
| #include "../database/exceptions.h" | #include "../database/exceptions.h" | ||||||
| #include "../request.h" | #include "../request.h" | ||||||
|  | #include "../parserlegacy.h" | ||||||
|  |  | ||||||
| #include "../parser.h" | bool HandlerPageEdit::canAccess(std::string page) | ||||||
| #include "../revisionrenderer.h" |  | ||||||
| bool HandlerPageEdit::canAccess([[maybe_unused]] std::string page) |  | ||||||
| { | { | ||||||
| 	return effectivePermissions(page).canEdit(); | 	return this->userSession->user.permissions.canEdit(); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool HandlerPageEdit::pageMustExist() | bool HandlerPageEdit::pageMustExist() | ||||||
| @@ -42,7 +41,7 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 		return errorResponse("No permission", "You don't have permission to create new pages"); | 		return errorResponse("No permission", "You don't have permission to create new pages"); | ||||||
| 	} | 	} | ||||||
| 	auto revisiondao = this->database->createRevisionDao(); | 	auto revisiondao = this->database->createRevisionDao(); | ||||||
| 	auto revision = revisiondao->getCurrentForPage(pagename); | 	auto revision = this->database->createRevisionDao()->getCurrentForPage(pagename); | ||||||
| 	std::string body; | 	std::string body; | ||||||
|  |  | ||||||
| 	unsigned int current_revision = 0; | 	unsigned int current_revision = 0; | ||||||
| @@ -51,15 +50,6 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 		body = revision->content; | 		body = revision->content; | ||||||
| 		current_revision = revision->revision; | 		current_revision = revision->revision; | ||||||
| 	} | 	} | ||||||
| 	std::string from = r.get("frompage"); |  | ||||||
| 	if(!from.empty()) |  | ||||||
| 	{ |  | ||||||
| 		if(!effectivePermissions(from).canRead()) |  | ||||||
| 		{ |  | ||||||
| 			return this->errorResponse("Permission denied", "No access permissions, so you can't use this page as a template"); |  | ||||||
| 		} |  | ||||||
| 		body = revisiondao->getCurrentForPage(from)->content; |  | ||||||
| 	} |  | ||||||
| 	if(r.getRequestMethod() == "POST") | 	if(r.getRequestMethod() == "POST") | ||||||
| 	{ | 	{ | ||||||
| 		if(r.post("do") == "submit") | 		if(r.post("do") == "submit") | ||||||
| @@ -69,14 +59,13 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
|  |  | ||||||
| 			// TODO: must check, whether categories differ, and perhaps don't allow every user | 			// TODO: must check, whether categories differ, and perhaps don't allow every user | ||||||
| 			// to set categories | 			// to set categories | ||||||
| 			Parser parser; | 			ParserLegacy parser; | ||||||
| 			std::vector<std::string> cats = parser.extractCategories(newContent); | 			std::vector<std::string> cats = parser.extractCategories(newContent); | ||||||
| 			try | 			try | ||||||
| 			{ | 			{ | ||||||
| 				this->database->beginTransaction(); | 				this->database->beginTransaction(); | ||||||
| 				std::string visiblecmd = parser.extractCommand("visible", newContent); | 				std::string visiblecmd = parser.extractCommand("visible", newContent); | ||||||
| 				std::string rename = parser.extractCommand("rename", newContent); | 				std::string rename = parser.extractCommand("rename", newContent); | ||||||
| 				std::string customtitle = parser.extractCommand("pagetitle", newContent); |  | ||||||
| 				Page page; | 				Page page; | ||||||
| 				std::optional<Page> currentPage = pageDao.find(pagename); | 				std::optional<Page> currentPage = pageDao.find(pagename); | ||||||
| 				if(currentPage) | 				if(currentPage) | ||||||
| @@ -94,11 +83,6 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 				page.current_revision = current_revision; | 				page.current_revision = current_revision; | ||||||
| 				page.listed = !(visiblecmd == "0"); | 				page.listed = !(visiblecmd == "0"); | ||||||
| 				page.name = pagename; | 				page.name = pagename; | ||||||
| 				page.title = customtitle; |  | ||||||
| 				if(page.title.empty()) |  | ||||||
| 				{ |  | ||||||
| 					page.title = page.name; |  | ||||||
| 				} |  | ||||||
| 				pageDao.save(page); | 				pageDao.save(page); | ||||||
|  |  | ||||||
| 				Revision newRevision; | 				Revision newRevision; | ||||||
| @@ -111,7 +95,6 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 				pageDao.setCategories(pagename, cats); | 				pageDao.setCategories(pagename, cats); | ||||||
| 				this->database->commitTransaction(); | 				this->database->commitTransaction(); | ||||||
| 				this->cache->removePrefix("page:"); // TODO: overkill? | 				this->cache->removePrefix("page:"); // TODO: overkill? | ||||||
| 				this->cache->removePrefix("feed:"); |  | ||||||
| 			} | 			} | ||||||
| 			catch(const DatabaseException &e) | 			catch(const DatabaseException &e) | ||||||
| 			{ | 			{ | ||||||
| @@ -124,17 +107,13 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 		if(r.post("do") == "preview") | 		if(r.post("do") == "preview") | ||||||
| 		{ | 		{ | ||||||
| 			std::string newContent = r.post("content"); | 			std::string newContent = r.post("content"); | ||||||
| 			Parser parser; | 			ParserLegacy parser; | ||||||
| 			std::string title = parser.extractCommand("pagetitle", newContent); |  | ||||||
| 			TemplatePage templatePage = this->templ->getPage("page_creation_preview"); | 			TemplatePage templatePage = this->templ->getPage("page_creation_preview"); | ||||||
| 			templatePage.setVar("actionurl", urlProvider->editPage(pagename)); | 			templatePage.setVar("actionurl", urlProvider->editPage(pagename)); | ||||||
|  | 			templatePage.setVar("preview_content", parser.parse(pageDao, *this->urlProvider, newContent)); | ||||||
| 			RevisionRenderer revisionRenderer { *this->templ, *this->database, *this->urlProvider }; |  | ||||||
|  |  | ||||||
| 			templatePage.setVar("preview_content", revisionRenderer.renderContent(newContent)); |  | ||||||
| 			templatePage.setVar("content", newContent); | 			templatePage.setVar("content", newContent); | ||||||
| 			setPageVars(templatePage, pagename); | 			setPageVars(templatePage, pagename); | ||||||
| 			templatePage.setVar("title", createPageTitle("Preview: " + title)); | 			templatePage.setVar("title", createPageTitle("Preview: " + pagename)); | ||||||
| 			templatePage.setVar("comment", r.post("comment")); | 			templatePage.setVar("comment", r.post("comment")); | ||||||
| 			Response response; | 			Response response; | ||||||
| 			response.setBody(templatePage.render()); | 			response.setBody(templatePage.render()); | ||||||
| @@ -142,7 +121,7 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	TemplatePage templatePage = this->templ->getPage("page_creation"); | 	TemplatePage &templatePage = this->templ->getPage("page_creation"); | ||||||
| 	templatePage.setVar("actionurl", urlProvider->editPage(pagename)); | 	templatePage.setVar("actionurl", urlProvider->editPage(pagename)); | ||||||
| 	templatePage.setVar("content", body); | 	templatePage.setVar("content", body); | ||||||
| 	setPageVars(templatePage, pagename); | 	setPageVars(templatePage, pagename); | ||||||
|   | |||||||
| @@ -21,13 +21,8 @@ SOFTWARE. | |||||||
| #include "handlerpageview.h" | #include "handlerpageview.h" | ||||||
| #include "../database/exceptions.h" | #include "../database/exceptions.h" | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "../parser.h" | #include "../parserlegacy.h" | ||||||
| #include "../htmllink.h" | #include "../htmllink.h" | ||||||
| #include "../dynamic/dynamiccontentpostlist.h" |  | ||||||
| #include "../dynamic/dynamiccontentincludepage.h" |  | ||||||
| #include "../dynamic/dynamiccontentsetvar.h" |  | ||||||
| #include "../dynamic/dynamiccontentgetvar.h" |  | ||||||
| #include "../revisionrenderer.h" |  | ||||||
|  |  | ||||||
| bool HandlerPageView::canAccess(std::string page) | bool HandlerPageView::canAccess(std::string page) | ||||||
| { | { | ||||||
| @@ -91,20 +86,19 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
|  |  | ||||||
| 	std::optional<Revision> revision; | 	std::optional<Revision> revision; | ||||||
| 	std::string templatepartname; | 	std::string templatepartname; | ||||||
| 	auto revisionDao = this->database->createRevisionDao(); |  | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		if(revisionid > 0) | 		if(revisionid > 0) | ||||||
| 		{ | 		{ | ||||||
| 			if(!effectivePermissions(pagename).canSeePageHistory()) | 			if(!effectivePermissions(pagename).canSeePageHistory()) | ||||||
| 			{ | 			{ | ||||||
| 				auto current = revisionDao->getCurrentForPage(pagename); | 				auto current = this->database->createRevisionDao()->getCurrentForPage(pagename); | ||||||
| 				if(current && current->revision > revisionid) | 				if(current && current->revision > revisionid) | ||||||
| 				{ | 				{ | ||||||
| 					return errorResponse("Error", "You are not allowed to view older revisions of this page"); | 					return errorResponse("Error", "You are not allowed to view older revisions of this page"); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			revision = revisionDao->getRevisionForPage(pagename, revisionid); | 			revision = this->database->createRevisionDao()->getRevisionForPage(pagename, revisionid); | ||||||
| 			if(!revision) | 			if(!revision) | ||||||
| 			{ | 			{ | ||||||
| 				return errorResponse("Revision not found", "No such revision found"); | 				return errorResponse("Revision not found", "No such revision found"); | ||||||
| @@ -124,7 +118,7 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 					return r; | 					return r; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			revision = revisionDao->getCurrentForPage(pagename); | 			revision = this->database->createRevisionDao()->getCurrentForPage(pagename); | ||||||
| 			templatepartname = "page_view"; | 			templatepartname = "page_view"; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -134,31 +128,52 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename, | |||||||
| 		return errorResponse("Database error", "While trying to fetch revision, a database error occured"); | 		return errorResponse("Database error", "While trying to fetch revision, a database error occured"); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	Parser parser; | 	TemplatePage &page = this->templ->getPage(templatepartname); | ||||||
|  |  | ||||||
|  |  | ||||||
| 	Response result; | 	Response result; | ||||||
| 	result.setStatus(200); | 	result.setStatus(200); | ||||||
|  | 	std::string indexcontent; | ||||||
|  | 	std::string parsedcontent; | ||||||
|  |  | ||||||
| 	RevisionRenderer revisionRenderer { *this->templ, *this->database, *this->urlProvider }; | 	if(revisionid > 0) | ||||||
|  | 	{ | ||||||
|  | 		indexcontent = createIndexContent(*parser, revision->content); | ||||||
| 	std::string customtitle = parser.extractCommand("pagetitle", revision->content); | 		parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content); | ||||||
| 	std::string parsedcontent = revisionRenderer.renderContent(revision.value(), customtitle); | 	} | ||||||
| 	/* TODO: Dynamic includes not considered, probably fine in practise */ | 	else | ||||||
| 	std::string indexcontent = createIndexContent(parser, revision->content); | 	{ | ||||||
|  | 		std::string cachekeyindexcontent = "page:indexcontent:" + pagename; | ||||||
|  | 		std::string cachekeyparsedcontent = "page:parsedcontent:" + pagename; | ||||||
|  | 		auto cachedindexcontent = this->cache->get(cachekeyindexcontent); | ||||||
|  | 		auto cachedparsedcontent = this->cache->get(cachekeyparsedcontent); | ||||||
|  | 		if(cachedindexcontent) | ||||||
|  | 		{ | ||||||
|  | 			indexcontent = *cachedindexcontent; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			indexcontent = createIndexContent(*parser, revision->content); | ||||||
|  | 			this->cache->put(cachekeyindexcontent, indexcontent); | ||||||
|  | 		} | ||||||
|  | 		if(cachedparsedcontent) | ||||||
|  | 		{ | ||||||
|  | 			parsedcontent = *cachedparsedcontent; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content); | ||||||
|  | 			this->cache->put(cachekeyparsedcontent, parsedcontent); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	std::string revisionstr = std::to_string(revision->revision); | 	std::string revisionstr = std::to_string(revision->revision); | ||||||
| 	TemplatePage page = this->templ->getPage(templatepartname); |  | ||||||
| 	page.setVar("content", parsedcontent); | 	page.setVar("content", parsedcontent); | ||||||
| 	page.setVar("index", indexcontent); | 	page.setVar("index", indexcontent); | ||||||
| 	page.setVar("editedby", revision->author); | 	page.setVar("editedby", revision->author); | ||||||
| 	page.setVar("editedon", utils::toISODateTime(revision->timestamp)); | 	page.setVar("editedon", utils::toISODate(revision->timestamp)); | ||||||
| 	page.setVar("historyurl", this->urlProvider->pageHistory(pagename)); | 	page.setVar("historyurl", this->urlProvider->pageHistory(pagename)); | ||||||
| 	page.setVar("revision", revisionstr); | 	page.setVar("revision", revisionstr); | ||||||
| 	setPageVars(page, pagename); | 	setPageVars(page, pagename); | ||||||
| 	if(!customtitle.empty()) |  | ||||||
| 	{ |  | ||||||
| 		page.setVar("title", createPageTitle(customtitle)); |  | ||||||
| 	} |  | ||||||
| 	std::string body = page.render(); | 	std::string body = page.render(); | ||||||
| 	if(revisionid == 0 && !this->userSession->loggedIn) | 	if(revisionid == 0 && !this->userSession->loggedIn) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -25,11 +25,7 @@ Response HandlerSearch::handleRequest(const Request &r) | |||||||
| 	std::string q = r.get("q"); | 	std::string q = r.get("q"); | ||||||
| 	if(q.empty()) | 	if(q.empty()) | ||||||
| 	{ | 	{ | ||||||
| 		TemplatePage searchForm = this->templ->getPage("searchform"); | 		return errorResponse("Missing search term", "No search term supplied"); | ||||||
| 		response.setBody(searchForm.render()); |  | ||||||
| 		response.setStatus(200); |  | ||||||
| 		setGeneralVars(searchForm); |  | ||||||
| 		return response; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	auto pageDao = this->database->createPageDao(); | 	auto pageDao = this->database->createPageDao(); | ||||||
| @@ -41,7 +37,7 @@ Response HandlerSearch::handleRequest(const Request &r) | |||||||
| 		{ | 		{ | ||||||
| 			return errorResponse("No results", "Your search for " + q + " did not yield any results."); | 			return errorResponse("No results", "Your search for " + q + " did not yield any results."); | ||||||
| 		} | 		} | ||||||
| 		TemplatePage searchPage = this->templ->getPage("search"); | 		TemplatePage &searchPage = this->templ->getPage("search"); | ||||||
| 		std::string body = this->templ->renderSearch(resultList); | 		std::string body = this->templ->renderSearch(resultList); | ||||||
| 		searchPage.setVar("pagelist", body); | 		searchPage.setVar("pagelist", body); | ||||||
| 		searchPage.setVar("searchterm", q); | 		searchPage.setVar("searchterm", q); | ||||||
|   | |||||||
| @@ -15,20 +15,19 @@ Response HandlerUserSettings::handleRequest(const Request &r) | |||||||
|  |  | ||||||
| 			if(newpassword != newpasswordconfirm) | 			if(newpassword != newpasswordconfirm) | ||||||
| 			{ | 			{ | ||||||
| 				// TODO: is not nice, users has to hit the back button... | 				//TODO: is not nice, users has to hit the back button... | ||||||
| 				return this->errorResponse("Passwords don't match", "The entered new passwords don't match"); | 				return this->errorResponse("Passwords don't match", "The entered new passwords don't match"); | ||||||
| 			} | 			} | ||||||
| 			auto userDao = this->database->createUserDao(); | 			auto userDao = this->database->createUserDao(); | ||||||
| 			Authenticator authenticator(*userDao); | 			Authenticator authenticator(*userDao); | ||||||
|  |  | ||||||
| 			std::variant<User, AuthenticationError> authresult = | 			std::variant<User, AuthenticationError> authresult = authenticator.authenticate(this->userSession->user.login, oldpassword); | ||||||
| 				authenticator.authenticate(this->userSession->user.login, oldpassword); |  | ||||||
| 			if(std::holds_alternative<AuthenticationError>(authresult)) | 			if(std::holds_alternative<AuthenticationError>(authresult)) | ||||||
| 			{ | 			{ | ||||||
| 				return this->errorResponse("Invalid current password", "The old password you entered is invalid"); | 				return this->errorResponse("Invalid current password", "The old password you entered is invalid"); | ||||||
| 			} | 			} | ||||||
| 			Random r; | 			Random r; | ||||||
| 			std::vector<char> salt = r.getRandom(AUTH_DEFAULT_SALT_SIZE); | 			std::vector<char> salt = r.getRandom(23); | ||||||
| 			User user = std::get<User>(authresult); | 			User user = std::get<User>(authresult); | ||||||
| 			user.salt = salt; | 			user.salt = salt; | ||||||
| 			user.password = authenticator.hash(newpassword, user.salt); | 			user.password = authenticator.hash(newpassword, user.salt); | ||||||
| @@ -51,7 +50,7 @@ Response HandlerUserSettings::handleRequest(const Request &r) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	TemplatePage userSettingsPage = this->templ->getPage("usersettings"); | 	TemplatePage &userSettingsPage = this->templ->getPage("usersettings"); | ||||||
| 	setGeneralVars(userSettingsPage); | 	setGeneralVars(userSettingsPage); | ||||||
| 	userSettingsPage.setVar("usersettingsurl", urlProvider->userSettings()); | 	userSettingsPage.setVar("usersettingsurl", urlProvider->userSettings()); | ||||||
| 	userSettingsPage.setVar("title", createPageTitle("User settings - " + this->userSession->user.login)); | 	userSettingsPage.setVar("title", createPageTitle("User settings - " + this->userSession->user.login)); | ||||||
| @@ -62,7 +61,7 @@ Response HandlerUserSettings::handleRequest(const Request &r) | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool HandlerUserSettings::canAccess([[maybe_unused]] const Permissions &perms) | bool HandlerUserSettings::canAccess(const Permissions &perms) | ||||||
| { | { | ||||||
| 	return this->userSession->loggedIn; | 	return this->userSession->loggedIn; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +0,0 @@ | |||||||
| #include "handlerversion.h" |  | ||||||
| #include "../version.h" |  | ||||||
| Response HandlerVersion::handleRequest([[maybe_unused]] const Request &r) |  | ||||||
| { |  | ||||||
| 	Response response; |  | ||||||
| 	response.setContentType("text/plain"); |  | ||||||
| 	response.setBody(get_version_string()); |  | ||||||
| 	return response; |  | ||||||
| } |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| #ifndef HANDLERVERSION_H |  | ||||||
| #define HANDLERVERSION_H |  | ||||||
| #include "handler.h" |  | ||||||
| class HandlerVersion : public Handler |  | ||||||
| { |  | ||||||
|   public: |  | ||||||
| 	using Handler::Handler; |  | ||||||
|  |  | ||||||
|   public: |  | ||||||
| 	Response handleRequest(const Request &r) override; |  | ||||||
|  |  | ||||||
| 	bool canAccess([[maybe_unused]] const Permissions &perms) override |  | ||||||
| 	{ |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // HANDLERVERSION_H |  | ||||||
							
								
								
									
										28
									
								
								iparser.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								iparser.h
									
									
									
									
									
								
							| @@ -2,34 +2,22 @@ | |||||||
| #define IPARSER_H | #define IPARSER_H | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <string_view> | #include <string_view> | ||||||
| #include <functional> |  | ||||||
| #include "headline.h" | #include "headline.h" | ||||||
| #include "database/pagedao.h" | #include "database/pagedao.h" | ||||||
| #include "urlprovider.h" | #include "urlprovider.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| class IParser | class IParser | ||||||
| { | { | ||||||
|   protected: |  | ||||||
| 	static std::string empty(std::string_view key, std::string_view content) |  | ||||||
| 	{ |  | ||||||
| 		return ""; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|   public: |   public: | ||||||
| 	virtual std::string extractCommand(std::string cmdname, const std::string &content) const = 0; | 	virtual std::string extractCommand(std::string cmdname, std::string content) const = 0; | ||||||
| 	virtual std::vector<Headline> extractHeadlines(const std::string &content) const = 0; | 	virtual std::vector<Headline> extractHeadlines(std::string content) const = 0; | ||||||
| 	virtual inline std::string parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content) const | 	virtual std::string parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const = 0; | ||||||
| 	{ | 	virtual std::vector<std::string> extractCategories(std::string content) const = 0; | ||||||
| 		return parse(pagedao, provider, content, empty); |  | ||||||
| 	} |  | ||||||
| 	virtual std::string parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content, |  | ||||||
| 							  const std::function<std::string(std::string_view, std::string_view)> &callback) const = 0; |  | ||||||
|  |  | ||||||
| 	virtual std::string parseDynamics( |  | ||||||
| 		const std::string &content, |  | ||||||
| 		const std::function<std::string(std::string_view, std::string_view)> &callback) const = 0; |  | ||||||
| 	virtual std::vector<std::string> extractCategories(const std::string &content) const = 0; |  | ||||||
|  |  | ||||||
| 	virtual ~IParser(){}; | 	virtual ~IParser(){}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif // PARSER_H | #endif // PARSER_H | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								page.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								page.h
									
									
									
									
									
								
							| @@ -7,7 +7,6 @@ class Page | |||||||
|   public: |   public: | ||||||
| 	Page(); | 	Page(); | ||||||
| 	std::string name; | 	std::string name; | ||||||
| 	std::string title; |  | ||||||
| 	bool listed; | 	bool listed; | ||||||
| 	unsigned int current_revision; | 	unsigned int current_revision; | ||||||
| 	unsigned int pageid; | 	unsigned int pageid; | ||||||
|   | |||||||
| @@ -1,66 +0,0 @@ | |||||||
| #include "pagelistrenderer.h" |  | ||||||
| #include "grouper.h" |  | ||||||
|  |  | ||||||
| PageListRenderer::PageListRenderer(Template &templ, UrlProvider &provider, Database &database) |  | ||||||
| { |  | ||||||
| 	this->templ = &templ; |  | ||||||
| 	this->urlProvider = &provider; |  | ||||||
| 	this->database = &database; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string PageListRenderer::render(const std::vector<Page> &pagelist, std::string type) const |  | ||||||
| { |  | ||||||
| 	TemplatePage pagelistrendergroup = this->templ->loadResolvedPart("pagelistrender_group"); |  | ||||||
| 	TemplatePage pagelistlink = this->templ->loadResolvedPart("pagelistrender_link"); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 	std::function<bool(const std::string &, const std::string &)> lesser = [](const std::string &a, const std::string &b) -> bool { |  | ||||||
| 		return a < b; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	std::function<bool(const std::string &, const std::string &)> greater = [](const std::string &a, const std::string &b) -> bool { |  | ||||||
| 		return a > b; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	using Grouper = Grouper<std::string, Page, std::function<bool(const std::string &,const std::string &)>>; |  | ||||||
|  |  | ||||||
| 	Grouper grouper = (type == "letter") ? Grouper(lesser) : Grouper(greater); |  | ||||||
| 	if(type == "letter") |  | ||||||
| 	{ |  | ||||||
| 		auto az = [&](const Page &p) -> std::string { |  | ||||||
| 			int upper = toupper(p.title[0]); // TODO: this is not unicode safe. |  | ||||||
| 			return { (char)upper }; |  | ||||||
| 		}; |  | ||||||
| 		grouper.group(az, pagelist); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		auto revisionDao = this->database->createRevisionDao(); |  | ||||||
| 		auto creationdate = [&revisionDao](const Page &p) -> std::string { |  | ||||||
| 			return utils::formatLocalDate(revisionDao->getRevisionForPage(p.name, 1).value().timestamp, "%Y-%m"); |  | ||||||
| 		}; |  | ||||||
| 		grouper.group(creationdate, pagelist); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::stringstream stream; |  | ||||||
| 	std::string lastGroup = ""; |  | ||||||
|  |  | ||||||
| 	for(auto &entry : grouper.getResults()) |  | ||||||
| 	{ |  | ||||||
| 		std::string groupname = entry.first; |  | ||||||
| 		const std::vector<const Page*> &pages = entry.second; |  | ||||||
| 		if(lastGroup != groupname) |  | ||||||
| 		{ |  | ||||||
| 			lastGroup = groupname; |  | ||||||
| 			pagelistrendergroup.setVar("groupname", groupname); |  | ||||||
| 			stream << pagelistrendergroup.render(); |  | ||||||
| 		} |  | ||||||
| 		for(const Page *p : pages) |  | ||||||
| 		{ |  | ||||||
| 			pagelistlink.setVar("inner", p->title); |  | ||||||
| 			pagelistlink.setVar("href", this->urlProvider->page(p->name)); |  | ||||||
| 			stream << pagelistlink.render(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return stream.str(); |  | ||||||
| } |  | ||||||
| @@ -1,27 +0,0 @@ | |||||||
| #ifndef PAGELISTRENDERER_H |  | ||||||
| #define PAGELISTRENDERER_H |  | ||||||
| #include "utils.h" |  | ||||||
| #include "page.h" |  | ||||||
| #include "template.h" |  | ||||||
| #include "htmllink.h" |  | ||||||
| #include "urlprovider.h" |  | ||||||
| #include "database/database.h" |  | ||||||
|  |  | ||||||
| class PageListRenderer |  | ||||||
| { |  | ||||||
| private: |  | ||||||
| 	Template *templ; |  | ||||||
| 	UrlProvider *urlProvider; |  | ||||||
| 	Database *database; |  | ||||||
|  |  | ||||||
| public: |  | ||||||
| 	PageListRenderer(Template &templ, UrlProvider &provider, Database &database); |  | ||||||
|  |  | ||||||
| 	std::string render(const std::vector<Page> &pages, std::string type) const; |  | ||||||
|  |  | ||||||
| 	inline static const std::string RENDER_GROUP_BY_LETTER { "letter" }; |  | ||||||
| 	inline static const std::string RENDER_GROUP_BY_CREATIONDATE { "creationdate" }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| }; |  | ||||||
| #endif |  | ||||||
							
								
								
									
										197
									
								
								parser.cpp
									
									
									
									
									
								
							
							
						
						
									
										197
									
								
								parser.cpp
									
									
									
									
									
								
							| @@ -1,197 +0,0 @@ | |||||||
| /* Copyright (c) 2018 Albert S. |  | ||||||
|  |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
|  |  | ||||||
| The above copyright notice and this permission notice shall be included in all |  | ||||||
| copies or substantial portions of the Software. |  | ||||||
|  |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |  | ||||||
| SOFTWARE. |  | ||||||
| */ |  | ||||||
| #include <regex> |  | ||||||
| #include <iostream> |  | ||||||
| #include <regex> |  | ||||||
| #include <vector> |  | ||||||
| #include <algorithm> |  | ||||||
| #include <iterator> |  | ||||||
| #include "parser.h" |  | ||||||
| #include "utils.h" |  | ||||||
| #include "htmllink.h" |  | ||||||
| std::vector<Headline> Parser::extractHeadlines(const std::string &content) const |  | ||||||
| { |  | ||||||
| 	std::vector<Headline> result; |  | ||||||
|  |  | ||||||
| 	std::string reg = R"(\[h(1|2|3)\](\[.*?\])*(.*?)(\[.*?\])*\[\/h\1\])"; |  | ||||||
| 	std::regex headerfinder(reg); |  | ||||||
| 	auto begin = std::sregex_iterator(content.begin(), content.end(), headerfinder); |  | ||||||
| 	auto end = std::sregex_iterator(); |  | ||||||
|  |  | ||||||
| 	for(auto it = begin; it != end; it++) |  | ||||||
| 	{ |  | ||||||
| 		auto smatch = *it; |  | ||||||
| 		Headline h; |  | ||||||
| 		h.level = utils::toUInt(smatch.str(1)); |  | ||||||
| 		h.title = smatch.str(3); |  | ||||||
| 		result.push_back(h); |  | ||||||
| 	} |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::vector<std::string> Parser::extractCategories(const std::string &content) const |  | ||||||
| { |  | ||||||
| 	std::vector<std::string> result; |  | ||||||
| 	std::string reg = R"(\[category\](.*?)\[/category\])"; |  | ||||||
| 	std::regex headerfinder(reg); |  | ||||||
| 	auto begin = std::sregex_iterator(content.begin(), content.end(), headerfinder); |  | ||||||
| 	auto end = std::sregex_iterator(); |  | ||||||
|  |  | ||||||
| 	for(auto it = begin; it != end; it++) |  | ||||||
| 	{ |  | ||||||
| 		auto smatch = *it; |  | ||||||
| 		result.emplace_back(smatch.str(1)); |  | ||||||
| 	} |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string Parser::extractCommand(std::string cmdname, const std::string &content) const |  | ||||||
| { |  | ||||||
| 	std::string cmd = "[cmd:" + cmdname + "]"; |  | ||||||
| 	std::string cmdend = "[/cmd:" + cmdname + "]"; |  | ||||||
|  |  | ||||||
| 	std::string_view view = content; |  | ||||||
| 	size_t pos = 0; |  | ||||||
| 	if((pos = view.find(cmd)) != std::string::npos) |  | ||||||
| 	{ |  | ||||||
| 		view.remove_prefix(pos); |  | ||||||
| 		view.remove_prefix(cmd.size()); |  | ||||||
| 		if((pos = view.find(cmdend)) != std::string::npos) |  | ||||||
| 		{ |  | ||||||
| 			auto result = view.substr(0, pos); |  | ||||||
| 			return std::string{result}; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return ""; |  | ||||||
| } |  | ||||||
| std::string Parser::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const |  | ||||||
| { |  | ||||||
| 	std::string linktag = match.str(1); |  | ||||||
| 	std::string inside = match.str(2); |  | ||||||
|  |  | ||||||
| 	std::vector<std::string> splitted = utils::split(inside, '|'); |  | ||||||
| 	HtmlLink htmllink; |  | ||||||
| 	if(splitted.size() == 2) |  | ||||||
| 	{ |  | ||||||
| 		htmllink.innervalue = splitted[1]; |  | ||||||
| 		htmllink.href = splitted[0]; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		htmllink.innervalue = inside; |  | ||||||
| 		htmllink.href = inside; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if(linktag == "wikilink") |  | ||||||
| 	{ |  | ||||||
| 		if(pageDao.exists(htmllink.href)) |  | ||||||
| 		{ |  | ||||||
| 			htmllink.cssclass = "exists"; |  | ||||||
| 		} |  | ||||||
| 		else |  | ||||||
| 		{ |  | ||||||
| 			htmllink.cssclass = "notexists"; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		htmllink.href = urlProvider.page(htmllink.href); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return htmllink.render(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string Parser::processImage(std::smatch &match) const |  | ||||||
| { |  | ||||||
| 	std::string tag = match.str(1); |  | ||||||
| 	std::string inside = match.str(2); |  | ||||||
| 	std::vector<std::string> splitted = utils::split(inside, '|'); |  | ||||||
| 	std::string width; |  | ||||||
| 	std::string height; |  | ||||||
| 	std::string src; |  | ||||||
| 	if(splitted.size() == 3) |  | ||||||
| 	{ |  | ||||||
| 		width = splitted[0]; |  | ||||||
| 		height = splitted[1]; |  | ||||||
| 		src = splitted[2]; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		src = splitted[0]; |  | ||||||
| 	} |  | ||||||
| 	if(!width.empty() && !height.empty()) |  | ||||||
| 	{ |  | ||||||
| 		return "<img src=\"" + src + "\" width=\"" + width + "\" height=\"" + height + "\"/>"; |  | ||||||
| 	} |  | ||||||
| 	return "<img src=\"" + src + "\"/>"; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string Parser::parse(const PageDao &pagedao, UrlProvider &provider, const std::string &content, |  | ||||||
| 						  const std::function<std::string(std::string_view, std::string_view)> &callback) const |  | ||||||
| { |  | ||||||
| 	std::string result; |  | ||||||
| 	// we don't care about commands, but we nevertheless replace them with empty strings |  | ||||||
| 	std::regex tagfinder( |  | ||||||
| 		R"(\[(b|i|u|li||ul|ol|code|blockquote|img|link|wikilink|h\d|cmd:visible|cmd:rename|cmd:redirect|cmd:pagetitle|cmd:allowinclude|category|dynamic:postlist|dynamic:includepage|dynamic:getvar|dynamic:setvar)*?\]((\s|\S)*?)\[/\1])"); |  | ||||||
| 	result = utils::regex_callback_replacer( |  | ||||||
| 		tagfinder, content, |  | ||||||
| 		[&](std::smatch &match) |  | ||||||
| 		{ |  | ||||||
| 			std::string tag = match.str(1); |  | ||||||
| 			std::string content = match.str(2); |  | ||||||
| 			std::string justreplace[] = {"b", "i", "u", "ul", "li", "ol"}; |  | ||||||
| 			if(tag != "code" && tag != "blockquote") |  | ||||||
| 			{ |  | ||||||
| 				content = parse(pagedao, provider, content, callback); |  | ||||||
| 			} |  | ||||||
| 			if(std::find(std::begin(justreplace), std::end(justreplace), tag) != std::end(justreplace)) |  | ||||||
| 			{ |  | ||||||
| 				return "<" + tag + ">" + content + "</" + tag + ">"; |  | ||||||
| 			} |  | ||||||
| 			if(tag == "link" || tag == "wikilink") |  | ||||||
| 			{ |  | ||||||
| 				return this->processLink( |  | ||||||
| 					pagedao, provider, |  | ||||||
| 					match); // TODO: recreate this so we don't check inside the function stuff again |  | ||||||
| 			} |  | ||||||
| 			if(tag == "img") |  | ||||||
| 			{ |  | ||||||
| 				return this->processImage(match); |  | ||||||
| 			} |  | ||||||
| 			if(tag[0] == 'h') |  | ||||||
| 			{ |  | ||||||
| 				return "<" + tag + " id='" + content + "'>" + content + "</" + tag + ">"; |  | ||||||
| 			} |  | ||||||
| 			if(tag == "code" || tag == "blockquote") |  | ||||||
| 			{ |  | ||||||
| 				return "<pre><" + tag + ">"+ utils::strreplace(content, "\r\n", "\n") + "</"+tag+"></pre>"; |  | ||||||
| 			} |  | ||||||
| 			return callback(tag, content); |  | ||||||
| 		}); |  | ||||||
| 	result = utils::strreplace(result, "\r\n", "<br>"); |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string Parser::parseDynamics(const std::string &content, |  | ||||||
| 								  const std::function<std::string(std::string_view, std::string_view)> &callback) const |  | ||||||
| { |  | ||||||
| 	std::regex tagfinder(R"(\[(dynamic:\w+)*?\]((\s|\S)*?)\[/\1])"); |  | ||||||
| 	return utils::regex_callback_replacer(tagfinder, content, |  | ||||||
| 										  [&](std::smatch &match) { return callback(match.str(1), match.str(2)); }); |  | ||||||
| } |  | ||||||
							
								
								
									
										26
									
								
								parser.h
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								parser.h
									
									
									
									
									
								
							| @@ -1,26 +0,0 @@ | |||||||
| #ifndef PARSER_H |  | ||||||
| #define PARSER_H |  | ||||||
| #include "iparser.h" |  | ||||||
|  |  | ||||||
| class Parser : public IParser |  | ||||||
| { |  | ||||||
|   private: |  | ||||||
| 	std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const; |  | ||||||
| 	std::string processImage(std::smatch &match) const; |  | ||||||
|  |  | ||||||
|   public: |  | ||||||
| 	std::string extractCommand(std::string cmdname, const std::string &content) const; |  | ||||||
| 	std::vector<Headline> extractHeadlines(const std::string &content) const override; |  | ||||||
| 	std::vector<std::string> extractCategories(const std::string &content) const override; |  | ||||||
| 	using IParser::parse; |  | ||||||
| 	virtual std::string parse( |  | ||||||
| 		const PageDao &pagedao, UrlProvider &provider, const std::string &content, |  | ||||||
| 		const std::function<std::string(std::string_view, std::string_view)> &callback) const override; |  | ||||||
| 	std::string parseDynamics( |  | ||||||
| 		const std::string &content, |  | ||||||
| 		const std::function<std::string(std::string_view, std::string_view)> &callback) const override; |  | ||||||
|  |  | ||||||
| 	using IParser::IParser; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // PARSER_H |  | ||||||
							
								
								
									
										139
									
								
								parserlegacy.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								parserlegacy.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  | /* Copyright (c) 2018 Albert S. | ||||||
|  |  | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
|  | */ | ||||||
|  | #include <regex> | ||||||
|  | #include <iostream> | ||||||
|  | #include <regex> | ||||||
|  | #include <vector> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <iterator> | ||||||
|  | #include "parserlegacy.h" | ||||||
|  | #include "utils.h" | ||||||
|  | #include "htmllink.h" | ||||||
|  | std::vector<Headline> ParserLegacy::extractHeadlines(std::string content) const | ||||||
|  | { | ||||||
|  | 	std::vector<Headline> result; | ||||||
|  | 	utils::regex_callback_extractor(std::regex(R"(\[h(1|2|3)\](.*?)\[/h\1\])"), content, [&](std::smatch &smatch) { | ||||||
|  | 		Headline h; | ||||||
|  | 		h.level = utils::toUInt(smatch.str(1)); | ||||||
|  | 		h.title = smatch.str(2); | ||||||
|  | 		result.push_back(h); | ||||||
|  | 	}); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<std::string> ParserLegacy::extractCategories(std::string content) const | ||||||
|  | { | ||||||
|  | 	std::vector<std::string> result; | ||||||
|  | 	std::string reg = R"(\[category\](.*?)\[/category\])"; | ||||||
|  | 	std::regex headerfinder(reg); | ||||||
|  | 	auto begin = std::sregex_iterator(content.begin(), content.end(), headerfinder); | ||||||
|  | 	auto end = std::sregex_iterator(); | ||||||
|  |  | ||||||
|  | 	for(auto it = begin; it != end; it++) | ||||||
|  | 	{ | ||||||
|  | 		auto smatch = *it; | ||||||
|  | 		result.emplace_back(smatch.str(1)); | ||||||
|  | 	} | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string ParserLegacy::extractCommand(std::string cmdname, std::string content) const | ||||||
|  | { | ||||||
|  | 	std::string cmd = "[cmd:" + cmdname + "]"; | ||||||
|  | 	std::string cmdend = "[/cmd:" + cmdname + "]"; | ||||||
|  |  | ||||||
|  | 	std::string_view view = content; | ||||||
|  | 	size_t pos = 0; | ||||||
|  | 	if((pos = view.find(cmd)) != std::string::npos) | ||||||
|  | 	{ | ||||||
|  | 		view.remove_prefix(pos); | ||||||
|  | 		view.remove_prefix(cmd.size()); | ||||||
|  | 		if((pos = view.find(cmdend)) != std::string::npos) | ||||||
|  | 		{ | ||||||
|  | 			auto result = view.substr(0, pos); | ||||||
|  | 			return std::string{result}; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return ""; | ||||||
|  | } | ||||||
|  | std::string ParserLegacy::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const | ||||||
|  | { | ||||||
|  | 	std::string linktag = match.str(1); | ||||||
|  | 	std::string inside = match.str(2); | ||||||
|  |  | ||||||
|  | 	std::vector<std::string> splitted = utils::split(inside, '|'); | ||||||
|  | 	HtmlLink htmllink; | ||||||
|  | 	if(splitted.size() == 2) | ||||||
|  | 	{ | ||||||
|  | 		htmllink.innervalue = splitted[1]; | ||||||
|  | 		htmllink.href = splitted[0]; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		htmllink.innervalue = inside; | ||||||
|  | 		htmllink.href = inside; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if(linktag == "wikilink") | ||||||
|  | 	{ | ||||||
|  | 		if(pageDao.exists(htmllink.href)) | ||||||
|  | 		{ | ||||||
|  | 			htmllink.cssclass = "exists"; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			htmllink.cssclass = "notexists"; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		htmllink.href = urlProvider.page(htmllink.href); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return htmllink.render(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string ParserLegacy::parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const | ||||||
|  | { | ||||||
|  | 	std::string result; | ||||||
|  | 	// we don't care about commands, but we nevertheless replace them with empty strings | ||||||
|  | 	std::regex tagfinder(R"(\[(b|i|u|li||ul|ol|link|wikilink|h\d|cmd:rename|cmd:redirect|category)*?\]((\s|\S)*?)\[/\1])"); | ||||||
|  | 	result = utils::regex_callback_replacer(tagfinder, content, [&](std::smatch &match) { | ||||||
|  | 		std::string tag = match.str(1); | ||||||
|  | 		std::string content = match.str(2); | ||||||
|  | 		std::string justreplace[] = {"b", "i", "u", "ul", "li", "ol"}; | ||||||
|  | 		content = parse(pagedao, provider, content); | ||||||
|  | 		if(std::find(std::begin(justreplace), std::end(justreplace), tag) != std::end(justreplace)) | ||||||
|  | 		{ | ||||||
|  | 			return "<" + tag + ">" + content + "</" + tag + ">"; | ||||||
|  | 		} | ||||||
|  | 		if(tag == "link" || tag == "wikilink") | ||||||
|  | 		{ | ||||||
|  | 			return this->processLink(pagedao, provider, | ||||||
|  | 									 match); // TODO: recreate this so we don't check inside the function stuff again | ||||||
|  | 		} | ||||||
|  | 		if(tag[0] == 'h') | ||||||
|  | 		{ | ||||||
|  | 			return "<" + tag + " id='" + content + "'>" + content + "</" + tag + ">"; | ||||||
|  | 		} | ||||||
|  | 		return std::string(""); | ||||||
|  | 	}); | ||||||
|  | 	result = utils::strreplace(result, "\r\n", "<br>"); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								parserlegacy.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								parserlegacy.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | #ifndef PARSER_H | ||||||
|  | #define PARSER_H | ||||||
|  | #include "iparser.h" | ||||||
|  |  | ||||||
|  | class ParserLegacy : public IParser | ||||||
|  | { | ||||||
|  |   private: | ||||||
|  | 	std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const; | ||||||
|  |  | ||||||
|  |   public: | ||||||
|  | 	std::string extractCommand(std::string cmdname, std::string content) const; | ||||||
|  | 	std::vector<Headline> extractHeadlines(std::string content) const override; | ||||||
|  | 	std::vector<std::string> extractCategories(std::string content) const override; | ||||||
|  | 	std::string parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const override; | ||||||
|  | 	using IParser::IParser; | ||||||
|  | 	~ParserLegacy(){}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // PARSER_H | ||||||
							
								
								
									
										71
									
								
								parsermarkdown.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								parsermarkdown.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | #include "parsermarkdown.h" | ||||||
|  | #include "logger.h" | ||||||
|  | #include "htmllink.h" | ||||||
|  |  | ||||||
|  | std::string ParserMarkdown::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const | ||||||
|  | { | ||||||
|  | 	std::string inner = match.str(1); | ||||||
|  | 	std::string link = match.str(2); | ||||||
|  | 	HtmlLink htmllink; | ||||||
|  | 	htmllink.href = link; | ||||||
|  | 	htmllink.innervalue = inner; | ||||||
|  |  | ||||||
|  | 	if(link.find("http://") == 0 || link.find("https://") == 0) | ||||||
|  | 	{ | ||||||
|  | 		return htmllink.render(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if(pageDao.exists(link)) | ||||||
|  | 	{ | ||||||
|  | 		htmllink.cssclass = "exists"; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		htmllink.cssclass = "notexists"; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	htmllink.href = urlProvider.page(htmllink.href); | ||||||
|  |  | ||||||
|  | 	return htmllink.render(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ParserMarkdown::ParserMarkdown() | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<Headline> ParserMarkdown::extractHeadlines(std::string content) const | ||||||
|  | { | ||||||
|  | 	std::vector<Headline> result; | ||||||
|  | 	utils::regex_callback_extractor(std::regex(R"((#{1,6}) (.*))"), content, [&](std::smatch &smatch) { | ||||||
|  | 		Headline h; | ||||||
|  | 		h.level = smatch.str(1).length(); | ||||||
|  | 		h.title = smatch.str(2); | ||||||
|  | 		result.push_back(h); | ||||||
|  | 	}); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string ParserMarkdown::parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const | ||||||
|  | { | ||||||
|  | 	std::shared_ptr<maddy::ParserConfig> config = std::make_shared<maddy::ParserConfig>(); | ||||||
|  | 	auto maddy = std::make_shared<maddy::Parser>(config); | ||||||
|  |  | ||||||
|  | 	auto linkParser = std::make_shared<maddy::LinkParser>(); | ||||||
|  | 	linkParser->setCallback([&](std::smatch &match) { return processLink(pagedao, provider, match); }); | ||||||
|  | 	maddy->setLinkParser(linkParser); | ||||||
|  | 	// TODO: hack because the parser breaks if there is an \r | ||||||
|  | 	content = utils::strreplace(content, "\r", ""); | ||||||
|  | 	std::stringstream s{content}; | ||||||
|  | 	std::string result = maddy->Parse(s); | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string ParserMarkdown::extractCommand(std::string cmdname, std::string content) const | ||||||
|  | { | ||||||
|  | 	return ""; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<std::string> ParserMarkdown::extractCategories(std::string content) const | ||||||
|  | { | ||||||
|  | 	return { }; | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								parsermarkdown.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								parsermarkdown.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | #ifndef PARSER_MARKDOWN_H | ||||||
|  | #define PARSER_MARKDOWN_H | ||||||
|  |  | ||||||
|  | #include "iparser.h" | ||||||
|  | #include "maddy/parser.h" | ||||||
|  |  | ||||||
|  | class ParserMarkdown : public IParser | ||||||
|  | { | ||||||
|  | private: | ||||||
|  | 	std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	ParserMarkdown(); | ||||||
|  | 	std::vector<Headline> extractHeadlines(std::string content) const; | ||||||
|  | 	std::string parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const; | ||||||
|  |  | ||||||
|  | 	std::string extractCommand(std::string cmdname, std::string content) const; | ||||||
|  | 	std::vector<std::string> extractCategories(std::string content) const; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif // PARSER_MARKDOWN_H | ||||||
| @@ -20,17 +20,6 @@ SOFTWARE. | |||||||
| */ | */ | ||||||
| #include "permissions.h" | #include "permissions.h" | ||||||
|  |  | ||||||
| static const std::map<std::string, int> permmap = {{"can_read", PERM_CAN_READ}, |  | ||||||
| 												   {"can_edit", PERM_CAN_EDIT}, |  | ||||||
| 												   {"can_page_history", PERM_CAN_PAGE_HISTORY}, |  | ||||||
| 												   {"can_global_history", PERM_CAN_GLOBAL_HISTORY}, |  | ||||||
| 												   {"can_delete", PERM_CAN_DELETE}, |  | ||||||
| 												   {"can_see_page_list", PERM_CAN_SEE_PAGE_LIST}, |  | ||||||
| 												   {"can_create", PERM_CAN_CREATE}, |  | ||||||
| 												   {"can_see_category_list", PERM_CAN_SEE_CATEGORY_LIST}, |  | ||||||
| 												   {"can_see_links_here", PERM_CAN_SEE_LINKS_HERE}, |  | ||||||
| 												   {"can_search", PERM_CAN_SEARCH}}; |  | ||||||
|  |  | ||||||
| Permissions::Permissions(int permissions) | Permissions::Permissions(int permissions) | ||||||
| { | { | ||||||
| 	this->permissions = permissions; | 	this->permissions = permissions; | ||||||
| @@ -47,20 +36,3 @@ Permissions::Permissions(const std::string &str) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string Permissions::toString(int perms) |  | ||||||
| { |  | ||||||
| 	std::string result; |  | ||||||
| 	for(auto pair : permmap) |  | ||||||
| 	{ |  | ||||||
| 		if(pair.second & perms) |  | ||||||
| 		{ |  | ||||||
| 			result += pair.first + ","; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if(result.size() > 0) |  | ||||||
| 	{ |  | ||||||
| 		result.pop_back(); |  | ||||||
| 	} |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -14,12 +14,20 @@ | |||||||
|  |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <map> | #include <map> | ||||||
|  |  | ||||||
| class Permissions | class Permissions | ||||||
|  |  | ||||||
| { | { | ||||||
|   private: |   private: | ||||||
| 	int permissions = 0; | 	int permissions = 0; | ||||||
|  | 	const std::map<std::string, int> permmap = {{"can_read", PERM_CAN_READ}, | ||||||
|  | 												{"can_edit", PERM_CAN_EDIT}, | ||||||
|  | 												{"can_page_history", PERM_CAN_PAGE_HISTORY}, | ||||||
|  | 												{"can_global_history", PERM_CAN_GLOBAL_HISTORY}, | ||||||
|  | 												{"can_delete", PERM_CAN_DELETE}, | ||||||
|  | 												{"can_see_page_list", PERM_CAN_SEE_PAGE_LIST}, | ||||||
|  | 												{"can_create", PERM_CAN_CREATE}, | ||||||
|  | 												{"can_see_category_list", PERM_CAN_SEE_CATEGORY_LIST}, | ||||||
|  | 												{"can_see_links_here", PERM_CAN_SEE_LINKS_HERE}, | ||||||
|  | 												{"can_search", PERM_CAN_SEARCH}}; | ||||||
|  |  | ||||||
|   public: |   public: | ||||||
| 	Permissions() | 	Permissions() | ||||||
| @@ -94,13 +102,6 @@ class Permissions | |||||||
| 	{ | 	{ | ||||||
| 		return this->permissions & PERM_CAN_SEE_PAGE_LIST; | 		return this->permissions & PERM_CAN_SEE_PAGE_LIST; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	std::string toString() const |  | ||||||
| 	{ |  | ||||||
| 		return Permissions::toString(this->permissions); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static std::string toString(int perms); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #endif // PERMISSIONS_H | #endif // PERMISSIONS_H | ||||||
|   | |||||||
							
								
								
									
										116
									
								
								qswiki.cpp
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								qswiki.cpp
									
									
									
									
									
								
							| @@ -25,7 +25,6 @@ SOFTWARE. | |||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <filesystem> | #include <filesystem> | ||||||
| #include <getopt.h> |  | ||||||
| #include "gateway/gatewayinterface.h" | #include "gateway/gatewayinterface.h" | ||||||
| #include "gateway/gatewayfactory.h" | #include "gateway/gatewayfactory.h" | ||||||
| #include "handlers/handlerfactory.h" | #include "handlers/handlerfactory.h" | ||||||
| @@ -38,12 +37,11 @@ SOFTWARE. | |||||||
| #include "requestworker.h" | #include "requestworker.h" | ||||||
| #include "cache/fscache.h" | #include "cache/fscache.h" | ||||||
| #include "sandbox/sandboxfactory.h" | #include "sandbox/sandboxfactory.h" | ||||||
| #include "cli.h" | #include "iparser.h" | ||||||
| #include "cliconsole.h" | #include "parserlegacy.h" | ||||||
| #include "cliserver.h" | #include "parsermarkdown.h" | ||||||
| #include "version.h" |  | ||||||
|  |  | ||||||
| void sigterm_handler([[maybe_unused]] int arg) | void sigterm_handler(int arg) | ||||||
| { | { | ||||||
| 	// TODO: proper shutdown. | 	// TODO: proper shutdown. | ||||||
| 	exit(EXIT_SUCCESS); | 	exit(EXIT_SUCCESS); | ||||||
| @@ -62,10 +60,6 @@ void setup_signal_handlers() | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| #define OPT_PRINT_VERSION 23 |  | ||||||
|  |  | ||||||
| static struct option long_options[] = {{"cli", no_argument, 0, 'c'}, {"version", no_argument, 0, OPT_PRINT_VERSION}}; |  | ||||||
|  |  | ||||||
| std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver) | std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver) | ||||||
| { | { | ||||||
|  |  | ||||||
| @@ -74,42 +68,23 @@ std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver) | |||||||
| 	return std::make_unique<FsCache>(path); | 	return std::make_unique<FsCache>(path); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | std::unique_ptr<IParser> createParser(const ConfigVariableResolver &resolver) | ||||||
|  | { | ||||||
|  | 	std::string parser = resolver.getConfig("parser"); | ||||||
|  | 	if(parser == "legacy") | ||||||
|  | 	{ | ||||||
|  | 		return std::make_unique<ParserLegacy>(); | ||||||
|  | 	} | ||||||
|  | 	return std::make_unique<ParserMarkdown>(); | ||||||
|  | } | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
| { | { | ||||||
|  |  | ||||||
| 	char *configfilepath = NULL; |  | ||||||
| 	int option; |  | ||||||
| 	int option_index; |  | ||||||
| 	bool cli_mode = false; |  | ||||||
|  |  | ||||||
| 	if(geteuid() == 0) | 	if(geteuid() == 0) | ||||||
| 	{ | 	{ | ||||||
| 		std::cerr << "Do not run this as root!" << std::endl; | 		std::cerr << "Do not run this as root!" << std::endl; | ||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	while((option = getopt_long(argc, argv, "cv", long_options, &option_index)) != -1) |  | ||||||
| 	{ |  | ||||||
| 		switch(option) |  | ||||||
| 		{ |  | ||||||
| 		case 'c': |  | ||||||
| 			cli_mode = true; |  | ||||||
| 			break; |  | ||||||
| 		case OPT_PRINT_VERSION: |  | ||||||
| 			std::cout << get_version_string() << std::endl; |  | ||||||
| 			exit(EXIT_SUCCESS); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if(optind == argc) |  | ||||||
| 	{ |  | ||||||
| 		std::cerr << "Missing config path" << std::endl; |  | ||||||
| 		return 1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	configfilepath = argv[optind++]; |  | ||||||
|  |  | ||||||
| 	auto sandbox = createSandbox(); | 	auto sandbox = createSandbox(); | ||||||
| 	// TODO: do we want to keep it mandatory or configurable? | 	// TODO: do we want to keep it mandatory or configurable? | ||||||
| 	if(!sandbox->supported()) | 	if(!sandbox->supported()) | ||||||
| @@ -117,18 +92,35 @@ int main(int argc, char **argv) | |||||||
| 		Logger::error() << "Sandbox is not supported, exiting"; | 		Logger::error() << "Sandbox is not supported, exiting"; | ||||||
| 		exit(EXIT_FAILURE); | 		exit(EXIT_FAILURE); | ||||||
| 	} | 	} | ||||||
|  | 	if(!sandbox->enableForInit()) | ||||||
|  | 	{ | ||||||
|  | 		Logger::error() << "Sandboxing for init mode could not be activated."; | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if(argc < 2) | 	if(argc < 2) | ||||||
| 	{ | 	{ | ||||||
| 		std::cerr << "no path to config file provided" << std::endl; | 		std::cerr << "no path to config file provided" << std::endl; | ||||||
| 		return 1; | 		return 1; | ||||||
| 	} | 	} | ||||||
| 	std::string configpath = std::filesystem::absolute(configfilepath).string(); |  | ||||||
|  |  | ||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		ConfigReader configreader(configpath); | 		ConfigReader configreader(argv[1]); | ||||||
| 		Config config = configreader.readConfig(); | 		Config config = configreader.readConfig(); | ||||||
|  |  | ||||||
|  | 		// TODO: config.connectiontring only works as long as we only support sqlite of course | ||||||
|  | 		if(!sandbox->enablePreWorker({ | ||||||
|  | 			   config.configVarResolver.getConfig("cache_fs_dir"), | ||||||
|  | 			   config.templatepath, | ||||||
|  | 			   std::filesystem::path(config.logfile).parent_path(), | ||||||
|  | 			   std::filesystem::path(config.connectionstring).parent_path(), | ||||||
|  | 		   })) | ||||||
|  | 		{ | ||||||
|  | 			Logger::error() << "Sandboxing for pre worker stage could not be activated."; | ||||||
|  | 			exit(EXIT_FAILURE); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		setup_signal_handlers(); | 		setup_signal_handlers(); | ||||||
|  |  | ||||||
| 		std::fstream logstream; | 		std::fstream logstream; | ||||||
| @@ -136,34 +128,6 @@ int main(int argc, char **argv) | |||||||
| 		Logger::setStream(&logstream); | 		Logger::setStream(&logstream); | ||||||
|  |  | ||||||
| 		auto database = createDatabase(config); | 		auto database = createDatabase(config); | ||||||
| 		std::string socketPath = config.configVarResolver.getConfig("socketpath"); |  | ||||||
| 		CLIHandler cliHandler(config, *database); |  | ||||||
|  |  | ||||||
| 		if(cli_mode) |  | ||||||
| 		{ |  | ||||||
| 			CLIConsole console{cliHandler, socketPath}; |  | ||||||
| 			console.startInteractive(); |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// TODO: config.connectiontring only works as long as we only support sqlite of course |  | ||||||
| 		if(!sandbox->enable({ |  | ||||||
| 			   config.configVarResolver.getConfig("cache_fs_dir"), |  | ||||||
| 			   config.templatepath, |  | ||||||
| 			   std::filesystem::path(config.logfile).parent_path(), |  | ||||||
| 			   std::filesystem::path(config.connectionstring).parent_path(), |  | ||||||
| 		   })) |  | ||||||
| 		{ |  | ||||||
| 			Logger::error() << "Sandboxing for worker could not be enabled!"; |  | ||||||
| 			exit(EXIT_FAILURE); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		CLIServer cliServer{cliHandler}; |  | ||||||
| 		if(!cliServer.detachServer(socketPath)) |  | ||||||
| 		{ |  | ||||||
| 			Logger::error() << "Error: Failed to detach unix socket server"; |  | ||||||
| 			return 1; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// TODO: quite ugly, anon-handling must be rethought | 		// TODO: quite ugly, anon-handling must be rethought | ||||||
| 		auto userdao = database->createUserDao(); | 		auto userdao = database->createUserDao(); | ||||||
| @@ -180,19 +144,25 @@ int main(int argc, char **argv) | |||||||
| 		userdao->save(anon.value()); | 		userdao->save(anon.value()); | ||||||
| 		User::setAnon(anon.value()); | 		User::setAnon(anon.value()); | ||||||
|  |  | ||||||
| 		MapCache<TemplatePage> mapCache; | 		Template siteTemplate{config.templateprefix, config.templatepath, config.urls, config.configVarResolver}; | ||||||
| 		Template siteTemplate{config.templateprefix, config.templatepath, config.urls, config.configVarResolver, |  | ||||||
| 							  mapCache}; |  | ||||||
| 		UrlProvider urlProvider{config.urls}; | 		UrlProvider urlProvider{config.urls}; | ||||||
|  |  | ||||||
| 		auto cache = createCache(config.configVarResolver); | 		auto cache = createCache(config.configVarResolver); | ||||||
| 		cache->clear(); | 		cache->clear(); | ||||||
|  |  | ||||||
| 		HandlerFactory handlerFactory{config.handlersConfig, siteTemplate, *database.get(), urlProvider, *cache.get()}; | 		auto parser = createParser(config.configVarResolver); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		HandlerFactory handlerFactory{config.handlersConfig, siteTemplate, *database.get(), urlProvider, *cache.get(), *parser.get()}; | ||||||
| 		RequestWorker requestWorker{handlerFactory, database->createSessionDao(), siteTemplate}; | 		RequestWorker requestWorker{handlerFactory, database->createSessionDao(), siteTemplate}; | ||||||
|  |  | ||||||
| 		auto interface = createGateway(config); | 		auto interface = createGateway(config); | ||||||
|  |  | ||||||
|  | 		if(!sandbox->enableForWorker()) | ||||||
|  | 		{ | ||||||
|  | 			Logger::error() << "Sandboxing for worker could not be enabled!"; | ||||||
|  | 			exit(EXIT_FAILURE); | ||||||
|  | 		} | ||||||
| 		interface->work(requestWorker); | 		interface->work(requestWorker); | ||||||
| 	} | 	} | ||||||
| 	catch(const std::exception &e) | 	catch(const std::exception &e) | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ std::pair<std::string, std::string> Request::createPairFromVar(std::string var) | |||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
| 		std::string key = var.substr(0, equal); | 		std::string key = var.substr(0, equal); | ||||||
| 		std::string val = utils::html_xss(utils::urldecode(var.substr(equal + 1))); | 		std::string val = utils::html_xss(var.substr(equal + 1)); | ||||||
| 		return std::make_pair(std::move(key), std::move(val)); | 		return std::make_pair(std::move(key), std::move(val)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -75,7 +75,7 @@ void Request::initPostMap(const std::string &url) | |||||||
| void Request::initCookies(const std::string &cookiestr) | void Request::initCookies(const std::string &cookiestr) | ||||||
| { | { | ||||||
| 	// TODO: find out what it really should be, ";" or "; "? | 	// TODO: find out what it really should be, ";" or "; "? | ||||||
| 	std::regex regex{";+\\s?"}; | 	std::regex regex { ";+\\s?" }; | ||||||
| 	auto cookiesplitted = utils::split(cookiestr, regex); | 	auto cookiesplitted = utils::split(cookiestr, regex); | ||||||
| 	for(const std::string &part : cookiesplitted) | 	for(const std::string &part : cookiesplitted) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ Response RequestWorker::processRequest(const Request &r) | |||||||
| 		if(session.loggedIn && session.csrf_token != r.post("csrf_token")) | 		if(session.loggedIn && session.csrf_token != r.post("csrf_token")) | ||||||
| 		{ | 		{ | ||||||
| 			// TODO: this is code duplication | 			// TODO: this is code duplication | ||||||
| 			TemplatePage error = this->templ->getPage("error"); | 			TemplatePage &error = this->templ->getPage("error"); | ||||||
| 			error.setVar("errortitle", "Invalid csrf token"); | 			error.setVar("errortitle", "Invalid csrf token"); | ||||||
| 			error.setVar("errormessage", "Invalid csrf token"); | 			error.setVar("errormessage", "Invalid csrf token"); | ||||||
| 			return {403, error.render()}; | 			return {403, error.render()}; | ||||||
|   | |||||||
| @@ -32,12 +32,12 @@ Response::Response(int http_status_code, std::string html) | |||||||
| 	this->html = std::move(html); | 	this->html = std::move(html); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Response::addHeader(std::string key, std::string value) | void Response::addHeader(const std::string &key, const std::string &value) | ||||||
| { | { | ||||||
| 	this->responseHeaders.insert(std::make_pair(key, value)); | 	this->responseHeaders.insert(std::make_pair(key, value)); | ||||||
| } | } | ||||||
|  |  | ||||||
| Response Response::redirectTemporarily(std::string url) | Response Response::redirectTemporarily(const std::string &url) | ||||||
| { | { | ||||||
| 	Response result; | 	Response result; | ||||||
| 	result.addHeader("Location", url); | 	result.addHeader("Location", url); | ||||||
|   | |||||||
| @@ -27,8 +27,8 @@ class Response | |||||||
| 		return this->html; | 		return this->html; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	void addHeader(std::string key, std::string value); | 	void addHeader(const std::string &key, const std::string &value); | ||||||
| 	static Response redirectTemporarily(std::string url); | 	static Response redirectTemporarily(const std::string &url); | ||||||
|  |  | ||||||
| 	void setStatus(int status) | 	void setStatus(int status) | ||||||
| 	{ | 	{ | ||||||
|   | |||||||
| @@ -1,67 +0,0 @@ | |||||||
|  #include "revisionrenderer.h" |  | ||||||
| #include "templatepage.h" |  | ||||||
| #include "dynamic/dynamiccontentpostlist.h" |  | ||||||
| #include "dynamic/dynamiccontentincludepage.h" |  | ||||||
| #include "dynamic/dynamiccontentgetvar.h" |  | ||||||
| #include "dynamic/dynamiccontentsetvar.h" |  | ||||||
| #include "parser.h" |  | ||||||
| #include "htmllink.h" |  | ||||||
|  |  | ||||||
| std::string RevisionRenderer::dynamicCallback(std::string_view key, std::string_view value) |  | ||||||
| { |  | ||||||
| 	if(key == "dynamic:postlist") |  | ||||||
| 	{ |  | ||||||
| 		auto postlist = this->dynamicContentFactory.createDynamicContent<DynamicContentPostList>(); |  | ||||||
| 		postlist->setArgument(std::string(value)); |  | ||||||
| 		return postlist->render(); |  | ||||||
| 	} |  | ||||||
| 	if(key == "dynamic:includepage") |  | ||||||
| 	{ |  | ||||||
| 		auto includePage =  this->dynamicContentFactory.createDynamicContent<DynamicContentIncludePage>(); |  | ||||||
| 		includePage->setArgument(std::string(value)); |  | ||||||
| 		return parser.parseDynamics(includePage->render(), std::bind(&RevisionRenderer::dynamicCallback, this, std::placeholders::_1, std::placeholders::_2)); |  | ||||||
| 	} |  | ||||||
| 	if(key == "dynamic:setvar") |  | ||||||
| 	{ |  | ||||||
| 		auto setVar = this->dynamicContentFactory.createDynamicContent<DynamicContentSetVar>(); |  | ||||||
| 		setVar->setMap(dynamicVarsMap); |  | ||||||
| 		setVar->setArgument(std::string(value)); |  | ||||||
| 		return setVar->render(); |  | ||||||
| 	} |  | ||||||
| 	if(key == "dynamic:getvar") |  | ||||||
| 	{ |  | ||||||
| 		auto getVar = this->dynamicContentFactory.createDynamicContent<DynamicContentGetVar>(); |  | ||||||
| 		getVar->setMap(dynamicVarsMap); |  | ||||||
| 		getVar->setArgument(std::string(value)); |  | ||||||
| 		return getVar->render(); |  | ||||||
| 	} |  | ||||||
| 	return std::string{}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RevisionRenderer::renderContent(std::string content) |  | ||||||
| { |  | ||||||
| 	dynamicVarsMap["pagetitle"] = parser.extractCommand("pagetitle", content); |  | ||||||
| 	dynamicVarsMap["createdon"] = utils::toISODate(time(NULL)); |  | ||||||
|  |  | ||||||
| 	std::string resolvedContent = parser.parseDynamics(content, std::bind(&RevisionRenderer::dynamicCallback, this, std::placeholders::_1, std::placeholders::_2)); |  | ||||||
|  |  | ||||||
| 	return parser.parse(*this->db->createPageDao(), *this->urlProvider, resolvedContent); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string RevisionRenderer::renderContent(const Revision &r, std::string_view customTitle) |  | ||||||
| { |  | ||||||
| 	auto revisionDao = this->db->createRevisionDao(); |  | ||||||
| 	auto firstRevision = revisionDao->getRevisionForPage(r.page, 1); |  | ||||||
| 	if(!firstRevision) |  | ||||||
| 	{ |  | ||||||
|  		throw std::runtime_error("Could not get first revision for page, which is odd. Solar flares?"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 	dynamicVarsMap["createdon"] = utils::toISODate(firstRevision.value().timestamp); |  | ||||||
| 	dynamicVarsMap["pagetitle"] = customTitle; |  | ||||||
|  |  | ||||||
| 	std::string resolvedContent = parser.parseDynamics(r.content, std::bind(&RevisionRenderer::dynamicCallback, this, std::placeholders::_1, std::placeholders::_2)); |  | ||||||
|  |  | ||||||
| 	return parser.parse(*this->db->createPageDao(), *this->urlProvider, resolvedContent); |  | ||||||
| } |  | ||||||
| @@ -1,29 +0,0 @@ | |||||||
| #ifndef REVISIONRENDERER_H |  | ||||||
| #define REVISIONRENDERER_H |  | ||||||
| #include "revision.h" |  | ||||||
| #include "templatepage.h" |  | ||||||
| #include "dynamic/dynamiccontentfactory.h" |  | ||||||
| #include "iparser.h" |  | ||||||
| #include "parser.h" |  | ||||||
| class RevisionRenderer |  | ||||||
| { |  | ||||||
| private: |  | ||||||
| 	DynamicContentFactory dynamicContentFactory; |  | ||||||
| 	Database *db; |  | ||||||
| 	UrlProvider *urlProvider; |  | ||||||
| 	std::map<std::string, std::string> dynamicVarsMap; |  | ||||||
| 	 |  | ||||||
| 	std::string dynamicCallback(std::string_view key, std::string_view value); |  | ||||||
| 	Parser parser; |  | ||||||
| 	 |  | ||||||
| public: |  | ||||||
| 	RevisionRenderer(Template &templ, Database &db, UrlProvider &urlProvider) :dynamicContentFactory(templ, db, urlProvider) |  | ||||||
| 	{ |  | ||||||
| 		this->db = &db; |  | ||||||
| 		this->urlProvider = &urlProvider; |  | ||||||
| 	} |  | ||||||
| 	std::string renderContent(std::string content); |  | ||||||
| 	std::string renderContent(const Revision &r, std::string_view customTitle); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif // REVISIONRENDERER_H |  | ||||||
| @@ -12,13 +12,63 @@ | |||||||
| #include <filesystem> | #include <filesystem> | ||||||
| #include <sys/mount.h> | #include <sys/mount.h> | ||||||
| #include <sys/capability.h> | #include <sys/capability.h> | ||||||
| #include <exile.hpp> | #include <qssb.h> | ||||||
| #include "../logger.h" | #include "../logger.h" | ||||||
| #include "../utils.h" | #include "../utils.h" | ||||||
| #include "../random.h" | #include "../random.h" | ||||||
|  |  | ||||||
| #include "sandbox-linux.h" | #include "sandbox-linux.h" | ||||||
|  |  | ||||||
|  | /* TODO: make a whitelist approach. So far we simply blacklist | ||||||
|  |  * obvious systemcalls. To whitelist, we need to analyse our | ||||||
|  |  * dependencies (http library, sqlite wrapper, sqlite lib etc.) */ | ||||||
|  |  | ||||||
|  | bool SandboxLinux::enableForInit() | ||||||
|  | { | ||||||
|  | 	umask(0027); | ||||||
|  | 	struct qssb_policy policy = {0}; | ||||||
|  | 	int blacklisted_syscalls[] = {QSSB_SYS(execveat), QSSB_SYS(execve), -1}; | ||||||
|  | 	policy.blacklisted_syscalls = blacklisted_syscalls; | ||||||
|  | 	policy.no_new_privs = 1; | ||||||
|  | 	int result = qssb_enable_policy(&policy); | ||||||
|  | 	if(result != 0) | ||||||
|  | 	{ | ||||||
|  | 		Logger::error() << "Failed to install sandboxing policy (init): " << result; | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool SandboxLinux::enablePreWorker(std::vector<std::string> fsPaths) | ||||||
|  | { | ||||||
|  | 	std::sort(fsPaths.begin(), fsPaths.end(), | ||||||
|  | 			  [](const std::string &a, const std::string &b) { return a.length() < b.length(); }); | ||||||
|  |  | ||||||
|  | 	struct qssb_path_policy *policies = new qssb_path_policy[fsPaths.size()]; | ||||||
|  | 	for(unsigned int i = 0; i < fsPaths.size(); i++) | ||||||
|  | 	{ | ||||||
|  | 		policies[i].next = policies + (i + 1); | ||||||
|  | 		policies[i].mountpoint = fsPaths[i].c_str(); | ||||||
|  | 		policies[i].policy = QSSB_MOUNT_ALLOW_READ | QSSB_MOUNT_ALLOW_WRITE; | ||||||
|  | 	} | ||||||
|  | 	policies[fsPaths.size() - 1].next = NULL; | ||||||
|  |  | ||||||
|  | 	struct qssb_policy policy = {0}; | ||||||
|  | 	policy.path_policies = policies; | ||||||
|  | 	policy.namespace_options |= QSSB_UNSHARE_MOUNT; | ||||||
|  | 	policy.namespace_options |= QSSB_UNSHARE_USER; | ||||||
|  | 	int blacklisted_syscalls[] = {QSSB_SYS(execveat), QSSB_SYS(execve), -1}; | ||||||
|  | 	policy.blacklisted_syscalls = blacklisted_syscalls; | ||||||
|  | 	int result = qssb_enable_policy(&policy); | ||||||
|  | 	if(result != 0) | ||||||
|  | 	{ | ||||||
|  | 		Logger::error() << "Failed to install sandboxing policy (preworker): %i" << result; | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 	delete[] policies; | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| bool SandboxLinux::supported() | bool SandboxLinux::supported() | ||||||
| { | { | ||||||
| 	std::fstream stream; | 	std::fstream stream; | ||||||
| @@ -36,32 +86,32 @@ bool SandboxLinux::supported() | |||||||
| 	} | 	} | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| bool SandboxLinux::enable(std::vector<std::string> fsPaths) | bool SandboxLinux::enableForWorker() | ||||||
| { | { | ||||||
| 	std::sort(fsPaths.begin(), fsPaths.end(), | 	struct qssb_policy policy = {0}; | ||||||
| 			  [](const std::string &a, const std::string &b) { return a.length() < b.length(); }); | 	policy.drop_caps = 1; | ||||||
|  | 	policy.not_dumpable = 1; | ||||||
|  | 	policy.no_new_privs = 1; | ||||||
|  |  | ||||||
| 	struct exile_policy *policy = exile_init_policy(); | 	/* TODO: as said, a whitelist approach is better. As such, this list is bound to be incomplete in the | ||||||
| 	if(policy == NULL) | 	 * sense that more could be listed here and some critical ones are probably missing */ | ||||||
|  | 	int blacklisted_syscalls[] = {QSSB_SYS(setuid), | ||||||
|  | 								  QSSB_SYS(connect), | ||||||
|  | 								  QSSB_SYS(chroot), | ||||||
|  | 								  QSSB_SYS(pivot_root), | ||||||
|  | 								  QSSB_SYS(mount), | ||||||
|  | 								  QSSB_SYS(setns), | ||||||
|  | 								  QSSB_SYS(unshare), | ||||||
|  | 								  QSSB_SYS(ptrace), | ||||||
|  | 								  QSSB_SYS(personality), | ||||||
|  | 								  QSSB_SYS(prctl), | ||||||
|  | 								  -1}; | ||||||
|  | 	policy.blacklisted_syscalls = blacklisted_syscalls; | ||||||
|  | 	if(qssb_enable_policy(&policy) != 0) | ||||||
| 	{ | 	{ | ||||||
| 		Logger::error() << "Failed to init sandboxing policy (worker) "; | 		Logger::error() << "Sandbox: Activation of seccomp blacklist failed!"; | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	for(unsigned int i = 0; i < fsPaths.size(); i++) |  | ||||||
| 	{ |  | ||||||
| 		exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ | EXILE_FS_ALLOW_ALL_WRITE, fsPaths[i].c_str()); |  | ||||||
| 	} |  | ||||||
| 	policy->drop_caps = 1; |  | ||||||
| 	policy->not_dumpable = 1; |  | ||||||
| 	policy->no_new_privs = 1; |  | ||||||
| 	policy->mount_path_policies_to_chroot = 1; |  | ||||||
| 	policy->vow_promises = exile_vows_from_str("stdio wpath cpath rpath inet unix thread"); |  | ||||||
| 	if(exile_enable_policy(policy) != 0) |  | ||||||
| 	{ |  | ||||||
| 		Logger::error() << "Sandbox: Activation of exile failed!"; |  | ||||||
| 		exile_free_policy(policy); |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| 	exile_free_policy(policy); |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,8 @@ class SandboxLinux : public Sandbox | |||||||
|   public: |   public: | ||||||
| 	using Sandbox::Sandbox; | 	using Sandbox::Sandbox; | ||||||
| 	bool supported() override; | 	bool supported() override; | ||||||
| 	bool enable(std::vector<std::string> fsPaths) override; | 	bool enableForInit() override; | ||||||
|  | 	bool enablePreWorker(std::vector<std::string> fsPaths) override; | ||||||
|  | 	bool enableForWorker() override; | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -6,6 +6,10 @@ class SandboxOpenBSD : public Sandbox | |||||||
| { | { | ||||||
|   public: |   public: | ||||||
| 	bool supported() override; | 	bool supported() override; | ||||||
| 	bool enable(std::vector<std::string> fsPaths) override; | 	bool enableForInit() override; | ||||||
|  | 	bool enableForWorker() override; | ||||||
|  |  | ||||||
|  |   private: | ||||||
|  | 	bool seccomp_blacklist(std::vector<int> syscalls); | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -10,7 +10,16 @@ class Sandbox | |||||||
| 	/* Whether the platform has everything required to active all sandbnox modes */ | 	/* Whether the platform has everything required to active all sandbnox modes */ | ||||||
| 	virtual bool supported() = 0; | 	virtual bool supported() = 0; | ||||||
|  |  | ||||||
| 	/* Activated after we have acquired resources (bound to ports etc.)*/ | 	/* Activated early. At this point, we need more system calls | ||||||
| 	virtual bool enable(std::vector<std::string> fsPaths) = 0; | 	 * than later on */ | ||||||
|  | 	virtual bool enableForInit() = 0; | ||||||
|  |  | ||||||
|  | 	/* Activated after config has been read. Now we now which paths we need access to */ | ||||||
|  | 	virtual bool enablePreWorker(std::vector<std::string> fsPaths) = 0; | ||||||
|  |  | ||||||
|  | 	/* Activated after we have acquired resources (bound to ports etc.) | ||||||
|  | 	 * | ||||||
|  | 	 * This should allow us to further restrcit the process */ | ||||||
|  | 	virtual bool enableForWorker() = 0; | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| CREATE TABLE page(id INTEGER PRIMARY KEY, name varchar(256), title varchar(1024), lastrevision integer, visible integer DEFAULT 1); | CREATE TABLE page(id INTEGER PRIMARY KEY, name varchar(256), lastrevision integer, visible integer DEFAULT 1); | ||||||
| CREATE TABLE user(id INTEGER PRIMARY KEY,username varchar(64),  | CREATE TABLE user(id INTEGER PRIMARY KEY,username varchar(64),  | ||||||
| password blob, salt blob, permissions integer, enabled integer DEFAULT 1); | password blob, salt blob, permissions integer, enabled integer DEFAULT 1); | ||||||
| CREATE TABLE session(id INTEGER PRIMARY KEY, csrf_token varchar(32),  | CREATE TABLE session(id INTEGER PRIMARY KEY, csrf_token varchar(32),  | ||||||
| @@ -25,13 +25,24 @@ count integer | |||||||
| CREATE TABLE category(id INTEGER PRIMARY KEY, name varchar(255)); | CREATE TABLE category(id INTEGER PRIMARY KEY, name varchar(255)); | ||||||
| CREATE TABLE categorymember(id INTEGER PRIMARY KEY, category REFERENCES category(id), page REFERENCES page (id)); | CREATE TABLE categorymember(id INTEGER PRIMARY KEY, category REFERENCES category(id), page REFERENCES page (id)); | ||||||
| CREATE INDEX revisionid ON revision (revisionid DESC); | CREATE INDEX revisionid ON revision (revisionid DESC); | ||||||
| CREATE INDEX pagename ON page (name); | CREATE INDEX pagename ON page (name) | ||||||
| CREATE INDEX token ON session (token); | ; | ||||||
| CREATE VIRTUAL TABLE search USING fts5(content, page UNINDEXED, content=revision,content_rowid=id); | CREATE INDEX token ON session (token) | ||||||
|  | ; | ||||||
|  | CREATE TRIGGER search_ai AFTER INSERT ON revision BEGIN | ||||||
|  |   DELETE FROM search WHERE page = new.page; | ||||||
|  |   INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page); | ||||||
|  | END; | ||||||
|  | CREATE TRIGGER search_au AFTER UPDATE ON revision BEGIN | ||||||
|  |   DELETE FROM search WHERE page = old.page; | ||||||
|  |   INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page); | ||||||
|  | END; | ||||||
|  | CREATE VIRTUAL TABLE search USING fts5(content, page UNINDEXED, content=revision,content_rowid=id) | ||||||
|  | /* search(content,page) */; | ||||||
|  | CREATE TABLE IF NOT EXISTS 'search_data'(id INTEGER PRIMARY KEY, block BLOB); | ||||||
|  | CREATE TABLE IF NOT EXISTS 'search_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID; | ||||||
|  | CREATE TABLE IF NOT EXISTS 'search_docsize'(id INTEGER PRIMARY KEY, sz BLOB); | ||||||
|  | CREATE TABLE IF NOT EXISTS 'search_config'(k PRIMARY KEY, v) WITHOUT ROWID; | ||||||
| CREATE TRIGGER search_ad AFTER DELETE ON revision BEGIN | CREATE TRIGGER search_ad AFTER DELETE ON revision BEGIN | ||||||
|   INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page); |   INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page); | ||||||
| END; | END; | ||||||
| CREATE TRIGGER search_ai AFTER INSERT ON revision BEGIN |  | ||||||
|   INSERT INTO search(search, rowid, content, page) SELECT 'delete', id, content, page FROM revision WHERE page = new.page AND revisionid = new.revisionid - 1;  |  | ||||||
|   INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page); |  | ||||||
| END; |  | ||||||
|   | |||||||
 Submodule submodules/cpp-httplib updated: c0b461a3b7...63643e6386
									
								
							 Submodule submodules/exile.h deleted from e711a1d53a
									
								
							
							
								
								
									
										1
									
								
								submodules/qsmaddy
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								submodules/qsmaddy
									
									
									
									
									
										Submodule
									
								
							 Submodule submodules/qsmaddy added at 167ce3943d
									
								
							
							
								
								
									
										1
									
								
								submodules/qssb.h
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								submodules/qssb.h
									
									
									
									
									
										Submodule
									
								
							 Submodule submodules/qssb.h added at 9df2e9ee90
									
								
							
							
								
								
									
										39
									
								
								template.cpp
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								template.cpp
									
									
									
									
									
								
							| @@ -24,25 +24,12 @@ SOFTWARE. | |||||||
| #include "htmllink.h" | #include "htmllink.h" | ||||||
| #include "logger.h" | #include "logger.h" | ||||||
| Template::Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls, | Template::Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls, | ||||||
| 				   ConfigVariableResolver &configVarsResolver, MapCache<TemplatePage> &pageCache) | 				   ConfigVariableResolver &configVarsResolver) | ||||||
| { | { | ||||||
| 	this->templateprefix = templateprefix; | 	this->templateprefix = templateprefix; | ||||||
| 	this->templatepath = templatepath; | 	this->templatepath = templatepath; | ||||||
| 	this->configUrls = &configUrls; | 	this->configUrls = &configUrls; | ||||||
| 	this->configVarResolver = &configVarsResolver; | 	this->configVarResolver = &configVarsResolver; | ||||||
| 	this->pageCache = &pageCache; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TemplatePage Template::getPage(const std::string &pagename) |  | ||||||
| { |  | ||||||
| 	auto result = this->pageCache->find(pagename); |  | ||||||
| 	if(result) |  | ||||||
| 	{ |  | ||||||
| 		return *result; |  | ||||||
| 	} |  | ||||||
| 	auto page = createPage(pagename); |  | ||||||
| 	this->pageCache->set(pagename, page); |  | ||||||
| 	return page; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::string Template::getPartPath(std::string_view partname) | std::string Template::getPartPath(std::string_view partname) | ||||||
| @@ -67,7 +54,7 @@ std::string Template::resolveIncludes(std::string_view content) | |||||||
| 	return replacer.parse(content); | 	return replacer.parse(content); | ||||||
| } | } | ||||||
|  |  | ||||||
| TemplatePage Template::createPage(std::string_view name) | TemplatePage Template::createPage(std::string name) | ||||||
| { | { | ||||||
| 	std::string content = loadResolvedPart(name); | 	std::string content = loadResolvedPart(name); | ||||||
| 	Varreplacer replacer(this->templateprefix); | 	Varreplacer replacer(this->templateprefix); | ||||||
| @@ -78,6 +65,16 @@ TemplatePage Template::createPage(std::string_view name) | |||||||
| 	return TemplatePage(replacer.parse(content)); | 	return TemplatePage(replacer.parse(content)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TemplatePage &Template::getPage(const std::string &pagename) | ||||||
|  | { | ||||||
|  | 	if(utils::hasKey(pagesMap, pagename)) | ||||||
|  | 	{ | ||||||
|  | 		return pagesMap[pagename]; | ||||||
|  | 	} | ||||||
|  | 	pagesMap.insert(std::make_pair(pagename, createPage(pagename))); | ||||||
|  | 	return pagesMap[pagename]; | ||||||
|  | } | ||||||
|  |  | ||||||
| // TODO: this restricts template a bit | // TODO: this restricts template a bit | ||||||
| std::string Template::renderSearch(const std::vector<std::string> &results, | std::string Template::renderSearch(const std::vector<std::string> &results, | ||||||
| 								   std::function<std::string(std::string)> callback) const | 								   std::function<std::string(std::string)> callback) const | ||||||
| @@ -133,20 +130,20 @@ std::string Template::renderRevisionList(const std::vector<Revision> &revisions, | |||||||
| 	std::stringstream stream; | 	std::stringstream stream; | ||||||
| 	UrlProvider urlprovider(*this->configUrls); | 	UrlProvider urlprovider(*this->configUrls); | ||||||
|  |  | ||||||
| 	auto genwithoutpage = [&] | 	auto genwithoutpage = [&] { | ||||||
| 	{ |  | ||||||
| 		for(const Revision &revision : revisions) | 		for(const Revision &revision : revisions) | ||||||
| 		{ | 		{ | ||||||
|  |  | ||||||
|  | 			Logger::debug() << "processing: " << revision.revision; | ||||||
| 			stream << "<tr><td><a href=\"" << urlprovider.pageRevision(revision.page, revision.revision) << "\">" | 			stream << "<tr><td><a href=\"" << urlprovider.pageRevision(revision.page, revision.revision) << "\">" | ||||||
| 				   << revision.revision << "</a></td>" | 				   << revision.revision << "</a></td>" | ||||||
| 				   << "<td>" << revision.author << "</td>" | 				   << "<td>" << revision.author << "</td>" | ||||||
| 				   << "<td>" << revision.comment << "</td>" | 				   << "<td>" << revision.comment << "</td>" | ||||||
| 				   << "<td>" << utils::toISODateTime(revision.timestamp) << "</td></tr>"; | 				   << "<td>" << utils::toISODate(revision.timestamp) << "</td></tr>"; | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	auto genwithpage = [&] | 	auto genwithpage = [&] { | ||||||
| 	{ |  | ||||||
| 		for(const Revision &revision : revisions) | 		for(const Revision &revision : revisions) | ||||||
| 		{ | 		{ | ||||||
|  |  | ||||||
| @@ -155,7 +152,7 @@ std::string Template::renderRevisionList(const std::vector<Revision> &revisions, | |||||||
| 				   << "<td>" << revision.revision << "</td>" | 				   << "<td>" << revision.revision << "</td>" | ||||||
| 				   << "<td>" << revision.author << "</td>" | 				   << "<td>" << revision.author << "</td>" | ||||||
| 				   << "<td>" << revision.comment << "</td>" | 				   << "<td>" << revision.comment << "</td>" | ||||||
| 				   << "<td>" << utils::toISODateTime(revision.timestamp) << "</td></tr>"; | 				   << "<td>" << utils::toISODate(revision.timestamp) << "</td></tr>"; | ||||||
| 		} | 		} | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								template.h
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								template.h
									
									
									
									
									
								
							| @@ -8,29 +8,31 @@ | |||||||
| #include "response.h" | #include "response.h" | ||||||
| #include "searchresult.h" | #include "searchresult.h" | ||||||
| #include "revision.h" | #include "revision.h" | ||||||
| #include "cache/mapcache.h" |  | ||||||
| class Template | class Template | ||||||
| { | { | ||||||
|   private: |   private: | ||||||
| 	ConfigVariableResolver *configVarResolver; | 	ConfigVariableResolver *configVarResolver; | ||||||
| 	ConfigUrls *configUrls; | 	ConfigUrls *configUrls; | ||||||
| 	MapCache<TemplatePage> *pageCache; |  | ||||||
|  |  | ||||||
| 	std::string templateprefix; | 	std::string templateprefix; | ||||||
| 	std::string templatepath; | 	std::string templatepath; | ||||||
|  |  | ||||||
|  | 	std::map<std::string, TemplatePage> pagesMap; | ||||||
| 	std::string resolveIncludes(std::string_view content); | 	std::string resolveIncludes(std::string_view content); | ||||||
|  |  | ||||||
| 	std::string getPartPath(std::string_view partname); | 	std::string getPartPath(std::string_view partname); | ||||||
|  | 	std::string loadResolvedPart(std::string_view partname); | ||||||
| 	std::string loadPartContent(std::string_view partname); | 	std::string loadPartContent(std::string_view partname); | ||||||
| 	TemplatePage createPage(std::string_view name); | 	TemplatePage createPage(std::string name); | ||||||
|  |  | ||||||
|   public: |   public: | ||||||
| 	Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls, | 	Template(std::string templateprefix, std::string templatepath, ConfigUrls &configUrls, | ||||||
| 			 ConfigVariableResolver &configVarsResolver, MapCache<TemplatePage> &pageCache); | 			 ConfigVariableResolver &configVarsResolver); | ||||||
|  | 	/* TODO: returning this as a reference is by no means a risk free business, | ||||||
| 	TemplatePage getPage(const std::string &pagename); | 		because between requests, different vars can be set conditionally, | ||||||
| 	std::string loadResolvedPart(std::string_view partname); | 		thus creating a mess | ||||||
|  | 	*/ | ||||||
|  | 	TemplatePage &getPage(const std::string &pagename); | ||||||
|  |  | ||||||
| 	std::string renderSearch(const std::vector<std::string> &results, | 	std::string renderSearch(const std::vector<std::string> &results, | ||||||
| 							 std::function<std::string(std::string)> callback) const; | 							 std::function<std::string(std::string)> callback) const; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| {qswiki:include:general_header} | {qswiki:include:general_header} | ||||||
| <div id="content" style="margin-top: 10px;"> | <div id="content" style="margin-top: 10px;"> | ||||||
| <h2>All pages</h2> | <h2>All pages</h2> | ||||||
| {qswiki:include:pagelistrender} | {qswiki:var:pagelist} | ||||||
| </div> | </div> | ||||||
| {qswiki:include:general_footer} | {qswiki:include:general_footer} | ||||||
| @@ -1 +0,0 @@ | |||||||
| <ul> |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user