Porównaj commity
	
		
			7 Commity
		
	
	
		
			feature/ma
			...
			8d685dc581
		
	
	| Autor | SHA1 | Data | |
|---|---|---|---|
| 8d685dc581 | |||
| ed43f5f700 | |||
| 10f00aeb45 | |||
| 67eb8b6428 | |||
| f26fd19fb4 | |||
| 204a72da1f | |||
| 88816a4015 | 
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -7,6 +7,3 @@
 | 
			
		||||
[submodule "submodules/qssb.h"]
 | 
			
		||||
	path = submodules/qssb.h
 | 
			
		||||
	url = https://gitea.quitesimple.org/crtxcr/qssb.h.git
 | 
			
		||||
[submodule "submodules/qsmaddy"]
 | 
			
		||||
	path = submodules/qsmaddy
 | 
			
		||||
	url = https://gitea.quitesimple.org/crtxcr/qsmaddy
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							@@ -2,8 +2,8 @@
 | 
			
		||||
 | 
			
		||||
CXXFLAGS=-std=c++17 -O0 -g -no-pie -pipe -MMD -Wall -Wextra
 | 
			
		||||
RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra
 | 
			
		||||
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs -lseccomp
 | 
			
		||||
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h -I submodules/qsmaddy/include/
 | 
			
		||||
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs
 | 
			
		||||
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h
 | 
			
		||||
 | 
			
		||||
CXX=g++
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,6 @@ Config::Config(const std::map<std::string, std::string> &map)
 | 
			
		||||
 | 
			
		||||
	this->configmap = map;
 | 
			
		||||
	this->wikipath = optional("wikipath", "/");
 | 
			
		||||
	this->parser = optional("parser", "markdown");
 | 
			
		||||
	this->handlersConfig.anon_username = optional("anon_username", "anonymouse");
 | 
			
		||||
	this->handlersConfig.wikiname = required("wikiname");
 | 
			
		||||
	this->logfile = required("logfile");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								config.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								config.h
									
									
									
									
									
								
							@@ -86,7 +86,6 @@ class Config
 | 
			
		||||
	std::string templateprefix;
 | 
			
		||||
	std::string logfile;
 | 
			
		||||
	std::string connectionstring;
 | 
			
		||||
	std::string parser;
 | 
			
		||||
	int session_max_lifetime;
 | 
			
		||||
	int threadscount;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,13 +9,10 @@
 | 
			
		||||
#include "../database/queryoption.h"
 | 
			
		||||
#include "../logger.h"
 | 
			
		||||
#include "../cache/icache.h"
 | 
			
		||||
#include "../iparser.h"
 | 
			
		||||
 | 
			
		||||
class Handler
 | 
			
		||||
{
 | 
			
		||||
  protected:
 | 
			
		||||
	ICache *cache;
 | 
			
		||||
	IParser *parser;
 | 
			
		||||
	Template *templ;
 | 
			
		||||
	Database *database;
 | 
			
		||||
	Session *userSession;
 | 
			
		||||
@@ -28,7 +25,7 @@ class Handler
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
	Handler(HandlerConfig &handlersConfig, Template &templ, Database &db, Session &userSession, UrlProvider &provider,
 | 
			
		||||
			ICache &cache, IParser &parser)
 | 
			
		||||
			ICache &cache)
 | 
			
		||||
	{
 | 
			
		||||
		this->handlersConfig = &handlersConfig;
 | 
			
		||||
		this->templ = &templ;
 | 
			
		||||
@@ -36,7 +33,6 @@ class Handler
 | 
			
		||||
		this->userSession = &userSession;
 | 
			
		||||
		this->urlProvider = &provider;
 | 
			
		||||
		this->cache = &cache;
 | 
			
		||||
		this->parser = &parser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	virtual Response handle(const Request &r);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,16 +10,15 @@ class HandlerFactory
 | 
			
		||||
	Database &db;
 | 
			
		||||
	UrlProvider &urlProvider;
 | 
			
		||||
	ICache &cache;
 | 
			
		||||
	IParser &parser;
 | 
			
		||||
 | 
			
		||||
	template <class T> inline std::unique_ptr<T> produce(Session &userSession)
 | 
			
		||||
	{
 | 
			
		||||
		return std::make_unique<T>(handlerConfig, templ, db, userSession, urlProvider, cache, parser);
 | 
			
		||||
		return std::make_unique<T>(handlerConfig, templ, db, userSession, urlProvider, cache);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
	HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache, IParser &parser)
 | 
			
		||||
		: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache), parser(parser)
 | 
			
		||||
	HandlerFactory(HandlerConfig &handlerConfig, Template &templ, Database &db, UrlProvider &urlprovider, ICache &cache)
 | 
			
		||||
		: handlerConfig(handlerConfig), templ(templ), db(db), urlProvider(urlprovider), cache(cache)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	std::unique_ptr<Handler> createHandler(const std::string &action, Session &userSession);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ SOFTWARE.
 | 
			
		||||
