Let's make (git) history!

This commit is contained in:
Albert S. 2018-11-03 17:12:20 +01:00
commit 076106ecb9
212 changed files with 11541 additions and 0 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*.o
*.d
*.out
*.gch
*.user
qswiki
wikiqs*
data/*
gtest*
cgi-bin/*

20
LICENCE Normal file
View File

@ -0,0 +1,20 @@
/* 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.
*/

59
Makefile Normal file
View File

@ -0,0 +1,59 @@
CXXFLAGS=-std=c++17 -O0 -g -pg -no-pie -pipe -MMD -Wall -Wextra
RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs
#currently default g++ versions in most distros do not usually support c++17 well enough
CXX=g++-8.2.0
SOURCES=$(wildcard *.cpp)
SOURCES+=$(wildcard gateway/*.cpp)
SOURCES+=$(wildcard handlers/*.cpp)
SOURCES+=$(wildcard database/*.cpp)
SOURCES+=$(wildcard cache/*.cpp)
HEADERS=$(wildcard *.h)
HEADERS+=$(wildcard gateway/*.h)
HEADERS+=$(wildcard handlers/*.h)
HEADERS+=$(wildcard database/*.h)
HEADERS+=$(wildcard cache/*.h)
OBJECTS=$(patsubst %.cpp, %.o, $(SOURCES))
WIKIOBJECTS=$(filter-out test.o, $(OBJECTS))
TESTOBJECTS=$(filter-out qswiki.o, $(OBJECTS))
DEPENDS = ${WIKIOBJECTS:.o=.d}
-include ${DEPENDS}
# Points to the root of Google Test, relative to where this file is.
# Remember to tweak this if you move this file.
GTEST_DIR = /home/data/SOURCES/gtest/googletest
GTESTS_TESTDIR = ./tests/
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_OBJECTS=$(filter-out qswiki.o, $(WIKIOBJECTS))
.DEFAULT_GOAL := qswiki
release: CXXFLAGS=$(RELEASE_CXXFLAGS)
release: qswiki
qswiki: $(WIKIOBJECTS)
$(CXX) $(WIKIOBJECTS) ${LDFLAGS} -I database/hdr -o qswiki
test: $(TESTOBJECTS)
$(CXX) $(TESTOBJECTS) ${LDFLAGS} -o test
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)
%.o:%.cpp
$(CXX) ${CXXFLAGS} ${LDFLAGS} -I database/hdr -c -o $@ $<
clean:
rm -f $(OBJECTS) $(DEPENDS)

77
README.md Normal file
View File

@ -0,0 +1,77 @@
# qswiki
About
====
qswiki is a wiki software, intended for small wikis. Originally
implemented in C, it's now written in C++.
History
====
A couple of 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 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 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 a little bit.
Of course, it's pretty straightforward at first. No really. Just use CGI.
And indeed, that's probably more than enough. 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. It initially
used a "pile of files database", but that became too painful, so then
I started using sqlite.
C++
---
Eventually the code became unmaintainable. Initially, I wanted something
quick. I did not care about memory leaks (as it was CGI initially).
After FastCGI, they became an issue. In the end, the task of avoiding
memory leaks became too annoying. And of course, C does not include any
"batteries" and while I could manage, this too was another good reason.
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
convenient even. Still, the standard library is lacking and
I would hope for a some better built-in Unicode support in the future.
Features
========
To be fair, at this point it doesn't even have a "diff" between revisions
yet and does not have features that make you prefer it over other wikis.
- CGI
- HTTP server using the header only library cpp-httplib. It's more
portable and more "future-proof" than FastCGI (since the official website
disappeared, the library's future appears to be uncertain).
- Support for user accounts. Passwords are stored using PBKDF2.
sqlite database, but not too much of an effort to add other types of
storage backends. sqlite is using the great header only library
sqlite_modern_cpp
- Relatively fine-grained permission system.
- Categories
- Templates
- FTS search
- Caching
Security
========
The most reasonable way would have been to add some sort sandboxing
support right away, but this is lacking so far. As for "web security",
all POST requests are centrally protected against CSRF attacks and all
input is escaped against XSS attacks.
Building
========
Dependencies:
- cpp-httplib: https://github.com/yhirose/cpp-httplib
- SqliteModernCpp: https://github.com/SqliteModernCpp
Given the fact those are header-only libraries, they are already
included here, so you only need to run:
```make release```
Setup
=====
To be written

8
TODO Normal file
View File

@ -0,0 +1,8 @@
search: allow all chars (filter sqlite match syntax)
diff
Redirection,Rename
UI for permission system.
user administration
user registration
more caching
not all config values take effect yet.

62
cache/fscache.cpp vendored Normal file
View File

@ -0,0 +1,62 @@
#include <filesystem>
#include <fstream>
#include "fscache.h"
#include "../logger.h"
FsCache::FsCache(std::string path)
{
if(!std::filesystem::exists(path))
{
throw std::runtime_error { "Directory does not exist" };
}
this->path = path;
}
std::string FsCache::getFilePath(std::string_view path) const
{
std::filesystem::path ps { path };
std::string name = ps.filename();
return std::filesystem::path { this->path } / name;
}
std::optional<std::string> FsCache::get(std::string_view key) const
{
std::string path = getFilePath(key);
if(std::filesystem::exists(path))
{
return utils::readCompleteFile(path);
}
return { };
}
void FsCache::put(std::string_view key, std::string val)
{
std::string path = std::filesystem::path { this->path } / key;
std::fstream f1;
f1.open(path, std::ios::out);
f1 << val;
}
void FsCache::remove(std::string_view key)
{
std::filesystem::remove_all(std::filesystem::path { this->path} / key);
}
void FsCache::removePrefix(std::string_view prefix)
{
//TODO: lock dir
for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path { this->path }))
{
if(static_cast<std::string>(entry.path().filename()).find(prefix) == 0)
{
std::filesystem::remove_all(entry);
}
}
}
void FsCache::clear()
{
for(auto &entry : std::filesystem::directory_iterator(std::filesystem::path { this->path }))
{
std::filesystem::remove_all(entry);
}
}

20
cache/fscache.h vendored Normal file
View File

@ -0,0 +1,20 @@
#ifndef FSCACHE_H
#define FSCACHE_H
#include "icache.h"
class FsCache : public ICache
{
private:
std::string path;
std::string getFilePath(std::string_view path) const;
public:
FsCache(std::string directory);
std::optional<std::string> get(std::string_view key) const;
void put(std::string_view key, std::string val);
void remove(std::string_view key);
void removePrefix(std::string_view prefix);
void clear();
using ICache::ICache;
~FsCache() { }
};
#endif // FSCACHE_H

18
cache/icache.h vendored Normal file
View File

@ -0,0 +1,18 @@
#ifndef ICACHE_H
#define ICACHE_H
#include <string>
#include <string_view>
#include <optional>
#include "../utils.h"
class ICache
{
public:
virtual std::optional<std::string> get(std::string_view key) const = 0;
virtual void put(std::string_view key, std::string val) = 0;
virtual void remove(std::string_view key) = 0;
virtual void removePrefix(std::string_view prefix) = 0;
virtual void clear() = 0;
virtual ~ICache() { }
};
#endif // ICACHE_H

26
category.cpp Normal file
View File

@ -0,0 +1,26 @@
/* 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 "category.h"
Category::Category()
{
}

14
category.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef CATEGORY_H
#define CATEGORY_H
#include <string>
class Category
{
public:
Category();
unsigned int id;
std::string name;
};
#endif // CATEGORY_H

127
config.cpp Normal file
View File

@ -0,0 +1,127 @@
/* 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 "config.h"
#include "permissions.h"
#include <exception>
#include <iostream>
std::string Config::required(const std::string &key)
{
auto it = this->configmap.find(key);
if(it != this->configmap.end())
{
return it->second;
}
throw std::runtime_error("Required config key " + key + " not found");
}
std::string Config::optional(const std::string &key, std::string defaultvalue)
{
auto it = this->configmap.find(key);
if(it != this->configmap.end())
{
return it->second;
}
return defaultvalue;
}
int Config::optional(const std::string &key, int defaultvalue)
{
auto it = this->configmap.find(key);
if(it != this->configmap.end())
{
std::string str = it->second;
return std::stoi(str);
}
return defaultvalue;
}
Config::Config(const std::map<std::string, std::string> &map)
{
this->configmap = map;
this->wikipath = optional("wikipath", "/");
this->anon_username = optional("anon_username", "anonymouse");
this->wikiname = required("wikiname");
this->logfile = required("logfile");
this->templatepath = required("templatepath");
this->linkallcats = required("linkallcats");
this->linkallpages = required("linkallpages");
this->linkcategory = required("linkcategory");
this->linkdelete = required("linkdelete");
this->linkedit = required("linkedit");
this->linkhistory = required("linkhistory");
this->linkindex = required("linkindex");
this->linklogout = required("linklogout");
this->linkpage = required("linkpage");
this->linkrecent = required("linkrecent");
this->linkrevision = required("linkrevision");
this->linksettings = required("linksettings");
this->linkshere = required("linkshere");
this->loginurl = required("loginurl");
this->linkrecentsort = required("linkrecentsort");
this->linkhistorysort = required("linkhistorysort");
this->actionurl = required("actionurl");
this->settingsurl = required("settingsurl");
this->deletionurl = required("deletionurl");
this->adminregisterurl = required("adminregisterurl");
this->userchangepwurl = required("userchangepwurl");
this->connectionstring = required("connectionstring");
this->max_pagename_length = optional("max_pagename_length", 256);
this->session_max_lifetime = optional("session_max_lifetime", 3600);
this->query_limit = optional("query_limit", 200);
this->threadscount = optional("threadscount", 1);
this->anon_permissions = Permissions(required("anon_permissions"));
this->templateprefix = "{wikiqs:";
}
ConfigReader::ConfigReader(const std::string &file)
{
this->path = file;
}
Config ConfigReader::readConfig()
{
std::fstream f1(path, std::fstream::in);
std::string line;
std::map<std::string, std::string> configmap;
while(getline(f1, line))
{
if(isspace(line[0]) || line[0] == '#') {
continue;
}
std::stringstream s(line);
std::string key;
std::string value;
s >> key >> value;
configmap.insert(std::make_pair(std::move(key), std::move(value)));
}
return Config(configmap);
}

74
config.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <string>
#include <fstream>
#include <sstream>
#include <map>
#include "permissions.h"
#include "utils.h"
class Config
{
private:
std::map<std::string, std::string> configmap;
std::string required(const std::string &key);
std::string optional(const std::string &key, std::string defaultvalue = "");
int optional(const std::string &key, int defaulvalue);
public:
Config(const std::map<std::string, std::string> &map );
//TODO: these could be references!?
std::string wikiname;
std::string wikipath;
std::string templatepath;
std::string templateprefix;
std::string logfile;
std::string anon_username;
std::string linkindex ;
std::string linkrecent ;
std::string linkallpages ;
std::string linkallcats ;
std::string linkshere ;
std::string linkpage ;
std::string linkrevision ;
std::string linkhistory ;
std::string linkedit ;
std::string linksettings;
std::string linkdelete ;
std::string linklogout ;
std::string linkcategory;
std::string loginurl;
std::string linkrecentsort;
std::string actionurl;
std::string settingsurl;
std::string deletionurl;
std::string linkhistorysort;
std::string adminregisterurl;
std::string userchangepwurl;
std::string connectionstring;
int query_limit;
int session_max_lifetime;
int max_pagename_length;
int threadscount;
Permissions anon_permissions;
std::string getConfig(const std::string &key) const
{
return utils::getKeyOrEmpty(configmap, key);
}
};
class ConfigReader
{
private:
std::string path;
public:
ConfigReader(const std::string &file);
Config readConfig();
};
#endif // CONFIG_H

28
cookie.cpp Normal file
View File

@ -0,0 +1,28 @@
/* 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 "cookie.h"
Cookie::Cookie(const std::string &key, const std::string &val, int expireSeconds)
{
this->key = key;
this->value = val;
this->expires = expireSeconds;
}

20
cookie.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef COOKIE_H
#define COOKIE_H
#include <string>
class Cookie
{
public:
std::string key;
std::string value;
int expires;
Cookie(const std::string &key, const std::string &val, int expireSeconds = 0);
std::string createHeaderValue() const
{
return key + "=" + value + "; path=/; HttpOnly";
}
};
#endif // COOKIE_H

26
database/categorydao.cpp Normal file
View File

@ -0,0 +1,26 @@
/* 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 "categorydao.h"
CategoryDao::CategoryDao()
{
}

21
database/categorydao.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CATEGORYDAO_H
#define CATEGORYDAO_H
#include <string>
#include <vector>
#include <optional>
#include "queryoption.h"
#include "../category.h"
class CategoryDao
{
public:
CategoryDao();
virtual void save(const Category &c) = 0;
virtual std::vector<std::string> fetchList(QueryOption o) = 0;
virtual std::optional<Category> find(std::string name) = 0;
virtual void deleteCategory(std::string name) = 0;
virtual std::vector<std::string> fetchMembers(std::string name, QueryOption o) = 0;
};
#endif // CATEGORYDAO_H

View File

@ -0,0 +1,118 @@
/* 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 <string>
#include <vector>
#include "categorydaosqlite.h"
#include "sqlitequeryoption.h"
CategoryDaoSqlite::CategoryDaoSqlite()
{
}
std::optional<Category> CategoryDaoSqlite::find(std::string name)
{
try {
Category result;
*db << "SELECT id, name FROM category WHERE name = ?" << name >> std::tie(result.id, result.name);
return result;
}
catch(const sqlite::exceptions::no_rows &e)
{
return {};
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
void CategoryDaoSqlite::save(const Category &c)
{
try
{
*db << "INSERT OR IGNORE INTO category (id, name) VALUES (SELECT id FROM category WHERE lower(name) = lower(?), lower(?)" <<c.name << c.name;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
void CategoryDaoSqlite::deleteCategory(std::string name)
{
try
{
*db << "BEGIN";
*db << "DELETE FROM categorymember WHERE catid = (SELECT id FROM category WHERE name = ?)" << name;
*db << "DELETE FROM category WHERE name = ?" << name;
*db << "COMMIT;";
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
std::vector<std::string> CategoryDaoSqlite::fetchList(QueryOption o)
{
std::vector<std::string> result;
try
{
auto queryoption = SqliteQueryOption(o).setPrependWhere(true).setOrderByColumn("name").build();
*db << "SELECT name FROM category " + queryoption >> [&](std::string n) { result.push_back(n);};
}
catch(const sqlite::exceptions::no_rows &e)
{
return result;
}
catch(const sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::vector<std::string> CategoryDaoSqlite::fetchMembers(std::string name, QueryOption o)
{
std::vector<std::string> result;
SqliteQueryOption queryOption { o };
std::string queryoptions = queryOption.setOrderByColumn("name").setVisibleColumnName("page.visible").setPrependWhere(false).build();
try
{
auto query = *db << "SELECT page.name AS name FROM categorymember INNER JOIN page ON page.id = categorymember.page WHERE category = (SELECT id FROM category WHERE name = ? ) AND " + queryoptions << name;
query >> [&](std::string p) { result.push_back(p);};
}
catch(const sqlite::exceptions::no_rows &e)
{
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}

View File

@ -0,0 +1,18 @@
#ifndef CATEGORYDAOSQLITE_H
#define CATEGORYDAOSQLITE_H
#include "categorydao.h"
#include "sqlitedao.h"
class CategoryDaoSqlite : public CategoryDao, protected SqliteDao
{
public:
CategoryDaoSqlite();
std::vector<std::string> fetchList(QueryOption o) override;
std::vector<std::string> fetchMembers(std::string name, QueryOption o) override;
void save(const Category &c) override;
void deleteCategory(std::string name) override;
std::optional<Category> find(std::string name) override;
using SqliteDao::SqliteDao;
};
#endif // CATEGORYDAOSQLITE_H

32
database/database.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef DATABASE_H
#define DATABASE_H
#include <memory>
#include <string>
#include "../user.h"
#include "../request.h"
#include "../response.h"
#include "pagedao.h"
#include "revisiondao.h"
#include "sessiondao.h"
#include "userdao.h"
#include "categorydao.h"
class Database
{
private:
std::string connnectionstring;
public:
Database() { }
Database(std::string connstring) { this->connnectionstring = connstring; }
virtual void beginTransaction() = 0;
virtual void rollbackTransaction() = 0;
virtual void commitTransaction() = 0;
virtual std::unique_ptr<PageDao> createPageDao() const = 0;
virtual std::unique_ptr<RevisionDao> createRevisionDao() const = 0;
virtual std::unique_ptr<SessionDao> createSessionDao() const = 0;
virtual std::unique_ptr<UserDao> createUserDao() const = 0;
virtual std::unique_ptr<CategoryDao> createCategoryDao() const = 0;
virtual ~Database() { }
};
#endif

View File

@ -0,0 +1,26 @@
/* 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 "databasefactory.h"
#include "sqlite.h"
std::unique_ptr<Database> createDatabase(const Config &o)
{
return std::make_unique<Sqlite>(o.connectionstring);
}

View File

@ -0,0 +1,7 @@
#ifndef DATABASEFACTORY_H
#define DATABASEFACTORY_H
#include "../config.h"
#include "database.h"
std::unique_ptr<Database> createDatabase(const Config &o);
#endif // DATABASEFACTORY_H

16
database/exceptions.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef EXCEPTIONS_H
#define EXCEPTIONS_H
#include <stdexcept>
class DatabaseException : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
class DatabaseQueryException : public DatabaseException
{
using DatabaseException::DatabaseException;
};
#endif // EXCEPTIONS_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
#pragma once
#include <string>
#include <stdexcept>
#include <sqlite3.h>
namespace sqlite {
class sqlite_exception: public std::runtime_error {
public:
sqlite_exception(const char* msg, std::string sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
sqlite_exception(int code, std::string sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {}
int get_code() const {return code & 0xFF;}
int get_extended_code() const {return code;}
std::string get_sql() const {return sql;}
private:
int code;
std::string sql;
};
namespace errors {
//One more or less trivial derived error class for each SQLITE error.
//Note the following are not errors so have no classes:
//SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE
//
//Note these names are exact matches to the names of the SQLITE error codes.
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\
derived
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
class base ## _ ## sub: public base { using base::base; };
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
//Some additional errors are here for the C++ interface
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; };
static void throw_sqlite_error(const int& error_code, const std::string &sql = "") {
switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \
derived \
default: throw name(error_code, sql); \
}
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql);
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
default: throw sqlite_exception(error_code, sql);
}
}
}
namespace exceptions = errors;
}

View File

@ -0,0 +1,93 @@
#if SQLITE_VERSION_NUMBER < 3010000
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#endif
SQLITE_MODERN_CPP_ERROR_CODE(ERROR,error,)
SQLITE_MODERN_CPP_ERROR_CODE(INTERNAL,internal,)
SQLITE_MODERN_CPP_ERROR_CODE(PERM,perm,)
SQLITE_MODERN_CPP_ERROR_CODE(ABORT,abort,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(ABORT,ROLLBACK,abort,rollback)
)
SQLITE_MODERN_CPP_ERROR_CODE(BUSY,busy,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY,RECOVERY,busy,recovery)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BUSY,SNAPSHOT,busy,snapshot)
)
SQLITE_MODERN_CPP_ERROR_CODE(LOCKED,locked,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(LOCKED,SHAREDCACHE,locked,sharedcache)
)
SQLITE_MODERN_CPP_ERROR_CODE(NOMEM,nomem,)
SQLITE_MODERN_CPP_ERROR_CODE(READONLY,readonly,)
SQLITE_MODERN_CPP_ERROR_CODE(INTERRUPT,interrupt,)
SQLITE_MODERN_CPP_ERROR_CODE(IOERR,ioerr,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,READ,ioerr,read)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHORT_READ,ioerr,short_read)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,WRITE,ioerr,write)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,FSYNC,ioerr,fsync)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DIR_FSYNC,ioerr,dir_fsync)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,TRUNCATE,ioerr,truncate)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,FSTAT,ioerr,fstat)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,UNLOCK,ioerr,unlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,RDLOCK,ioerr,rdlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DELETE,ioerr,delete)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,BLOCKED,ioerr,blocked)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,NOMEM,ioerr,nomem)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,ACCESS,ioerr,access)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CHECKRESERVEDLOCK,ioerr,checkreservedlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,LOCK,ioerr,lock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CLOSE,ioerr,close)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DIR_CLOSE,ioerr,dir_close)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMOPEN,ioerr,shmopen)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMSIZE,ioerr,shmsize)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMLOCK,ioerr,shmlock)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SHMMAP,ioerr,shmmap)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,SEEK,ioerr,seek)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,DELETE_NOENT,ioerr,delete_noent)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,MMAP,ioerr,mmap)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,GETTEMPPATH,ioerr,gettemppath)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,CONVPATH,ioerr,convpath)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,VNODE,ioerr,vnode)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(IOERR,AUTH,ioerr,auth)
)
SQLITE_MODERN_CPP_ERROR_CODE(CORRUPT,corrupt,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CORRUPT,VTAB,corrupt,vtab)
)
SQLITE_MODERN_CPP_ERROR_CODE(NOTFOUND,notfound,)
SQLITE_MODERN_CPP_ERROR_CODE(FULL,full,)
SQLITE_MODERN_CPP_ERROR_CODE(CANTOPEN,cantopen,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,NOTEMPDIR,cantopen,notempdir)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,ISDIR,cantopen,isdir)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,FULLPATH,cantopen,fullpath)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CANTOPEN,CONVPATH,cantopen,convpath)
)
SQLITE_MODERN_CPP_ERROR_CODE(PROTOCOL,protocol,)
SQLITE_MODERN_CPP_ERROR_CODE(EMPTY,empty,)
SQLITE_MODERN_CPP_ERROR_CODE(SCHEMA,schema,)
SQLITE_MODERN_CPP_ERROR_CODE(TOOBIG,toobig,)
SQLITE_MODERN_CPP_ERROR_CODE(CONSTRAINT,constraint,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,CHECK,constraint,check)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,COMMITHOOK,constraint,commithook)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,FOREIGNKEY,constraint,foreignkey)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,FUNCTION,constraint,function)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,NOTNULL,constraint,notnull)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,PRIMARYKEY,constraint,primarykey)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,TRIGGER,constraint,trigger)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,UNIQUE,constraint,unique)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,VTAB,constraint,vtab)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(CONSTRAINT,ROWID,constraint,rowid)
)
SQLITE_MODERN_CPP_ERROR_CODE(MISMATCH,mismatch,)
SQLITE_MODERN_CPP_ERROR_CODE(MISUSE,misuse,)
SQLITE_MODERN_CPP_ERROR_CODE(NOLFS,nolfs,)
SQLITE_MODERN_CPP_ERROR_CODE(AUTH,auth,
)
SQLITE_MODERN_CPP_ERROR_CODE(FORMAT,format,)
SQLITE_MODERN_CPP_ERROR_CODE(RANGE,range,)
SQLITE_MODERN_CPP_ERROR_CODE(NOTADB,notadb,)
SQLITE_MODERN_CPP_ERROR_CODE(NOTICE,notice,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE,RECOVER_WAL,notice,recover_wal)
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(NOTICE,RECOVER_ROLLBACK,notice,recover_rollback)
)
SQLITE_MODERN_CPP_ERROR_CODE(WARNING,warning,
SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(WARNING,AUTOINDEX,warning,autoindex)
)

View File

@ -0,0 +1,101 @@
#include "errors.h"
#include <sqlite3.h>
#include <utility>
#include <tuple>
#include <type_traits>
namespace sqlite {
namespace detail {
template<class>
using void_t = void;
template<class T, class = void>
struct is_callable : std::false_type {};
template<class Functor, class ...Arguments>
struct is_callable<Functor(Arguments...), void_t<decltype(std::declval<Functor>()(std::declval<Arguments>()...))>> : std::true_type {};
template<class Functor, class ...Functors>
class FunctorOverload: public Functor, public FunctorOverload<Functors...> {
public:
template<class Functor1, class ...Remaining>
FunctorOverload(Functor1 &&functor, Remaining &&... remaining):
Functor(std::forward<Functor1>(functor)),
FunctorOverload<Functors...>(std::forward<Remaining>(remaining)...) {}
using Functor::operator();
using FunctorOverload<Functors...>::operator();
};
template<class Functor>
class FunctorOverload<Functor>: public Functor {
public:
template<class Functor1>
FunctorOverload(Functor1 &&functor):
Functor(std::forward<Functor1>(functor)) {}
using Functor::operator();
};
template<class Functor>
class WrapIntoFunctor: public Functor {
public:
template<class Functor1>
WrapIntoFunctor(Functor1 &&functor):
Functor(std::forward<Functor1>(functor)) {}
using Functor::operator();
};
template<class ReturnType, class ...Arguments>
class WrapIntoFunctor<ReturnType(*)(Arguments...)> {
ReturnType(*ptr)(Arguments...);
public:
WrapIntoFunctor(ReturnType(*ptr)(Arguments...)): ptr(ptr) {}
ReturnType operator()(Arguments... arguments) { return (*ptr)(std::forward<Arguments>(arguments)...); }
};
inline void store_error_log_data_pointer(std::shared_ptr<void> ptr) {
static std::shared_ptr<void> stored;
stored = std::move(ptr);
}
template<class T>
std::shared_ptr<typename std::decay<T>::type> make_shared_inferred(T &&t) {
return std::make_shared<typename std::decay<T>::type>(std::forward<T>(t));
}
}
template<class Handler>
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler);
template<class Handler>
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler);
template<class ...Handler>
typename std::enable_if<sizeof...(Handler)>=2>::type
error_log(Handler &&...handler) {
return error_log(detail::FunctorOverload<detail::WrapIntoFunctor<typename std::decay<Handler>::type>...>(std::forward<Handler>(handler)...));
}
template<class Handler>
typename std::enable_if<!detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler) {
return error_log(std::forward<Handler>(handler), [](const sqlite_exception&) {});
}
template<class Handler>
typename std::enable_if<detail::is_callable<Handler(const sqlite_exception&)>::value>::type
error_log(Handler &&handler) {
auto ptr = detail::make_shared_inferred([handler = std::forward<Handler>(handler)](int error_code, const char *errstr) mutable {
switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \
derived \
default: handler(errors::name(errstr, "", error_code)); \
};break;
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
case SQLITE_ ## BASE ## _ ## SUB: \
handler(errors::base ## _ ## sub(errstr, "", error_code)); \
break;
#include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE
default: handler(sqlite_exception(errstr, "", error_code)); \
}
});
sqlite3_config(SQLITE_CONFIG_LOG, (void(*)(void*,int,const char*))[](void *functor, int error_code, const char *errstr) {
(*static_cast<decltype(ptr.get())>(functor))(error_code, errstr);
}, ptr.get());
detail::store_error_log_data_pointer(std::move(ptr));
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#ifndef SQLITE_HAS_CODEC
#define SQLITE_HAS_CODEC
#endif
#include "../sqlite_modern_cpp.h"
namespace sqlite {
struct sqlcipher_config : public sqlite_config {
std::string key;
};
class sqlcipher_database : public database {
public:
sqlcipher_database(std::string db, const sqlcipher_config &config): database(db, config) {
set_key(config.key);
}
sqlcipher_database(std::u16string db, const sqlcipher_config &config): database(db, config) {
set_key(config.key);
}
void set_key(const std::string &key) {
if(auto ret = sqlite3_key(_db.get(), key.data(), key.size()))
errors::throw_sqlite_error(ret);
}
void set_key(const std::string &key, const std::string &db_name) {
if(auto ret = sqlite3_key_v2(_db.get(), db_name.c_str(), key.data(), key.size()))
errors::throw_sqlite_error(ret);
}
void rekey(const std::string &new_key) {
if(auto ret = sqlite3_rekey(_db.get(), new_key.data(), new_key.size()))
errors::throw_sqlite_error(ret);
}
void rekey(const std::string &new_key, const std::string &db_name) {
if(auto ret = sqlite3_rekey_v2(_db.get(), db_name.c_str(), new_key.data(), new_key.size()))
errors::throw_sqlite_error(ret);
}
};
}

View File

@ -0,0 +1,55 @@
#pragma once
#include <tuple>
#include<type_traits>
namespace sqlite {
namespace utility {
template<typename> struct function_traits;
template <typename Function>
struct function_traits : public function_traits<
decltype(&std::remove_reference<Function>::type::operator())
> { };
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct function_traits<
ReturnType(ClassType::*)(Arguments...) const
> : function_traits<ReturnType(*)(Arguments...)> { };
/* support the non-const operator ()
* this will work with user defined functors */
template <
typename ClassType,
typename ReturnType,
typename... Arguments
>
struct function_traits<
ReturnType(ClassType::*)(Arguments...)
> : function_traits<ReturnType(*)(Arguments...)> { };
template <
typename ReturnType,
typename... Arguments
>
struct function_traits<
ReturnType(*)(Arguments...)
> {
typedef ReturnType result_type;
template <std::size_t Index>
using argument = typename std::tuple_element<
Index,
std::tuple<Arguments...>
>::type;
static const std::size_t arity = sizeof...(Arguments);
};
}
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <cassert>
#include <exception>
#include <iostream>
namespace sqlite {
namespace utility {
#ifdef __cpp_lib_uncaught_exceptions
class UncaughtExceptionDetector {
public:
operator bool() {
return count != std::uncaught_exceptions();
}
private:
int count = std::uncaught_exceptions();
};
#else
class UncaughtExceptionDetector {
public:
operator bool() {
return std::uncaught_exception();
}
};
#endif
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <locale>
#include <string>
#include <algorithm>
#include "../errors.h"
namespace sqlite {
namespace utility {
inline std::string utf16_to_utf8(const std::u16string &input) {
struct : std::codecvt<char16_t, char, std::mbstate_t> {
} codecvt;
std::mbstate_t state{};
std::string result((std::max)(input.size() * 3 / 2, std::size_t(4)), '\0');
const char16_t *remaining_input = input.data();
std::size_t produced_output = 0;
while(true) {
char *used_output;
switch(codecvt.out(state, remaining_input, &input[input.size()],
remaining_input, &result[produced_output],
&result[result.size() - 1] + 1, used_output)) {
case std::codecvt_base::ok:
result.resize(used_output - result.data());
return result;
case std::codecvt_base::noconv:
// This should be unreachable
case std::codecvt_base::error:
throw errors::invalid_utf16("Invalid UTF-16 input", "");
case std::codecvt_base::partial:
if(used_output == result.data() + produced_output)
throw errors::invalid_utf16("Unexpected end of input", "");
produced_output = used_output - result.data();
result.resize(
result.size()
+ (std::max)((&input[input.size()] - remaining_input) * 3 / 2,
std::ptrdiff_t(4)));
}
}
}
} // namespace utility
} // namespace sqlite

View File

@ -0,0 +1,201 @@
#pragma once
#include "../errors.h"
#include <sqlite3.h>
#include <optional>
#include <variant>
namespace sqlite::utility {
template<typename ...Options>
struct VariantFirstNullable {
using type = void;
};
template<typename T, typename ...Options>
struct VariantFirstNullable<T, Options...> {
using type = typename VariantFirstNullable<Options...>::type;
};
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
template<typename T, typename ...Options>
struct VariantFirstNullable<std::optional<T>, Options...> {
using type = std::optional<T>;
};
#endif
template<typename T, typename ...Options>
struct VariantFirstNullable<std::unique_ptr<T>, Options...> {
using type = std::unique_ptr<T>;
};
template<typename ...Options>
struct VariantFirstNullable<std::nullptr_t, Options...> {
using type = std::nullptr_t;
};
template<typename Callback, typename ...Options>
inline void variant_select_null(Callback&&callback) {
if constexpr(std::is_same_v<typename VariantFirstNullable<Options...>::type, void>) {
throw errors::mismatch("NULL is unsupported by this variant.", "", SQLITE_MISMATCH);
} else {
std::forward<Callback>(callback)(typename VariantFirstNullable<Options...>::type());
}
}
template<typename ...Options>
struct VariantFirstIntegerable {
using type = void;
};
template<typename T, typename ...Options>
struct VariantFirstIntegerable<T, Options...> {
using type = typename VariantFirstIntegerable<Options...>::type;
};
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
template<typename T, typename ...Options>
struct VariantFirstIntegerable<std::optional<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstIntegerable<Options...>::type>;
};
#endif
template<typename T, typename ...Options>
struct VariantFirstIntegerable<std::enable_if_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>>, std::unique_ptr<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstIntegerable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstIntegerable<Options...>::type>;
};
template<typename ...Options>
struct VariantFirstIntegerable<int, Options...> {
using type = int;
};
template<typename ...Options>
struct VariantFirstIntegerable<sqlite_int64, Options...> {
using type = sqlite_int64;
};
template<typename Callback, typename ...Options>
inline auto variant_select_integer(Callback&&callback) {
if constexpr(std::is_same_v<typename VariantFirstIntegerable<Options...>::type, void>) {
throw errors::mismatch("Integer is unsupported by this variant.", "", SQLITE_MISMATCH);
} else {
std::forward<Callback>(callback)(typename VariantFirstIntegerable<Options...>::type());
}
}
template<typename ...Options>
struct VariantFirstFloatable {
using type = void;
};
template<typename T, typename ...Options>
struct VariantFirstFloatable<T, Options...> {
using type = typename VariantFirstFloatable<Options...>::type;
};
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
template<typename T, typename ...Options>
struct VariantFirstFloatable<std::optional<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstFloatable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstFloatable<Options...>::type>;
};
#endif
template<typename T, typename ...Options>
struct VariantFirstFloatable<std::unique_ptr<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstFloatable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstFloatable<Options...>::type>;
};
template<typename ...Options>
struct VariantFirstFloatable<float, Options...> {
using type = float;
};
template<typename ...Options>
struct VariantFirstFloatable<double, Options...> {
using type = double;
};
template<typename Callback, typename ...Options>
inline auto variant_select_float(Callback&&callback) {
if constexpr(std::is_same_v<typename VariantFirstFloatable<Options...>::type, void>) {
throw errors::mismatch("Real is unsupported by this variant.", "", SQLITE_MISMATCH);
} else {
std::forward<Callback>(callback)(typename VariantFirstFloatable<Options...>::type());
}
}
template<typename ...Options>
struct VariantFirstTextable {
using type = void;
};
template<typename T, typename ...Options>
struct VariantFirstTextable<T, Options...> {
using type = typename VariantFirstTextable<void, Options...>::type;
};
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
template<typename T, typename ...Options>
struct VariantFirstTextable<std::optional<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstTextable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstTextable<Options...>::type>;
};
#endif
template<typename T, typename ...Options>
struct VariantFirstTextable<std::unique_ptr<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstTextable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstTextable<Options...>::type>;
};
template<typename ...Options>
struct VariantFirstTextable<std::string, Options...> {
using type = std::string;
};
template<typename ...Options>
struct VariantFirstTextable<std::u16string, Options...> {
using type = std::u16string;
};
template<typename Callback, typename ...Options>
inline void variant_select_text(Callback&&callback) {
if constexpr(std::is_same_v<typename VariantFirstTextable<Options...>::type, void>) {
throw errors::mismatch("Text is unsupported by this variant.", "", SQLITE_MISMATCH);
} else {
std::forward<Callback>(callback)(typename VariantFirstTextable<Options...>::type());
}
}
template<typename ...Options>
struct VariantFirstBlobable {
using type = void;
};
template<typename T, typename ...Options>
struct VariantFirstBlobable<T, Options...> {
using type = typename VariantFirstBlobable<Options...>::type;
};
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
template<typename T, typename ...Options>
struct VariantFirstBlobable<std::optional<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstBlobable<T, Options...>::type, T>, std::optional<T>, typename VariantFirstBlobable<Options...>::type>;
};
#endif
template<typename T, typename ...Options>
struct VariantFirstBlobable<std::unique_ptr<T>, Options...> {
using type = std::conditional_t<std::is_same_v<typename VariantFirstBlobable<T, Options...>::type, T>, std::unique_ptr<T>, typename VariantFirstBlobable<Options...>::type>;
};
template<typename T, typename A, typename ...Options>
struct VariantFirstBlobable<std::enable_if_t<std::is_pod_v<T>>, std::vector<T, A>, Options...> {
using type = std::vector<T, A>;
};
template<typename Callback, typename ...Options>
inline auto variant_select_blob(Callback&&callback) {
if constexpr(std::is_same_v<typename VariantFirstBlobable<Options...>::type, void>) {
throw errors::mismatch("Blob is unsupported by this variant.", "", SQLITE_MISMATCH);
} else {
std::forward<Callback>(callback)(typename VariantFirstBlobable<Options...>::type());
}
}
template<typename ...Options>
inline auto variant_select(int type) {
return [type](auto &&callback) {
using Callback = decltype(callback);
switch(type) {
case SQLITE_NULL:
variant_select_null<Callback, Options...>(std::forward<Callback>(callback));
break;
case SQLITE_INTEGER:
variant_select_integer<Callback, Options...>(std::forward<Callback>(callback));
break;
case SQLITE_FLOAT:
variant_select_float<Callback, Options...>(std::forward<Callback>(callback));
break;
case SQLITE_TEXT:
variant_select_text<Callback, Options...>(std::forward<Callback>(callback));
break;
case SQLITE_BLOB:
variant_select_blob<Callback, Options...>(std::forward<Callback>(callback));
break;
default:;
/* assert(false); */
}
};
}
}

