Этот коммит содержится в:
2018-11-03 17:12:20 +01:00
Коммит 3bfebfe8a8
212 изменённых файлов: 11970 добавлений и 0 удалений

76
handlers/handler.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,76 @@
/* 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;
}

40
handlers/handler.h Обычный файл
Просмотреть файл

@ -0,0 +1,40 @@
#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

45
handlers/handlerallcategories.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,45 @@
/* 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;
}

14
handlers/handlerallcategories.h Обычный файл
Просмотреть файл

@ -0,0 +1,14 @@
#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

48
handlers/handlerallpages.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,48 @@
/* 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");
}
}

13
handlers/handlerallpages.h Обычный файл
Просмотреть файл

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

50
handlers/handlercategory.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,50 @@
/* 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");
}
}

13
handlers/handlercategory.h Обычный файл
Просмотреть файл

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

30
handlers/handlerdefault.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,30 @@
/* 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 Обычный файл
Просмотреть файл

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

102
handlers/handlerfactory.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,102 @@
/* 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>();
}

9
handlers/handlerfactory.h Обычный файл
Просмотреть файл

@ -0,0 +1,9 @@
#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

104
handlers/handlerhistory.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,104 @@
/* 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 Обычный файл
Просмотреть файл

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

26
handlers/handlerinvalidaction.cpp Обычный файл
Просмотреть файл

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

15
handlers/handlerinvalidaction.h Обычный файл
Просмотреть файл

@ -0,0 +1,15 @@
#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

118
handlers/handlerlogin.cpp Обычный файл
Просмотреть файл

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

22
handlers/handlerlogin.h Обычный файл
Просмотреть файл

@ -0,0 +1,22 @@
#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

90
handlers/handlerpage.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,90 @@
/* 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);
}
}

23
handlers/handlerpage.h Обычный файл
Просмотреть файл

@ -0,0 +1,23 @@
#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

47
handlers/handlerpagedelete.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,47 @@
/* 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");
}
}

27
handlers/handlerpagedelete.h Обычный файл
Просмотреть файл

@ -0,0 +1,27 @@
#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

115
handlers/handlerpageedit.cpp Обычный файл
Просмотреть файл

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

22
handlers/handlerpageedit.h Обычный файл
Просмотреть файл

@ -0,0 +1,22 @@
#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

164
handlers/handlerpageview.cpp Обычный файл
Просмотреть файл

@ -0,0 +1,164 @@
/* 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;
}

26
handlers/handlerpageview.h Обычный файл
Просмотреть файл

@ -0,0 +1,26 @@
#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

63
handlers/handlersearch.cpp Обычный файл
Просмотреть файл

@ -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 Обычный файл
Просмотреть файл

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