#include "handlerpageedit.h"
 | 
			
		||||
#include "../database/exceptions.h"
 | 
			
		||||
#include "../request.h"
 | 
			
		||||
#include "../parserlegacy.h"
 | 
			
		||||
 | 
			
		||||
#include "../parser.h"
 | 
			
		||||
bool HandlerPageEdit::canAccess(std::string page)
 | 
			
		||||
{
 | 
			
		||||
	return this->userSession->user.permissions.canEdit();
 | 
			
		||||
@@ -59,7 +59,7 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
 | 
			
		||||
 | 
			
		||||
			// TODO: must check, whether categories differ, and perhaps don't allow every user
 | 
			
		||||
			// to set categories
 | 
			
		||||
			ParserLegacy parser;
 | 
			
		||||
			Parser parser;
 | 
			
		||||
			std::vector<std::string> cats = parser.extractCategories(newContent);
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
@@ -107,7 +107,7 @@ Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename,
 | 
			
		||||
		if(r.post("do") == "preview")
 | 
			
		||||
		{
 | 
			
		||||
			std::string newContent = r.post("content");
 | 
			
		||||
			ParserLegacy parser;
 | 
			
		||||
			Parser parser;
 | 
			
		||||
			TemplatePage templatePage = this->templ->getPage("page_creation_preview");
 | 
			
		||||
			templatePage.setVar("actionurl", urlProvider->editPage(pagename));
 | 
			
		||||
			templatePage.setVar("preview_content", parser.parse(pageDao, *this->urlProvider, newContent));
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ SOFTWARE.
 | 
			
		||||
#include "handlerpageview.h"
 | 
			
		||||
#include "../database/exceptions.h"
 | 
			
		||||
#include "../logger.h"
 | 
			
		||||
#include "../parserlegacy.h"
 | 
			
		||||
#include "../parser.h"
 | 
			
		||||
#include "../htmllink.h"
 | 
			
		||||
 | 
			
		||||
bool HandlerPageView::canAccess(std::string page)
 | 
			
		||||
@@ -130,7 +130,7 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
 | 
			
		||||
 | 
			
		||||
	TemplatePage &page = this->templ->getPage(templatepartname);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	Parser parser;
 | 
			
		||||
	Response result;
 | 
			
		||||
	result.setStatus(200);
 | 
			
		||||
	std::string indexcontent;
 | 
			
		||||
@@ -138,8 +138,8 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
 | 
			
		||||
 | 
			
		||||
	if(revisionid > 0)
 | 
			
		||||
	{
 | 
			
		||||
		indexcontent = createIndexContent(*parser, revision->content);
 | 
			
		||||
		parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content);
 | 
			
		||||
		indexcontent = createIndexContent(parser, revision->content);
 | 
			
		||||
		parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
@@ -153,7 +153,7 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			indexcontent = createIndexContent(*parser, revision->content);
 | 
			
		||||
			indexcontent = createIndexContent(parser, revision->content);
 | 
			
		||||
			this->cache->put(cachekeyindexcontent, indexcontent);
 | 
			
		||||
		}
 | 
			
		||||
		if(cachedparsedcontent)
 | 
			
		||||
@@ -162,7 +162,7 @@ Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename,
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			parsedcontent = parser->parse(pageDao, *this->urlProvider, revision->content);
 | 
			
		||||
			parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content);
 | 
			
		||||
			this->cache->put(cachekeyparsedcontent, parsedcontent);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,6 @@
 | 
			
		||||