26
database/pagedao.cpp Normal file
View File

@ -0,0 +1,26 @@
/* 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 "pagedao.h"
PageDao::PageDao()
{
}

28
database/pagedao.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef PAGEDAO_H
#define PAGEDAO_H
#include <string>
#include <optional>
#include <vector>
#include "queryoption.h"
#include "../page.h"
#include "../searchresult.h"
class PageDao
{
public:
PageDao();
virtual bool exists(std::string page) const = 0;
virtual bool exists(int id) const = 0;
virtual std::optional<Page> find(std::string name) = 0;
virtual std::optional<Page> find(int id) = 0;
virtual std::vector<std::string> getPageList(QueryOption option) = 0;
virtual std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) = 0;
virtual void deletePage(std::string page) = 0;
virtual void save(const Page &page) = 0;
//TODO: this may not be the correct place for this.
virtual void setCategories(std::string pagename, const std::vector<std::string> &catnames) = 0;
virtual std::vector<SearchResult> search(std::string query, QueryOption option) = 0;
virtual ~PageDao() { }
};
#endif // PAGEDAO_H

201
database/pagedaosqlite.cpp Normal file
View File

@ -0,0 +1,201 @@
/* 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 <sqlite_modern_cpp/errors.h>
#include "pagedaosqlite.h"
#include "exceptions.h"
#include "sqlitequeryoption.h"
#include "../logger.h"
/* TODO: copied from C version mostly, review whether access to table other than page is ok */
bool PageDaoSqlite::exists(int id) const
{
auto binder = *db << "SELECT 1 from page WHERE id = ?" << id ;
return execBool(binder);
}
bool PageDaoSqlite::exists(std::string name) const
{
auto binder = *db << "SELECT 1 FROM page WHERE name = ?" << name;
return execBool(binder);
}
std::optional<Page> PageDaoSqlite::find(std::string name)
{
int pageid = fetchPageId(name);
return find(pageid);
}
std::optional<Page> PageDaoSqlite::find(int id)
{
Page result;
try
{
auto ps = *db << "SELECT name, lastrevision, visible FROM page WHERE id = ?";
ps << id >> std::tie(result.name, result.current_revision, result.listed);
}
catch(const sqlite::errors::no_rows &e)
{
return { };
}
catch(sqlite::sqlite_exception& e)
{
throwFrom(e);
}
return result;
}
void PageDaoSqlite::deletePage(std::string page)
{
int pageId = this->fetchPageId(page);
//TODO on delete cascade is better most certainly
try
{
*db << "BEGIN;";
*db << "DELETE FROM revision WHERE page = ?;" << pageId;
*db << "DELETE FROM categorymember WHERE page = ?;" << pageId;
*db << "DELETE FROM permissions WHERE page = ?;" << pageId;
*db << "DELETE FROM page WHERE id =?;" << pageId;
*db << "COMMIT;";
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
void PageDaoSqlite::save(const Page &page)
{
try
{
*db << "INSERT INTO page (name, lastrevision, visible) VALUES(?, ?, ?)" << page.name << page.current_revision << page.listed;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
std::vector<std::string> PageDaoSqlite::getPageList(QueryOption option)
{
std::vector<std::string> result;
try
{
std::string queryOption = SqliteQueryOption(option).setOrderByColumn("lower(name)").setVisibleColumnName("visible").setPrependWhere(true).build();
std::string query = "SELECT name FROM page " + queryOption;
*db << query >> [&](std::string name)
{
result.push_back(name);
};
}
catch(const sqlite::errors::no_rows &e)
{
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::vector<std::string> PageDaoSqlite::fetchCategories(std::string pagename, QueryOption option)
{
std::vector<std::string> result;
try
{
auto query = *db << "SELECT name FROM categorymember INNNER JOIN category ON category = category.id WHERE page = (SELECT id FROM page WHERE name = ?)" << pagename;
query << " AND " << SqliteQueryOption(option).setPrependWhere(false).setOrderByColumn("name").build();
query >> [&](std::string pagename) { result.push_back(pagename);};
}
catch(const sqlite::exceptions::no_rows &e)
{
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::vector<SearchResult> PageDaoSqlite::search(std::string name, QueryOption option)
{
std::vector<SearchResult> result;
try
{
std::string qo = SqliteQueryOption(option).setPrependWhere(false).setOrderByColumn("rank").build();
//TODO: what is passed here, simple gets thrown to the MATCH operator without escaping or anything and this is suboptimal
auto query = *db << "SELECT page.name FROM search INNER JOIN page ON search.page = page.id WHERE search MATCH ? " << name;
query >> [&](std::string pagename) {
SearchResult sresult;
sresult.pagename = pagename;
sresult.query = name;
result.push_back(sresult);
};
}
catch(const sqlite::exceptions::no_rows &e)
{
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
void PageDaoSqlite::setCategories(std::string pagename, const std::vector<std::string> &catnames)
{
try
{
int pageid = fetchPageId(pagename);
*db << "savepoint setcategories;";
*db << "DELETE FROM categorymember WHERE page = ?" << pageid;
for(const std::string &cat : catnames)
{
*db << "INSERT OR IGNORE INTO category (id, name) VALUES( (SELECT id FROM category WHERE lower(name) = lower(?)), lower(?))" << cat << cat;
*db << "INSERT INTO categorymember (category, page) VALUES ( (SELECT ID FROM category WHERE lower(name) = lower(?)), ?)" << cat << pageid;
}
*db << "release setcategories;";
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
int PageDaoSqlite::fetchPageId(std::string pagename)
{
auto binder = *db << "SELECT id FROM page WHERE name = ?" << pagename;
return execInt(binder);
}

28
database/pagedaosqlite.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef PAGEDAOSQLITE_H
#define PAGEDAOSQLITE_H
#include <string>
#include <optional>
#include <sqlite3.h>
#include "../page.h"
#include "pagedao.h"
#include "sqlitedao.h"
class PageDaoSqlite : public PageDao, protected SqliteDao
{
public:
PageDaoSqlite() { }
void deletePage(std::string page) override;
bool exists(int id) const override;
bool exists(std::string name) const override;
void save(const Page &page) override;
std::optional<Page> find(std::string name) override;
std::optional<Page> find(int id) override;
std::vector<std::string> getPageList(QueryOption option) override;
std::vector<std::string> fetchCategories(std::string pagename, QueryOption option) override;
using SqliteDao::SqliteDao;
int fetchPageId(std::string pagename);
std::vector<SearchResult> search(std::string query, QueryOption option) override;
void setCategories(std::string pagename, const std::vector<std::string> &catnames) override;
};
#endif // PAGEDAOSQLITE_H

View File

@ -0,0 +1,26 @@
/* 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 "permissionsdao.h"
PermissionsDao::PermissionsDao()
{
}

12
database/permissionsdao.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef PERMISSIONSDAO_H
#define PERMISSIONSDAO_H
#include "../permissions.h"
#include "../user.h"
class PermissionsDao
{
public:
PermissionsDao();
virtual Permissions find(std::string pagename, std::string username) = 0;
};
#endif // PERMISSIONSDAO_H

View File

@ -0,0 +1,32 @@
/* 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 "permissionsdaosqlite.h"
PermissionsDaoSqlite::PermissionsDaoSqlite()
{
}
Permissions PermissionsDaoSqlite::find(std::string pagename, std::string username)
{
/* auto query = *db << "SELECT COALESCE( (SELECT permissions FROM permissions WHERE page = ? AND userid = ?), (SELECT permissions FROM user WHERE ID = ?))";
exec*/
}

View File

@ -0,0 +1,15 @@
#ifndef PERMISSIONSDAOSQLITE_H
#define PERMISSIONSDAOSQLITE_H
#include "permissionsdao.h"
#include "sqlitedao.h"
class PermissionsDaoSqlite : public PermissionsDao, protected SqliteDao
{
public:
PermissionsDaoSqlite();
Permissions find(std::string pagename, std::string username) override;
using SqliteDao::SqliteDao;
};
#endif // PERMISSIONSDAOSQLITE_H

22
database/queryoption.cpp Normal file
View File

@ -0,0 +1,22 @@
/* 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 "queryoption.h"

19
database/queryoption.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef QUERYOPTION_H
#define QUERYOPTION_H
enum SORT_ORDER
{
ASCENDING=0,
DESCENDING
};
class QueryOption
{
public:
unsigned int offset = 0;
unsigned int limit = 0;
SORT_ORDER order = ASCENDING;
bool includeInvisible = true;
};
#endif // QUERYOPTION_H

22
database/revisiondao.cpp Normal file
View File

@ -0,0 +1,22 @@
/* 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 "revisiondao.h"

22
database/revisiondao.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef REVISIONDAO_H
#define REVISIONDAO_H
#include <vector>
#include <optional>
#include "../revision.h"
#include "queryoption.h"
class RevisionDao
{
public:
virtual void save(const Revision &revision) = 0;
virtual std::vector<Revision> getAllRevisions(QueryOption &options) = 0;
virtual std::vector<Revision> getAllRevisionsForPage(std::string pagename, QueryOption &option) = 0;
virtual std::optional<Revision> getCurrentForPage(std::string pagename) = 0;
virtual std::optional<Revision> getRevisionForPage(std::string pagnename, unsigned int revision) = 0;
virtual unsigned int countTotalRevisions() = 0;
virtual unsigned int countTotalRevisions(std::string pagename) = 0;
virtual ~RevisionDao() { }
};
#endif // REVISIONDAO_H

View File

@ -0,0 +1,168 @@
/* 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 "revisiondaosqlite.h"
#include "exceptions.h"
#include "sqlitequeryoption.h"
#include "../utils.h"
RevisionDaoSqlite::RevisionDaoSqlite()
{
}
void RevisionDaoSqlite::save(const Revision &revision)
{
try
{
*db << "savepoint revisionsubmit;";
*db << "INSERT INTO revision(author, comment, content, creationtime, page, revisionid) VALUES((SELECT id FROM user WHERE username = ?), ?, ?, DATETIME(), (SELECT id FROM page WHERE name = ?), (SELECT lastrevision+1 FROM page WHERE id = (SELECT id FROM page WHERE name = ?)));" <<
revision.author << revision.comment << revision.content << revision.page << revision.page;
*db << "UPDATE page SET lastrevision=lastrevision+1 WHERE name = ?; " << revision.page;
*db << "release revisionsubmit;";
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
std::vector<Revision> RevisionDaoSqlite::getAllRevisions(QueryOption &options)
{
std::vector<Revision> result;
try
{
SqliteQueryOption queryOption { options };
std::string queryOptionSql = queryOption.setPrependWhere(true).setOrderByColumn("id").build();
auto query = *db << "SELECT author, comment, content, strftime('%s',creationtime), (SELECT name FROM page WHERE page.id = page ), revisionid FROM revision " + queryOptionSql;
query >> [&](std::string author, std::string comment, std::string content, time_t creationtime, std::string page, unsigned int revisionid)
{
Revision r;
r.author = author;
r.comment = comment;
r.content = content;
r.timestamp = creationtime;
r.page = page;
r.revision = revisionid;
result.push_back(r);
};
}
catch(const sqlite::errors::no_rows &e)
{
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::vector<Revision> RevisionDaoSqlite::getAllRevisionsForPage(std::string pagename, QueryOption &option)
{
std::vector<Revision> result;
try
{
SqliteQueryOption queryOption { option };
std::string queryOptionSql = queryOption.setPrependWhere(false).setOrderByColumn("id").build();
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), (SELECT name FROM page WHERE page.id = page ), revisionid FROM revision WHERE page = (SELECT id FROM page WHERE name = ?) " + queryOptionSql << pagename;
query >> [&](std::string author, std::string comment, std::string content, time_t creationtime, std::string page, unsigned int revisionid)
{
Revision r;
r.author = author;
r.comment = comment;
r.content = content;
r.timestamp = creationtime;
r.page = page;
r.revision = revisionid;
result.push_back(r);
};
}
catch(const sqlite::errors::no_rows &e)
{
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::optional<Revision> RevisionDaoSqlite::getCurrentForPage(std::string pagename)
{
Revision result;
try
{
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), page, revisionid FROM revision WHERE page = (SELECT id FROM page WHERE name = ? ) AND revisionid = (SELECT lastrevision FROM page WHERE name = ?)";
query << pagename << pagename;
query >> std::tie(result.author, result.comment, result.content, result.timestamp, result.page, result.revision);
}
catch(const sqlite::errors::no_rows &e)
{
return { };
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}
std::optional<Revision> RevisionDaoSqlite::getRevisionForPage(std::string pagename, unsigned int revision)
{
Revision result;
try
{
auto query = *db << "SELECT (SELECT username FROM user WHERE id = author), comment, content, strftime('%s',creationtime), page, revisionid FROM revision WHERE page = (SELECT id FROM page WHERE name = ? ) AND revisionid = ?";
query << pagename << revision;
query >> std::tie(result.author, result.comment, result.content, result.timestamp, result.page, result.revision);
}
catch(const sqlite::exceptions::no_rows &e)
{
return { };
}
return result;
}
unsigned int RevisionDaoSqlite::countTotalRevisions()
{
auto query = *db << "SELECT COUNT(ROWID) FROM revision";
return static_cast<unsigned int>(execInt(query));
}
unsigned int RevisionDaoSqlite::countTotalRevisions(std::string page)
{
auto query = *db << "SELECT COUNT(ROWID) FROM revision WHERE page = (SELECT id FROM page WHERE name = ?)" << page;
return static_cast<unsigned int>(execInt(query));
}

View File

@ -0,0 +1,23 @@
#ifndef REVISIONDAOSQLITE_H
#define REVISIONDAOSQLITE_H
#include <sqlite3.h>
#include "revisiondao.h"
#include "sqlitedao.h"
class RevisionDaoSqlite : public RevisionDao, protected SqliteDao
{
public:
RevisionDaoSqlite();
void save(const Revision &revision) override;
std::vector<Revision> getAllRevisions(QueryOption &options) override;
std::vector<Revision> getAllRevisionsForPage(std::string pagename, QueryOption &option) override;
std::optional<Revision> getCurrentForPage(std::string pagename) override;
std::optional<Revision> getRevisionForPage(std::string pagnename, unsigned int revision) override;
unsigned int countTotalRevisions() override;
unsigned int countTotalRevisions(std::string pagename) override;
using SqliteDao::SqliteDao;
};
#endif // REVISIONDAOSQLITE_H

26
database/sessiondao.cpp Normal file
View File

@ -0,0 +1,26 @@
/* 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 "sessiondao.h"
SessionDao::SessionDao()
{
}

16
database/sessiondao.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef SESSIONDAO_H
#define SESSIONDAO_H
#include <string>
#include <optional>
#include "../session.h"
class SessionDao
{
public:
SessionDao();
virtual void save(const Session &session) = 0;
virtual std::optional<Session> find(std::string token) = 0;
virtual void deleteSession(std::string token) = 0;
virtual ~SessionDao() { }
};
#endif // SESSIONDAO_H

View File

@ -0,0 +1,97 @@
/* 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 "sessiondaosqlite.h"
#include "userdaosqlite.h"
void SessionDaoSqlite::save(const Session &session)
{
try
{
//TODO: we do not store creationtime
auto q = *db << "INSERT OR REPLACE INTO session(id, token, csrf_token, creationtime, userid) VALUES((SELECT id FROM session WHERE token = ?), ?, ?, DATETIME(), (SELECT id FROM user WHERE username = ?))";
q << session.token << session.token << session.csrf_token << session.user.login;
q.execute();
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
void SessionDaoSqlite::deleteSession(std::string token)
{
try
{
auto stmt = *db << "DELETE FROM session WHERE token = ?" << token;
stmt.execute();
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
std::optional<Session> SessionDaoSqlite::find(std::string token)
{
Session result;
try
{
std::string username;
auto q = *db << "SELECT userid, token, csrf_token, strftime('%s', creationtime) FROM session WHERE token = ?" << token;
int userid;
q >> std::tie(userid, result.token, result.csrf_token, result.creation_time);
if(userid > -1)
{
UserDaoSqlite userDao { this-> db };
auto u = userDao.find(userid);
if(u)
{
result.user = *u;
}
else
{
Logger::error() << "Session for non existent user";
throw DatabaseQueryException("Session for non existent user");
}
}
else
{
result.user = User::Anonymous();
}
result.loggedIn = userid != -1;
}
catch(const sqlite::exceptions::no_rows &e)
{
return { };
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
return result;
}

View File

@ -0,0 +1,20 @@
#ifndef SESSIONDAOSQLITE_H
#define SESSIONDAOSQLITE_H
#include "sessiondao.h"
#include "../session.h"
#include "sqlitedao.h"
class SessionDaoSqlite : public SessionDao, protected SqliteDao
{
public:
SessionDaoSqlite();
void save(const Session &session) override;
std::optional<Session> find(std::string token) override;
void deleteSession(std::string token) override;
using SqliteDao::SqliteDao;
};
#endif // SESSIONDAOSQLITE_H

91
database/sqlite.cpp Normal file
View File

@ -0,0 +1,91 @@
/* 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 "sqlite.h"
#include "../logger.h"
#include "pagedaosqlite.h"
#include "revisiondaosqlite.h"
#include "sessiondaosqlite.h"
#include "userdaosqlite.h"
#include "categorydaosqlite.h"
#include "exceptions.h"
Sqlite::Sqlite(std::string path) : Database(path)
{
this->db = std::make_shared<sqlite::database>(path);
*db << "PRAGMA journal_mode=WAL;";
}
std::unique_ptr<RevisionDao> Sqlite::createRevisionDao() const
{
return create<RevisionDaoSqlite>();
}
std::unique_ptr<PageDao> Sqlite::createPageDao() const
{
return create<PageDaoSqlite>();
}
std::unique_ptr<UserDao> Sqlite::createUserDao() const
{
return create<UserDaoSqlite>();
}
std::unique_ptr<SessionDao> Sqlite::createSessionDao() const
{
return create<SessionDaoSqlite>();
}
std::unique_ptr<CategoryDao> Sqlite::createCategoryDao() const
{
return create<CategoryDaoSqlite>();
}
void Sqlite::beginTransaction()
{
if(!inTransaction)
{
*db << "begin;";
inTransaction = true;
}
}
void Sqlite::rollbackTransaction()
{
if(inTransaction)
{
*db << "rollback;";
inTransaction = false;
}
}
void Sqlite::commitTransaction()
{
if(inTransaction)
{
*db << "commit;";
inTransaction = false;
}
}

31
database/sqlite.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef SQLITE_H
#define SQLITE_H
#include <string>
#include <sqlite3.h>
#include <sqlite_modern_cpp.h>
#include "database.h"
class Sqlite : public Database
{
private:
bool inTransaction = false;
std::shared_ptr<sqlite::database> db;
template<class T> std::unique_ptr<T> create() const
{
return std::make_unique<T>(db);
}
public:
Sqlite(std::string path);
std::unique_ptr<PageDao> createPageDao() const;
std::unique_ptr<RevisionDao> createRevisionDao() const;
std::unique_ptr<UserDao> createUserDao() const;
std::unique_ptr<SessionDao> createSessionDao() const;
std::unique_ptr<CategoryDao> createCategoryDao() const;
void beginTransaction();
void commitTransaction();
void rollbackTransaction();
};
#endif // SQLITE_H

52
database/sqlitedao.cpp Normal file
View File

@ -0,0 +1,52 @@
/* 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 "sqlitedao.h"
bool SqliteDao::execBool(sqlite::database_binder &binder) const
{
bool result;
try
{
bool result;
binder >> result;
return result;
}
catch(sqlite::sqlite_exception& e)
{
//TODO: well, we may want to check whether rows have found or not and thus log this here
return false;
}
}
int SqliteDao::execInt(sqlite::database_binder &binder) const
{
try
{
int result;
binder >> result;
return result;
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}

36
database/sqlitedao.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef SQLITEDAO_H
#define SQLITEDAO_H
#include <string_view>
#include <sqlite3.h>
#include <stdarg.h>
#include <sqlite_modern_cpp.h>
#include <sqlite_modern_cpp/errors.h>
#include "queryoption.h"
#include "exceptions.h"
#include "../logger.h"
class SqliteDao
{
protected:
std::shared_ptr<sqlite::database> db;
public:
SqliteDao() { }
SqliteDao(std::shared_ptr<sqlite::database> db) { this->db = db; }
void setDb(std::shared_ptr<sqlite::database> db) { this->db = db; }
inline void throwFrom(const sqlite::sqlite_exception &e) const
{
std::string msg = "Sqlite Error: " + std::to_string(e.get_code()) + " SQL: " + e.get_sql();
Logger::error() << msg << " Extended code: " << e.get_extended_code();
throw DatabaseQueryException(msg);
}
bool execBool(sqlite::database_binder &binder) const;
int execInt(sqlite::database_binder &binder) const;
};
#endif // SQLITEDAO_H

View File

@ -0,0 +1,66 @@
/* 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 "sqlitequeryoption.h"
SqliteQueryOption::SqliteQueryOption(const QueryOption &o) { this->o = o; }
SqliteQueryOption &SqliteQueryOption::setOrderByColumn(std::string name)
{
this->orderByColumnName = name;
return *this;
}
SqliteQueryOption &SqliteQueryOption::setVisibleColumnName(std::string name)
{
this->visibleColumnName = name;
return *this;
}
SqliteQueryOption &SqliteQueryOption::setPrependWhere(bool b) { this->prependWhere = b; return *this; }
std::string SqliteQueryOption::build()
{
std::string result;
if(!o.includeInvisible && ! this->visibleColumnName.empty())
{
if(this->prependWhere)
{
result += "WHERE ";
}
result += this->visibleColumnName + " = 1";
}
result += " ORDER BY " + orderByColumnName;
if(o.order == ASCENDING)
{
result += " ASC";
}
else
{
result += " DESC";
}
//TODO: limits for offset?
if(o.limit > 0 )
result += " LIMIT " + std::to_string(o.limit) + " OFFSET " + std::to_string(o.offset);
return result;
}

View File

@ -0,0 +1,26 @@
#ifndef SQLITEQUERYOPTION_H
#define SQLITEQUERYOPTION_H
#include <string>
#include "queryoption.h"
class SqliteQueryOption
{
private:
QueryOption o;
std::string visibleColumnName;
std::string orderByColumnName;
bool prependWhere;
public:
SqliteQueryOption(const QueryOption &o);
SqliteQueryOption &setOrderByColumn(std::string name);
SqliteQueryOption &setVisibleColumnName(std::string name);
SqliteQueryOption &setPrependWhere(bool b);
std::string build();
};
#endif // SQLITEQUERYOPTION_H

26
database/userdao.cpp Normal file
View File

@ -0,0 +1,26 @@
/* 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 "userdao.h"
UserDao::UserDao()
{
}

20
database/userdao.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef USERDAO_H
#define USERDAO_H
#include <string>
#include <optional>
#include "../user.h"
class UserDao
{
public:
UserDao();
virtual bool exists(std::string username) = 0;
virtual std::optional<User> find(std::string username) = 0;
virtual std::optional<User> find(int id) = 0;
virtual void deleteUser(std::string username) = 0;
virtual void save(const User &u) = 0;
virtual ~UserDao() { };
};
#endif // USERDAO_H

View File

@ -0,0 +1,93 @@
/* 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 <sqlite3.h>
#include <string>
#include <memory>
#include <cstring>
#include "userdaosqlite.h"
UserDaoSqlite::UserDaoSqlite()
{
}
bool UserDaoSqlite::exists(std::string username)
{
auto prep = *db << "SELECT 1 FROM user WHERE username = ?" << username;
return execBool(prep);
}
std::optional<User> UserDaoSqlite::find(std::string username)
{
try
{
User user;
auto stmt = *db << "SELECT username, password, salt, permissions FROM user WHERE username = ?" << username;
int perms = 0;
stmt >> std::tie(user.login, user.password, user.salt, perms);
user.permissions = Permissions { perms };
return std::move(user);
}
catch(const sqlite::errors::no_rows &e)
{
return { };
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
std::optional<User> UserDaoSqlite::find(int id)
{
try
{
User user;
auto stmt = *db << "SELECT username, password, salt, permissions FROM user WHERE id = ?" << id;
int perms = 0;
stmt >> std::tie(user.login, user.password, user.salt, perms);
user.permissions = Permissions { perms };
return std::move(user);
}
catch(const sqlite::errors::no_rows &e)
{
return { };
}
catch(sqlite::sqlite_exception &e)
{
throwFrom(e);
}
}
void UserDaoSqlite::deleteUser(std::string username)
{
//What to do with the contributions of the user?
}
void UserDaoSqlite::save(const User &u)
{
}

22
database/userdaosqlite.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef USERDAOSQLITE_H
#define USERDAOSQLITE_H
#include <string>
#include <optional>
#include <vector>
#include "userdao.h"
#include "sqlitedao.h"
class UserDaoSqlite : public UserDao, protected SqliteDao
{
public:
bool exists(std::string username);
std::optional<User> find(std::string username);
std::optional<User> find(int id);
void deleteUser(std::string username);
void save(const User &u);
using SqliteDao::SqliteDao;
UserDaoSqlite();
};
#endif // USERDAOSQLITE_H

115
gateway/cgi.cpp Normal file
View File

@ -0,0 +1,115 @@
/* 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 "cgi.h"
#include "../utils.h"
#include <cstdlib>
#include <string>
#include <memory>
#include <iostream>
Cgi::Cgi(const Config &c)
{
this->config = &c;
}
bool Cgi::keepReading()
{
return !this->responseSent;
}
Request Cgi::readRequest()
{
std::string request_uri = utils::getenv("REQUEST_URI");
if(request_uri == "")
{
throw std::runtime_error("REQUEST_URI is empty");
}
Request result { request_uri };
std::string method = utils::getenv("REQUEST_METHOD");
if(method == "POST")
{
std::string content_type = utils::getenv("CONTENT_TYPE");
if(content_type != "application/x-www-form-urlencoded")
{
throw "invalid content_type";
}
std::string content_length = utils::getenv("CONTENT_LENGTH");
int cl = std::stoi(content_length);
std::unique_ptr<char[]> ptr(new char[cl+1]);
std::cin.get(ptr.get(), cl+1);
std::string post_data { ptr.get() };
}
result.initCookies(utils::getenv("HTTP_COOKIE"));
result.setIp(utils::getenv("REMOTE_ADDR"));
result.setUseragent(utils::getenv("HTTP_USER_AGENT"));
return result;
}
void Cgi::work(RequestWorker &worker)
{
while(this->keepReading())
{
Request req = readRequest();
sendResponse(worker.processRequest(req));
}
}
void Cgi::sendResponse(const Response &r)
{
std::cout << "Status: " << r.getStatus() << "\r\n";
std::cout << "Content-Type: " << r.getContentType() <<"\r\n";
for(auto header : r.getResponseHeaders())
{
std::string key = header.first;
std::string second = header.second;
if(key.back() != ':')
{
std::cout << key << ":" << second << "\r\n";
}
else
{
std::cout << key << second << "\r\n";
}
}
for(const Cookie &c : r.getCookies())
{
std::cout << "Set-Cookie: " << c.createHeaderValue() << "\r\n";
}
std::cout << "\r\n";
std::cout << r.getBody();
std::cout.flush();
this->responseSent = true;
}
Cgi::~Cgi()
{
}

21
gateway/cgi.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CGI_H
#define CGI_H
#include "gatewayinterface.h"
#include "../requestworker.h"
class Cgi : public GatewayInterface
{
private:
bool responseSent = false;
const Config *config;
Request readRequest();
void sendResponse(const Response &r);
public:
Cgi(const Config &c);
bool keepReading() override;
void work(RequestWorker &worker) override;
~Cgi();
};
#endif // CGI_H

View File

@ -0,0 +1,29 @@
/* 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 "gatewayfactory.h"
#include "cgi.h"
#include "httpgateway.h"
std::unique_ptr<GatewayInterface> createGateway(const Config &c)
{
return std::make_unique<HttpGateway>(c);
}

8
gateway/gatewayfactory.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef GATEWAYFACTORY_H
#define GATEWAYFACTORY_H
#include <memory>
#include "../config.h"
#include "gatewayinterface.h"
std::unique_ptr<GatewayInterface> createGateway(const Config &c);
#endif // GATEWAYFACTORY_H

View File

@ -0,0 +1,26 @@
/* 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 "gatewayinterface.h"
GatewayInterface::GatewayInterface()
{
}

View File

@ -0,0 +1,16 @@
#ifndef GATEWAYINTERFACE_H
#define GATEWAYINTERFACE_H
#include "../request.h"
#include "../response.h"
#include "../config.h"
#include "../requestworker.h"
class GatewayInterface
{
public:
GatewayInterface();
virtual bool keepReading() = 0;
virtual void work(RequestWorker &worker) = 0;
virtual ~GatewayInterface() { }
};
#endif // GATEWAYINTERFACE_H

95
gateway/httpgateway.cpp Normal file
View File

@ -0,0 +1,95 @@
/* 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 "httpgateway.h"
#include "../logger.h"
HttpGateway::HttpGateway(const Config &config)
{
}
bool HttpGateway::keepReading()
{
return true;
}
Request HttpGateway::convertRequest(httplib::Request request)
{
Request result;
result.setRequestMethod(request.method);
result.setUrl(request.target);
//TODO: this eats resources, where perhaps it does not need to. move it to request?
for (auto &it : request.params)
{
it.second = utils::html_xss(std::string { it.second });
}
if(request.method == "GET")
{
result.setGetVars(request.params);
}
else
{
result.initGetMap(request.target);
result.setPostVars(request.params);
}
if(request.has_header("COOKIE"))
{
result.initCookies(request.get_header_value("COOKIE"));
}
result.setIp(request.get_header_value("REMOTE_ADDR"));
return result;
}
httplib::Response HttpGateway::convertResponse(Response response)
{
httplib::Response result;
result.set_content(response.getBody(), response.getContentType().c_str());
result.status = response.getStatus();
for(auto &header : response.getResponseHeaders())
{
result.set_header(header.first.c_str(), header.second.c_str());
}
for(const Cookie &cookie : response.getCookies())
{
result.set_header("Set-Cookie", cookie.createHeaderValue().c_str());
}
return result;
}
void HttpGateway::work(RequestWorker &worker)
{
httplib::Server server;
auto handler = [&](const httplib::Request& req, httplib::Response& res) {
Request wikiRequest = convertRequest(req);
Logger::debug() << "httpgateway: received request " << wikiRequest;
Response wikiresponse = worker.processRequest(wikiRequest);
res = convertResponse(wikiresponse);
};
server.Get("/(.*)", handler);
server.Post("/(.*)", handler);
server.listen("localhost", 1234);
}

22
gateway/httpgateway.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef HTTPGATEWAY_H
#define HTTPGATEWAY_H
#include "httplib.h"
#include "gatewayinterface.h"
#include "../requestworker.h"
#include "../request.h"
#include "../response.h"
#include "../utils.h"
class HttpGateway : public GatewayInterface
{
private:
Response convertResponse(httplib::Response response);
httplib::Response convertResponse(Response response);
Request convertRequest(httplib::Request request);
// void worker(const httplib::Request& req, httplib::Response& res);
public:
HttpGateway(const Config &config);
bool keepReading() override;
void work(RequestWorker &worker) override;
};
#endif // HTTPGATEWAY_H

2395
gateway/httplib.h Normal file

File diff suppressed because it is too large Load Diff

79
handlers/handler.cpp Normal file
View File

@ -0,0 +1,79 @@
/* 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 "handler.h"
void Handler::setGeneralVars(TemplatePage &page)
{
if(userSession->loggedIn)
{
page.setVar("loginstatus", "Logged in as " + userSession->user.login);
}
else
{
page.setVar("loginstatus", "not logged in");
}
page.setVar("csrf_token", utils::toString(this->userSession->csrf_token));
}
Response Handler::errorResponse(std::string errortitle, std::string errormessage, int status)
{
TemplatePage &error = this->templ->getPage("error");
error.setVar("errortitle", errortitle);
error.setVar("errormessage", errormessage);
//TODO: log?
setGeneralVars(error);
return { status, error.render()};
}
QueryOption Handler::queryOption(const Request &r) const
{
QueryOption result;
result.includeInvisible = false;
try
{
result.limit = utils::toUInt(r.get("limit"));
}
catch(std::exception &e)
{
result.limit = 0;
}
try
{
result.offset = utils::toUInt(r.get("offset"));
}
catch(std::exception &e)
{
result.offset = 0;
}
std::string order = r.get("sort");
if(order == "0")
{
result.order = ASCENDING;
}
else
{
result.order = DESCENDING;
}
return result;
}

37
handlers/handler.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef HANDLER_H
#define HANDLER_H
#include "../response.h"
#include "../request.h"
#include "../template.h"
#include "../database/database.h"
#include "../urlprovider.h"
#include "../database/queryoption.h"
#include "../logger.h"
#include "../cache/icache.h"
class Handler
{
protected:
ICache *cache;
Template *templ;
Database *database;
Session *userSession;
UrlProvider *urlProvider;
QueryOption queryOption(const Request &r) const;
public:
Handler(Template &templ, Database &db, Session &userSession, UrlProvider &provider, ICache &cache)
{
this->templ = &templ;
this->database = &db;
this->userSession = &userSession;
this->urlProvider = &provider;
this->cache = &cache;
}
virtual Response handle(const Request &r) = 0;
void setGeneralVars(TemplatePage &page);
virtual ~Handler() { }
Response errorResponse(std::string errortitle, std::string errormessage, int status = 200);
};
#endif // HANDLER_H

View File

@ -0,0 +1,44 @@
/* 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 "handlerallcategories.h"
#include "../urlprovider.h"
#include "../logger.h"
Response HandlerAllCategories::handle(const Request &r)
{
auto categoryDao = this->database->createCategoryDao();
QueryOption qo = queryOption(r);
auto resultList = categoryDao->fetchList(qo);
if(resultList.size() == 0)
{
return errorResponse("No categories", "This wiki does not have any categories defined yet or your query options did not yield any results");
}
TemplatePage &searchPage = this->templ->getPage("allcategories");
std::string body = this->templ->renderSearch(resultList, [&](std::string str) {
return this->urlProvider->category(str);
});
searchPage.setVar("categorylist", body);
setGeneralVars(searchPage);
Response response;
response.setBody(searchPage.render());
response.setStatus(200);
return response;
}

View File

@ -0,0 +1,15 @@
#ifndef HANDLERALLCATEGORIES_H
#define HANDLERALLCATEGORIES_H
#include "handler.h"
class HandlerAllCategories : public Handler
{
public:
HandlerAllCategories();
using Handler::Handler;
Response handle(const Request &r) override;
};
#endif // HANDLERALLCATEGORIES_H

View File

@ -0,0 +1,51 @@
/* 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 "handlerallpages.h"
Response HandlerAllPages::handle(const Request &r)
{
try
{
Response response;
auto pageDao = this->database->createPageDao();
QueryOption qo = queryOption(r);
auto resultList = pageDao->getPageList(qo);
if(resultList.size() == 0)
{
return errorResponse("No pages", "This wiki does not have any pages yet");
}
TemplatePage &searchPage = this->templ->getPage("allpages");
std::string body = this->templ->renderSearch(resultList);
searchPage.setVar("pagelist", body);
setGeneralVars(searchPage);
response.setBody(searchPage.render());
response.setStatus(200);
return response;
}
catch(std::exception &e)
{
Logger::error() << "Error during allpages Handler" << e.what();
return errorResponse("Error", "An unknown error occured");
}
}

View File

@ -0,0 +1,13 @@
#ifndef HANDLERALLPAGES_H
#define HANDLERALLPAGES_H
#include "handler.h"
class HandlerAllPages : public Handler
{
public:
HandlerAllPages();
using Handler::Handler;
Response handle(const Request &r) override;
};
#endif // HANDLERALLPAGES_H

View File

@ -0,0 +1,51 @@
/* 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 "handlercategory.h"
Response HandlerCategory::handle(const Request &r)
{
try
{
Response response;
std::string categoryname = r.get("category");
auto categoryDao = this->database->createCategoryDao();
if(! categoryDao->find(categoryname))
{
return this->errorResponse("No such category", "A category with the provided name does not exist", 404);
}
QueryOption qo = queryOption(r);
auto resultList = categoryDao->fetchMembers(categoryname, qo);
TemplatePage &searchPage = this->templ->getPage("show_category");
std::string body = this->templ->renderSearch(resultList);
searchPage.setVar("pagelist", body);
searchPage.setVar("categoryname", categoryname);
setGeneralVars(searchPage);
response.setBody(searchPage.render());
response.setStatus(200);
return response;
}
catch(std::exception &e)
{
Logger::error() << "Error during category Handler" << e.what();
return errorResponse("Error", "An unknown error occured");
}
}

View File

@ -0,0 +1,13 @@
#ifndef HANDLERCATEGORY_H
#define HANDLERCATEGORY_H
#include "handler.h"
class HandlerCategory : public Handler
{
public:
HandlerCategory();
using Handler::Handler;
Response handle(const Request &r) override;
};
#endif // HANDLERCATEGORY_H

View File

@ -0,0 +1,32 @@
/* 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 "handlerdefault.h"
Response HandlerDefault::handle(const Request &r)
{
return Response::redirectTemporarily(this->urlProvider->index());
}
HandlerDefault::~HandlerDefault()
{
}

13
handlers/handlerdefault.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef HANDLERDEFAULT_H
#define HANDLERDEFAULT_H
#include "handler.h"
class HandlerDefault : public Handler
{
public:
Response handle(const Request &r) override;
~HandlerDefault() override;
using Handler::Handler;
};
#endif // HANDLERDEFAULT_H

101
handlers/handlerfactory.cpp Normal file
View File

@ -0,0 +1,101 @@
/* 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 "handlerfactory.h"
#include "handler.h"
#include "handlerdefault.h"
#include "handlerpageview.h"
#include "handlerinvalidaction.h"
#include "handlerlogin.h"
#include "handlerpageedit.h"
#include "handlersearch.h"
#include "handlerallpages.h"
#include "handlerallcategories.h"
#include "handlercategory.h"
#include "handlerhistory.h"
#include "handlerpagedelete.h"
class Factory
{
Template &templ;
Database &db;
Session &userSession;
UrlProvider &urlProvider;
ICache &cache;
public:
Factory(Template &templ, Database &db, Session &usersession, UrlProvider &urlprovider, ICache &cache) : templ(templ) ,db(db), userSession(usersession), urlProvider(urlprovider), cache(cache) { }
template<class T>
inline std::unique_ptr<T> produce()
{
return std::make_unique<T>(templ, db, userSession, urlProvider, cache);
}
};
std::unique_ptr<Handler> createHandler(const std::string &action, Template &templ, Database
&db, Session &usersession, UrlProvider &urlprovider, ICache &cache)
{
Factory producer(templ, db, usersession, urlprovider, cache);
if(action == "" || action == "index")
{
return producer.produce<HandlerDefault>();
}
if(action == "show")
{
return producer.produce<HandlerPageView>();
}
if(action == "edit")
{
return producer.produce<HandlerPageEdit>();
}
if(action == "login")
{
return producer.produce<HandlerLogin>();
}
if(action == "search")
{
return producer.produce<HandlerSearch>();
}
if(action == "delete")
{
return producer.produce<HandlerPageDelete>();
}
if(action == "allpages")
{
return producer.produce<HandlerAllPages>();
}
if(action == "allcategories")
{
return producer.produce<HandlerAllCategories>();
}
if(action == "showcat")
{
return producer.produce<HandlerCategory>();
}
if(action == "recent")
{
return producer.produce<HandlerHistory>();
}
return producer.produce<HandlerInvalidAction>();
}

View File

@ -0,0 +1,8 @@
#ifndef HANDLERFACTORY_H
#define HANDLERFACTORY_H
#include <memory>
#include "handler.h"
#include "../template.h"
std::unique_ptr<Handler> createHandler(const std::string &action, Template &templ, Database &db, Session &usersession, UrlProvider &urlprovider, ICache &cache);
#endif // HANDLERFACTORY_H

106
handlers/handlerhistory.cpp Normal file
View File

@ -0,0 +1,106 @@
/* 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 "handlerhistory.h"
#include "handler.h"
#include "../htmllink.h"
#include "../logger.h"
#include "../database/exceptions.h"
Response HandlerHistory::handle(const Request &r)
{
QueryOption qo = queryOption(r);
std::string page = r.get("page");
unsigned int count = 0;
std::vector<Revision> resultList;
auto revisionDao = this->database->createRevisionDao();
auto makeSortedLink = [&](unsigned int limit, unsigned int offset, unsigned int order)
{
if(!page.empty())
{
return this->urlProvider->pageHistorySort(page, limit, offset, order);
}
return this->urlProvider->recentSorted(limit, offset, order);
};
std::string templatename = "recentchanges";
try
{
if(!page.empty())
{
auto pageDao = this->database->createPageDao();
if(!pageDao->exists(page))
{
return errorResponse("No such page", "No such page exists to show history for", 404);
}
count = revisionDao->countTotalRevisions(page);
resultList = revisionDao->getAllRevisionsForPage(page, qo);
templatename = "page_history";
}
else
{
count = revisionDao->countTotalRevisions();
if(count == 0)
{
return errorResponse("No revisions", "This wiki does not have any pages with revisions yet");
}
resultList = revisionDao->getAllRevisions(qo);
}
}
catch(const DatabaseException &e)
{
Logger::error() << "DatabaseException in handlerhistory: " << e.what();
return errorResponse("Database error", "While trying to fetch revision list, a database error occured");
}
TemplatePage historyPage = this->templ->getPage(templatename);
setGeneralVars(historyPage);
if( (qo.offset + (unsigned int)resultList.size()) < count)
{
HtmlLink link;
link.href = makeSortedLink(qo.limit, qo.offset + qo.limit, qo.order);
link.innervalue = "Next page";
historyPage.setVar("nextpage", link.render());
}
unsigned int prevoffset = qo.offset - qo.limit;
if(prevoffset > count)
{
prevoffset = 0;
}
if(qo.offset > 0 && qo.offset < count)
{
HtmlLink link;
link.href = makeSortedLink(qo.limit, prevoffset, qo.order);
link.innervalue = "Previous page";
historyPage.setVar("prevpage", link.render());
}
unsigned int neworder = ( qo.order == DESCENDING ) ? ASCENDING : DESCENDING ;
historyPage.setVar("linkrecentsort", makeSortedLink(qo.limit, qo.offset, neworder));
historyPage.setVar("revisionlist", this->templ->renderRevisionList(resultList, page.empty()));
Response response;
response.setBody(historyPage.render());
response.setStatus(200);
return response;
}

14
handlers/handlerhistory.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef HANDLERHISTORY_H
#define HANDLERHISTORY_H
#include "handler.h"
class HandlerHistory : public Handler
{
public:
HandlerHistory();
using Handler::Handler;
Response handle(const Request &r) override;
};
#endif // HANDLERHISTORY_H

View File

@ -0,0 +1,27 @@
/* 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 "handlerinvalidaction.h"
Response HandlerInvalidAction::handle(const Request &r)
{
return errorResponse("Invalid action", "No action defined for this action");
}

View File

@ -0,0 +1,14 @@
#ifndef HANDLERINVALIDACTION_H
#define HANDLERINVALIDACTION_H
#include "handler.h"
class HandlerInvalidAction : public Handler
{
public:
Response handle(const Request &r) override;
~HandlerInvalidAction() override { }
using Handler::Handler;
};
#endif // HANDLERINVALIDACTION_H

123
handlers/handlerlogin.cpp Normal file
View File

@ -0,0 +1,123 @@
/* 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 <openssl/evp.h>
#include "handlerlogin.h"
#include "../logger.h"
struct LoginFail
{
unsigned int count;
time_t lastfail;
};
static std::map<std::string, LoginFail> loginFails;
//TODO: make configurable
bool HandlerLogin::isBanned(std::string ip)
{
if(utils::hasKey(loginFails, ip))
{
LoginFail &fl = loginFails[ip];
return fl.count > 5 && (time(nullptr) - fl.lastfail) < 1200;
}
return false;
}
void HandlerLogin::incFailureCount(std::string ip)
{
LoginFail &fl = loginFails[ip];
fl.count+=1;
fl.lastfail = time(nullptr);
}
std::vector<char> HandlerLogin::pbkdf5(std::string password, const std::vector<char> &salt)
{
unsigned char hash[32];
const EVP_MD *sha256 = EVP_sha256();
const unsigned char *rawsalt = reinterpret_cast<const unsigned char *>(salt.data());
PKCS5_PBKDF2_HMAC(password.c_str(), password.size(), rawsalt, salt.size(), 300000, sha256, sizeof(hash), hash);
std::vector<char> result;
for(size_t i = 0; i < sizeof(hash); i++)
{
result.push_back(static_cast<char>(hash[i]));
}
return result;
}
Response HandlerLogin::handle(const Request &r)
{
auto createErrorReesponse = [&]() { return errorResponse("Login error", "The supplied credenetials are incorrect"); };
if(isBanned(r.getIp()))
{
return errorResponse("Banned", "You have been banned for too many login attempts. Try again later");
}
if(r.param("submit") == "1")
{
std::string password = r.post("password");
std::string username = r.post("user");
auto userDao = this->database->createUserDao();
std::optional<User> user = userDao->find(username);
if(!user)
{
return createErrorReesponse();
}
auto hashresult = pbkdf5(password, user.value().salt);
//TODO: timing attack
if(hashresult == user.value().password)
{
loginFails.erase(r.getIp());
Response r = Response::redirectTemporarily(urlProvider->index());
*(this->userSession) = Session(user.value());
return r;
}
else
{
//TODO: only if wanted by config
incFailureCount(r.getIp());
return createErrorReesponse();
}
// auto pbkdf5 = pbkdf5(password, user->)
}
std::string page = r.get("page");
if(page.empty())
page = "index";
TemplatePage &loginTemplatePage = this->templ->getPage("login");
setGeneralVars(loginTemplatePage);
loginTemplatePage.setVar("loginurl", urlProvider->login(page));
Response result;
result.setStatus(200);
result.setBody(loginTemplatePage.render());
return result;
}

19
handlers/handlerlogin.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef HANDLERLOGIN_H
#define HANDLERLOGIN_H
#include <vector>
#include "handler.h"
class HandlerLogin : public Handler
{
private:
bool isBanned(std::string ip);
void incFailureCount(std::string ip);
std::vector<char> pbkdf5(std::string password, const std::vector<char> &salt);
public:
HandlerLogin();
Response handle(const Request &r) override;
~HandlerLogin() override { }
using Handler::Handler;
};
#endif // HANDERLOGIN_H

94
handlers/handlerpage.cpp Normal file
View File

@ -0,0 +1,94 @@
/* 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 "handlerpage.h"
Response HandlerPage::handle(const Request &r)
{
std::string pagename = r.get("page");
auto pageDao = this->database->createPageDao();
if(pagename.empty())
{
return errorResponse("No page given", "No page given to request");
}
if(pageMustExist() && !pageDao->exists(pagename))
{
std::string createlink = this->urlProvider->editPage(pagename);
return errorResponse("Page not found", "The requested page was not found. Do you want to <a href=\"" + createlink + "\">create</a> it?", 404);
}
if(!canAccess(pagename))
{
return errorResponse("Permission denied", accessErrorMessage());
}
return this->handleRequest(*pageDao, pagename, r);
}
std::string HandlerPage::accessErrorMessage()
{
return "You don't have permission to access this page";
}
bool HandlerPage::pageMustExist()
{
return true;
}
void HandlerPage::setPageVars(TemplatePage &page, std::string pagename)
{
setGeneralVars(page);
if(!pagename.empty())
{
std::string headerlinks;
TemplatePage &headerlink = this->templ->getPage("_headerlink");
auto addHeaderLink = [&headerlinks, &headerlink](std::string href, std::string value)
{
headerlink.setVar("href", href);
headerlink.setVar("value", value);
headerlinks += headerlink.render();
};
Permissions &perms = this->userSession->user.permissions;
if(perms.canEdit())
{
addHeaderLink(this->urlProvider->editPage(pagename), "Edit");
addHeaderLink(this->urlProvider->pageSettings(pagename), "Page settings");
}
if(perms.canDelete())
{
addHeaderLink(this->urlProvider->pageDelete(pagename), "Delete");
}
if(perms.canSeePageHistory())
{
addHeaderLink(this->urlProvider->pageHistory(pagename), "Show history");
}
page.setVar("headerlinks", headerlinks);
page.setVar("page", pagename);
}
}

21
handlers/handlerpage.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef HANDLERPAGE_H
#define HANDLERPAGE_H
#include "handler.h"
class HandlerPage : public Handler
{
protected:
virtual bool canAccess(std::string page) = 0;
virtual bool pageMustExist();
virtual std::string accessErrorMessage();
public:
Response handle(const Request &r) override;
virtual Response handleRequest(PageDao &pageDao, std::string pagename, const Request &r) =0;
~HandlerPage() override { }
using Handler::Handler;
void setPageVars(TemplatePage &page, std::string pagename);
};
#endif // HANDLERPAGE_H

View File

@ -0,0 +1,51 @@
/* 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 "handlerpagedelete.h"
#include "../database/exceptions.h"
Response HandlerPageDelete::handleRequest(PageDao &pageDao, std::string pagename, const Request &r)
{
try
{
if(r.getRequestMethod() == "POST")
{
pageDao.deletePage(pagename);
this->cache->removePrefix("page:"); //TODO: overkill?
return Response::redirectTemporarily(this->urlProvider->index());
}
TemplatePage delPage = this->templ->getPage("page_deletion");
delPage.setVar("deletionurl", this->urlProvider->pageDelete(pagename));
setPageVars(delPage, pagename);
Response r;
r.setBody(delPage.render());
return r;
}
catch(const DatabaseException &e)
{
Logger::debug() << "Error delete page: " << e.what();
return errorResponse("Database error", "A database error occured while trying to delete this page");
}
}

View File

@ -0,0 +1,26 @@
#ifndef HANDLERPAGEDELETE_H
#define HANDLERPAGEDELETE_H
#include "handlerpage.h"
class HandlerPageDelete : public HandlerPage
{
bool pageMustExist() override
{
return true;
}
bool canAccess(std::string page) override
{
return this->userSession->user.permissions.canDelete();
}
std::string accessErrorMessage() override
{
return "You don't have permission to delete pages";
}
public:
Response handleRequest(PageDao &pageDao, std::string pagename, const Request &r) override;
using HandlerPage::HandlerPage;
};
#endif // HANDLERPAGEDELETE_H

View File

@ -0,0 +1,122 @@
/* 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 "handlerpageedit.h"
#include "../database/exceptions.h"
#include "../request.h"
#include "../parser.h"
bool HandlerPageEdit::canAccess(std::string page)
{
return this->userSession->user.permissions.canEdit();
}
bool HandlerPageEdit::pageMustExist()
{
return false;
}
Response HandlerPageEdit::handleRequest(PageDao &pageDao, std::string pagename, const Request &r)
{
bool pageexists = pageDao.exists(pagename);
if(!pageexists && !this->userSession->user.permissions.canCreate())
{
return errorResponse("No permission", "You don't have permission to create new pages");
}
auto revisiondao = this->database->createRevisionDao();
auto revision = this->database->createRevisionDao()->getCurrentForPage(pagename);
std::string body;
if(revision)
{
body = revision->content;
}
if(r.getRequestMethod() == "POST")
{
if(r.post("do") == "submit")
{
std::string newContent = r.post("content");
std::string newComment = r.post("comment");
Revision newRevision;
newRevision.author = this->userSession->user.login;
newRevision.comment = newComment;
newRevision.page = pagename;
newRevision.content = newContent;
//TODO: must check, whether categories differ, and perhaps don't allow every user
//to set categories
Parser parser;
std::vector<std::string> cats = parser.extractCategories(newContent);
try
{
this->database->beginTransaction();
if(!pageexists)
{
Page newPage;
newPage.current_revision = 0;
newPage.listed = true;
newPage.name = pagename;
pageDao.save(newPage);
}
revisiondao->save(newRevision);
pageDao.setCategories(pagename, cats);
this->database->commitTransaction();
this->cache->removePrefix("page:"); //TODO: overkill?
}
catch(const DatabaseException &e)
{
Logger::debug() << "Error saving revision: " << e.what();
return errorResponse("Database error", "A database error occured while trying to save this revision");
}
return Response::redirectTemporarily(urlProvider->page(pagename));
}
if(r.post("do") == "preview")
{
std::string newContent = r.post("content");
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) );
templatePage.setVar("content", newContent);
setPageVars(templatePage, pagename);
Response response;
response.setBody(templatePage.render());
return response;
}
}
TemplatePage &templatePage = this->templ->getPage("page_creation");
templatePage.setVar("actionurl", urlProvider->editPage(pagename));
templatePage.setVar("content", body);
setPageVars(templatePage, pagename);
Response response;
response.setBody(templatePage.render());
return response;
}

View File

@ -0,0 +1,20 @@
#ifndef HANDLERPAGEEDI_H
#define HANDLERPAGEEDI_H
#include "handlerpage.h"
#include "../page.h"
class HandlerPageEdit : public HandlerPage
{
protected:
bool pageMustExist() override;
bool canAccess(std::string page) override;
public:
Response handleRequest(PageDao &pageDao, std::string pagename, const Request &r) override;
~HandlerPageEdit() override { }
using HandlerPage::HandlerPage;
};
#endif // HANDLERPAGEEDI_H

View File

@ -0,0 +1,170 @@
/* 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 "handlerpageview.h"
#include "../database/exceptions.h"
#include "../logger.h"
#include "../parser.h"
#include "../htmllink.h"
bool HandlerPageView::canAccess(std::string page)
{
return this->userSession->user.permissions.canRead();
}
std::string HandlerPageView::createIndexContent(IParser &parser, std::string content)
{
std::vector<Headline> headlines = parser.extractHeadlines(content);
std::string indexcontent = "";
unsigned int l = 0;
for(const Headline &h : headlines)
{
if(h.level > l)
{
indexcontent += "<ul>";
}
else if(h.level < l)
{
indexcontent += "</ul>";
}
l = h.level;
HtmlLink link;
link.href="#" + h.title;
link.innervalue = h.title;
link.cssclass = "indexlink";
indexcontent += "<li>"+link.render()+"</li>";
}
indexcontent += "</ul>";
return indexcontent;
}
Response HandlerPageView::handleRequest(PageDao &pageDao, std::string pagename, const Request &r)
{
std::string revisionparam = r.get("revision");
unsigned int revisionid=0;
if(!revisionparam.empty())
{
try
{
revisionid = utils::toUInt(revisionparam);
}
catch(const std::exception &e)
{
return errorResponse("Error", "Supplied revisionid is misformated");
}
}
std::optional<Revision> revision;
std::string templatepartname;
try
{
if(revisionid > 0 )
{
revision = this->database->createRevisionDao()->getRevisionForPage(pagename, revisionid);
if(!revision)
{
return errorResponse("Revision not found", "No such revision found");
}
templatepartname = "page_view_revision";
}
else
{
if(! this->userSession->loggedIn)
{
auto content = this->cache->get("page:foranon:" + pagename);
if(content)
{
Response r;
r.setBody(*content);
//TODO: etag?
return r;
}
}
revision = this->database->createRevisionDao()->getCurrentForPage(pagename);
templatepartname = "page_view";
}
}
catch(const DatabaseException &e)
{
Logger::error() << "DatabaseException in handlerpageview: " << e.what();
return errorResponse("Database error", "While trying to fetch revision, a database error occured");
}
TemplatePage &page = this->templ->getPage(templatepartname);
Parser parser;
Response result;
result.setStatus(200);
std::string indexcontent;
std::string parsedcontent;
if(revisionid > 0)
{
indexcontent = createIndexContent(parser, revision->content);
parsedcontent = parser.parse(pageDao, *this->urlProvider, revision->content);
}
else
{
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);
}
}
page.setVar("content", parsedcontent);
page.setVar("index", indexcontent);
page.setVar("editedby", revision->author );
page.setVar("editedon", utils::toISODate(revision->timestamp));
page.setVar("historyurl", this->urlProvider->pageHistory(pagename));
page.setVar("revision", revisionparam);
setPageVars(page, pagename);
std::string body = page.render();
if(revisionid == 0 && ! this->userSession->loggedIn)
{
this->cache->put("page:foranon:" + pagename, body);
}
result.addHeader("ETAG", std::to_string(revision->revision)+ "+" + std::to_string(this->userSession->loggedIn));
result.setBody(body);
return result;
}

View File

@ -0,0 +1,25 @@
#ifndef HANDLERPAGEVIEW_H
#define HANDLERPAGEVIEW_H
#include "handler.h"
#include "handlerpage.h"
#include "../page.h"
#include "../iparser.h"
class HandlerPageView : public HandlerPage
{
protected:
bool canAccess(std::string page) override;
std::string accessErrorMessage() override
{
return "You don't have permission to view this page";
}
std::string createIndexContent(IParser &parser, std::string content);
public:
Response handleRequest(PageDao &pageDao, std::string pagename, const Request &r) override;
~HandlerPageView() override { }
using HandlerPage::HandlerPage;
};
#endif // HANDLERPAGEVIEW_H

View File

@ -0,0 +1,63 @@
/* 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 "handlersearch.h"
Response HandlerSearch::handle(const Request &r)
{
Response response;
std::string q = r.get("q");
if(q.empty())
{
return errorResponse("Missing search term", "No search term supplied");
}
for(int x : q)
{
if(!isalnum(x) && !isspace(x))
{
return errorResponse("Invalid char", "Currently, the search is limited and so only supports alpha numeric characters and spaces");
}
}
auto pageDao = this->database->createPageDao();
QueryOption qo = queryOption(r);
try
{
auto resultList = pageDao->search(q, qo);
if(resultList.size() == 0)
{
return errorResponse("No results", "Your search for " + q + " did not yield any results.");
}
TemplatePage &searchPage = this->templ->getPage("search");
std::string body = this->templ->renderSearch(resultList);
searchPage.setVar("pagelist", body);
searchPage.setVar("searchterm", q);
setGeneralVars(searchPage);
response.setBody(searchPage.render());
response.setStatus(200);
return response;
}
catch(std::exception &e)
{
Logger::error() << "Search failed, q: " << q << "Error: " << e.what();
return errorResponse("Technical Error", "The system failed to perform your search");
}
}

13
handlers/handlersearch.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef HANDLERSEARCH_H
#define HANDLERSEARCH_H
#include <vector>
#include "handler.h"
class HandlerSearch : public Handler
{
public:
HandlerSearch();
using Handler::Handler;
Response handle(const Request &r) override;
};
#endif // HANDLERSEARCH_H

21
headline.cpp Normal file
View File

@ -0,0 +1,21 @@
/* 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 "headline.h"

12
headline.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef HEADLINE_H
#define HEADLINE_H
#include <string>
class Headline
{
public:
unsigned int level;
std::string title;
};
#endif // HEADLINE_H

26
htmllink.cpp Normal file
View File

@ -0,0 +1,26 @@
/* 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 "htmllink.h"
HtmlLink::HtmlLink()
{
}

21
htmllink.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef HTMLLINK_H
#define HTMLLINK_H
#include <string>
class HtmlLink
{
public:
HtmlLink();
std::string href;
std::string innervalue;
std::string cssclass;
std::string render()
{
return "<a href=\"" + href + "\" class=\"" + cssclass + "\">" + innervalue + "</a>";
}
};
#endif // HTMLLINK_H

Some files were not shown because too many files have changed in this diff Show More