#include "headline.h"
 | 
			
		||||
#include "database/pagedao.h"
 | 
			
		||||
#include "urlprovider.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IParser
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
@@ -18,6 +16,4 @@ class IParser
 | 
			
		||||
	virtual ~IParser(){};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // PARSER_H
 | 
			
		||||
 
 | 
			
		||||
@@ -24,22 +24,29 @@ SOFTWARE.
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include "parserlegacy.h"
 | 
			
		||||
#include "parser.h"
 | 
			
		||||
#include "utils.h"
 | 
			
		||||
#include "htmllink.h"
 | 
			
		||||
std::vector<Headline> ParserLegacy::extractHeadlines(std::string content) const
 | 
			
		||||
std::vector<Headline> Parser::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) {
 | 
			
		||||
	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(2);
 | 
			
		||||
		result.push_back(h);
 | 
			
		||||
	});
 | 
			
		||||
	}
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ParserLegacy::extractCategories(std::string content) const
 | 
			
		||||
std::vector<std::string> Parser::extractCategories(std::string content) const
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::string> result;
 | 
			
		||||
	std::string reg = R"(\[category\](.*?)\[/category\])";
 | 
			
		||||
@@ -55,7 +62,7 @@ std::vector<std::string> ParserLegacy::extractCategories(std::string content) co
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ParserLegacy::extractCommand(std::string cmdname, std::string content) const
 | 
			
		||||
std::string Parser::extractCommand(std::string cmdname, std::string content) const
 | 
			
		||||
{
 | 
			
		||||
	std::string cmd = "[cmd:" + cmdname + "]";
 | 
			
		||||
	std::string cmdend = "[/cmd:" + cmdname + "]";
 | 
			
		||||
@@ -74,7 +81,7 @@ std::string ParserLegacy::extractCommand(std::string cmdname, std::string conten
 | 
			
		||||
	}
 | 
			
		||||
	return "";
 | 
			
		||||
}
 | 
			
		||||
std::string ParserLegacy::processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const
 | 
			
		||||
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);
 | 
			
		||||
@@ -109,7 +116,7 @@ std::string ParserLegacy::processLink(const PageDao &pageDao, UrlProvider &urlPr
 | 
			
		||||
	return htmllink.render();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ParserLegacy::parse(const PageDao &pagedao, UrlProvider &provider, std::string content) const
 | 
			
		||||
std::string Parser::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
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
#define PARSER_H
 | 
			
		||||
#include "iparser.h"
 | 
			
		||||
 | 
			
		||||
class ParserLegacy : public IParser
 | 
			
		||||
class Parser : public IParser
 | 
			
		||||
{
 | 
			
		||||
  private:
 | 
			
		||||
	std::string processLink(const PageDao &pageDao, UrlProvider &urlProvider, std::smatch &match) const;
 | 
			
		||||
@@ -13,7 +13,7 @@ class ParserLegacy : public IParser
 | 
			
		||||
	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(){};
 | 
			
		||||
	~Parser(){};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // PARSER_H
 | 
			
		||||
@@ -1,71 +0,0 @@
 | 
			
		||||
#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 { };
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
							
								
								
									
										34
									
								
								qswiki.cpp
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								qswiki.cpp
									
									
									
									
									
								
							@@ -37,10 +37,6 @@ SOFTWARE.
 | 
			
		||||
#include "requestworker.h"
 | 
			
		||||
#include "cache/fscache.h"
 | 
			
		||||
#include "sandbox/sandboxfactory.h"
 | 
			
		||||
#include "iparser.h"
 | 
			
		||||
#include "parserlegacy.h"
 | 
			
		||||
#include "parsermarkdown.h"
 | 
			
		||||
 | 
			
		||||
void sigterm_handler(int arg)
 | 
			
		||||
{
 | 
			
		||||
	// TODO: proper shutdown.
 | 
			
		||||
@@ -67,17 +63,6 @@ std::unique_ptr<ICache> createCache(const ConfigVariableResolver &resolver)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	if(geteuid() == 0)
 | 
			
		||||
@@ -92,21 +77,21 @@ int main(int argc, char **argv)
 | 
			
		||||
		Logger::error() << "Sandbox is not supported, exiting";
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
	if(argc < 2)
 | 
			
		||||
	{
 | 
			
		||||
		std::cerr << "no path to config file provided" << std::endl;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
	std::string configpath = std::filesystem::absolute(argv[1]).string();
 | 
			
		||||
	if(!sandbox->enableForInit())
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Sandboxing for init mode could not be activated.";
 | 
			
		||||
		exit(EXIT_FAILURE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(argc < 2)
 | 
			
		||||
	{
 | 
			
		||||
		std::cerr << "no path to config file provided" << std::endl;
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		ConfigReader configreader(argv[1]);
 | 
			
		||||
		ConfigReader configreader(configpath);
 | 
			
		||||
		Config config = configreader.readConfig();
 | 
			
		||||
 | 
			
		||||
		// TODO: config.connectiontring only works as long as we only support sqlite of course
 | 
			
		||||
@@ -150,10 +135,7 @@ int main(int argc, char **argv)
 | 
			
		||||
		auto cache = createCache(config.configVarResolver);
 | 
			
		||||
		cache->clear();
 | 
			
		||||
 | 
			
		||||
		auto parser = createParser(config.configVarResolver);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		HandlerFactory handlerFactory{config.handlersConfig, siteTemplate, *database.get(), urlProvider, *cache.get(), *parser.get()};
 | 
			
		||||
		HandlerFactory handlerFactory{config.handlersConfig, siteTemplate, *database.get(), urlProvider, *cache.get()};
 | 
			
		||||
		RequestWorker requestWorker{handlerFactory, database->createSessionDao(), siteTemplate};
 | 
			
		||||
 | 
			
		||||
		auto interface = createGateway(config);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,16 +26,26 @@
 | 
			
		||||
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)
 | 
			
		||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
			
		||||
	if(policy == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Failed to install sandboxing policy (init): " << result;
 | 
			
		||||
		Logger::error() << "Failed to init sandboxing policy (init)";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	policy->namespace_options = QSSB_UNSHARE_USER;
 | 
			
		||||
	policy->drop_caps = 0;
 | 
			
		||||
	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execveat));
 | 
			
		||||
	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execve));
 | 
			
		||||
	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
 | 
			
		||||
 | 
			
		||||
	int result = qssb_enable_policy(policy);
 | 
			
		||||
	if(result != 0)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Failed to enable sandboxing policy (init): " << result;
 | 
			
		||||
		qssb_free_policy(policy);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	qssb_free_policy(policy);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -44,28 +54,34 @@ 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()];
 | 
			
		||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
			
		||||
	if(policy == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Failed to init sandboxing policy (pre)";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
		qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ | QSSB_FS_ALLOW_WRITE, fsPaths[i].c_str());
 | 
			
		||||
	}
 | 
			
		||||
	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);
 | 
			
		||||
	policy->namespace_options = QSSB_UNSHARE_MOUNT;
 | 
			
		||||
	policy->drop_caps = 0;
 | 
			
		||||
	policy->mount_path_policies_to_chroot = 1;
 | 
			
		||||
 | 
			
		||||
	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execveat));
 | 
			
		||||
	qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execve));
 | 
			
		||||
	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
 | 
			
		||||
 | 
			
		||||
	int result = qssb_enable_policy(policy);
 | 
			
		||||
	if(result != 0)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Failed to install sandboxing policy (preworker): %i" << result;
 | 
			
		||||
		qssb_free_policy(policy);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	delete[] policies;
 | 
			
		||||
	qssb_free_policy(policy);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -88,30 +104,35 @@ bool SandboxLinux::supported()
 | 
			
		||||
}
 | 
			
		||||
bool SandboxLinux::enableForWorker()
 | 
			
		||||
{
 | 
			
		||||
	struct qssb_policy policy = {0};
 | 
			
		||||
	policy.drop_caps = 1;
 | 
			
		||||
	policy.not_dumpable = 1;
 | 
			
		||||
	policy.no_new_privs = 1;
 | 
			
		||||
	struct qssb_policy *policy = qssb_init_policy();
 | 
			
		||||
	if(policy == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Failed to init sandboxing policy (worker) ";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	policy->drop_caps = 1;
 | 
			
		||||
	policy->not_dumpable = 1;
 | 
			
		||||
	policy->no_new_privs = 1;
 | 
			
		||||
	policy->namespace_options = 0;
 | 
			
		||||
 | 
			
		||||
	/* TODO: as said, a whitelist approach is better. As such, this list is bound to be incomplete in the
 | 
			
		||||
	 * 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)
 | 
			
		||||
 | 
			
		||||
	/* TODO: use qssb groups */
 | 
			
		||||
	long 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)};
 | 
			
		||||
 | 
			
		||||
	qssb_append_syscalls_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, blacklisted_syscalls,
 | 
			
		||||
								sizeof(blacklisted_syscalls) / sizeof(blacklisted_syscalls[0]));
 | 
			
		||||
	qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
 | 
			
		||||
 | 
			
		||||
	if(qssb_enable_policy(policy) != 0)
 | 
			
		||||
	{
 | 
			
		||||
		Logger::error() << "Sandbox: Activation of seccomp blacklist failed!";
 | 
			
		||||
		qssb_free_policy(policy);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qssb_free_policy(policy);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,24 +25,13 @@ count integer
 | 
			
		||||
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 INDEX revisionid ON revision (revisionid DESC);
 | 
			
		||||
CREATE INDEX pagename ON page (name)
 | 
			
		||||
;
 | 
			
		||||
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 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 TRIGGER search_ad AFTER DELETE ON revision BEGIN
 | 
			
		||||
  INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page);
 | 
			
		||||
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: 63643e6386...d87d0672a8
									
								
							 Submodule submodules/qsmaddy deleted from 167ce3943d
									
								
							 Submodule submodules/qssb.h updated: 9df2e9ee90...11d64c6fcf
									
								
							
							
								
								
									
										19
									
								
								utils.cpp
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								utils.cpp
									
									
									
									
									
								
							@@ -46,6 +46,12 @@ std::string utils::html_xss(std::string_view str)
 | 
			
		||||
		case '%':
 | 
			
		||||
			result += "%";
 | 
			
		||||
			break;
 | 
			
		||||
		case '\'':
 | 
			
		||||
			result += "'";
 | 
			
		||||
			break;
 | 
			
		||||
		case '&':
 | 
			
		||||
			result += "&";
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			result += c;
 | 
			
		||||
		}
 | 
			
		||||
@@ -93,7 +99,7 @@ std::vector<std::string> utils::split(const std::string &str, char delim)
 | 
			
		||||
// TODO: can easily break if we pass a regex here
 | 
			
		||||
std::vector<std::string> utils::split(const std::string &str, const std::string &delim)
 | 
			
		||||
{
 | 
			
		||||
	std::regex regex { delim + "+" };
 | 
			
		||||
	std::regex regex{delim + "+"};
 | 
			
		||||
	return split(str, regex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -175,14 +181,3 @@ std::string utils::toISODate(time_t t)
 | 
			
		||||
	}
 | 
			
		||||
	return std::string{result};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void utils::regex_callback_extractor(std::regex regex, const std::string &input, std::function<void (std::smatch &)> callback)
 | 
			
		||||
{
 | 
			
		||||
	auto begin = std::sregex_iterator(input.begin(), input.end(), regex);
 | 
			
		||||
	auto end = std::sregex_iterator();
 | 
			
		||||
	for(auto it = begin; it != end; it++)
 | 
			
		||||
	{
 | 
			
		||||
		std::smatch smatch = *it;
 | 
			
		||||
		callback(smatch);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								utils.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								utils.h
									
									
									
									
									
								
							@@ -60,7 +60,6 @@ template <class T, class U> std::vector<U> getAll(std::multimap<T, U> map, T key
 | 
			
		||||
std::string regex_callback_replacer(std::regex regex, const std::string &input,
 | 
			
		||||
									std::function<std::string(std::smatch &)> callback);
 | 
			
		||||
 | 
			
		||||
void regex_callback_extractor(std::regex regex, const std::string &input, std::function<void(std::smatch &)> callback);
 | 
			
		||||
std::string readCompleteFile(std::string_view filepath);
 | 
			
		||||
 | 
			
		||||
inline std::string nz(const char *s)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user