1
0
Fork 0
mirror of https://github.com/KingDuckZ/kamokan.git synced 2025-10-02 15:00:02 +00:00

Separate Tawashi and Kamokan.

Unit tests are still a bit mixed up, but that should
be simple to split once I have a separate repo for
Tawashi.
This commit is contained in:
King_DuckZ 2017-06-12 10:32:20 +01:00
commit 6c357f1dc7
35 changed files with 74 additions and 37 deletions

View file

@ -2,37 +2,23 @@ project(tawashi VERSION 0.2.3 LANGUAGES CXX C)
option(TAWASHI_WITH_IP_LOGGING "Enable code in Tawashi that may result in users IPs being stored in the DB or in logs" ON)
find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options filesystem system)
find_package(SourceHighlight REQUIRED)
find_package(Boost 1.53.0 REQUIRED)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME} STATIC
add_library(${PROJECT_NAME}
split_get_vars.cpp
response.cpp
submit_paste_response.cpp
cgi_environment_vars.cpp
cgi_env.cpp
num_to_token.cpp
cgi_post.cpp
escapist.cpp
index_response.cpp
pastie_response.cpp
ini_file.cpp
pathname/pathname.cpp
response_factory.cpp
list_highlight_langs.cpp
settings_bag.cpp
sanitized_utf8.cpp
tiger.c
error_response.cpp
tawashi_exception.cpp
http_header.cpp
quick_submit_paste_response.cpp
ip_utils.cpp
mime_split.cpp
storage.cpp
truncated_string.cpp
)
@ -51,11 +37,8 @@ target_include_directories(${PROJECT_NAME} SYSTEM
)
target_link_libraries(${PROJECT_NAME}
PRIVATE ${Boost_LIBRARIES}
PRIVATE incredis
PRIVATE ${SourceHighlight_LIBRARIES}
PUBLIC mstch
PRIVATE houdini
PRIVATE pthread
PRIVATE duckhandy
)
target_compile_definitions(${PROJECT_NAME}
PRIVATE BOOST_SPIRIT_USE_PHOENIX_V3=1

View file

@ -1,70 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "error_response.hpp"
#include "error_reasons.hpp"
#include "cgi_env.hpp"
#include "sprout/array/array.hpp"
#include "string_lengths.hpp"
#include <boost/algorithm/string/replace.hpp>
#include <boost/lexical_cast.hpp>
#include <ciso646>
#include <string>
#include <cassert>
namespace tawashi {
ErrorResponse::ErrorResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
) :
Response(parSettings, parStreamOut, parCgiEnv, false)
{
}
void ErrorResponse::on_mustache_prepare (mstch::map& parContext) {
auto get = cgi_env().query_string_split();
const int reason_int = boost::lexical_cast<int>(get["reason"]);
ErrorReasons reason_code(ErrorReasons::UnkownReason);
if (reason_int >= 0 and reason_int < ErrorReasons::_size())
reason_code = ErrorReasons::_from_integral(reason_int);
constexpr const sprout::array<const char*, ErrorReasons::_size()> err_descs {
"Submitted pastie is either too short or too long and was rejected.",
"Submitted pastie couldn't be saved.",
"The pastie was not saved because the client is submitting too many pasties too quickly. Please wait a bit longer and try again.",
"An unknown error was raised.",
"Unable to connect to Redis.",
"Request is missing a POST variable.",
"Pastie not found.",
"Invalid CONTENT_TYPE.",
"Unsupported CONTENT_TYPE.",
"Invalid pastie token."
};
constexpr const auto lengths = string_lengths(err_descs);
static_assert(err_descs.static_size == lengths.static_size, "Mismatching array sizes between strings and their lengths");
#if !defined(NDEBUG)
for (std::size_t z = 0; z < err_descs.size(); ++z) {
assert(std::strlen(err_descs[z]) == lengths[z]);
}
#endif
parContext["error_message"] = std::string(err_descs[reason_code], lengths[reason_code]);
parContext["error_id"] = std::to_string(reason_code);
}
} //namespace tawashi

View file

@ -1,39 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "response.hpp"
#include <boost/utility/string_view.hpp>
namespace tawashi {
class ErrorResponse : public Response {
public:
ErrorResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_view page_basename() const override { return boost::string_view("error"); }
private:
virtual void on_mustache_prepare (mstch::map& parContext) override;
};
} //namespace tawashi

View file

@ -1,31 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "index_response.hpp"
#include <boost/algorithm/string/replace.hpp>
namespace tawashi {
IndexResponse::IndexResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
) :
Response(parSettings, parStreamOut, parCgiEnv, false)
{
}
} //namespace tawashi

View file

@ -1,37 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "response.hpp"
#include <boost/utility/string_view.hpp>
namespace tawashi {
class IndexResponse : public Response {
public:
IndexResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_view page_basename() const override { return boost::string_view("index"); }
private:
};
} //namespace tawashi

View file

@ -1,147 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "ini_file.hpp"
#include <utility>
#include <boost/spirit/include/qi_core.hpp>
#include <boost/spirit/include/qi_sequence.hpp>
#include <boost/spirit/include/qi_plus.hpp>
#include <boost/spirit/include/qi_difference.hpp>
#include <boost/spirit/include/qi_raw.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_kleene.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_eol.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_hold.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/spirit/include/qi_list.hpp>
#include <boost/spirit/include/qi_optional.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/phoenix/object/construct.hpp>
#include <boost/phoenix/bind/bind_member_function.hpp>
#include <boost/phoenix/bind/bind_member_variable.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <type_traits>
#include <iterator>
namespace tawashi {
namespace {
typedef boost::string_view string_type;
template <typename Iterator, typename Skipper>
struct IniGrammar : boost::spirit::qi::grammar<Iterator, IniFile::IniMapType(), Skipper> {
explicit IniGrammar (const std::string* parString);
boost::spirit::qi::rule<Iterator, IniFile::IniMapType(), Skipper> start;
boost::spirit::qi::rule<Iterator, string_type(), Skipper> section;
boost::spirit::qi::rule<Iterator, string_type(), Skipper> key;
boost::spirit::qi::rule<Iterator, IniFile::KeyValueMapType::value_type(), Skipper> key_value;
boost::spirit::qi::rule<Iterator, IniFile::KeyValueMapType(), Skipper> key_values;
const std::string* m_master_string;
Iterator m_begin;
};
template <typename Iterator, typename Skipper>
IniGrammar<Iterator, Skipper>::IniGrammar (const std::string* parString) :
IniGrammar::base_type(start),
m_master_string(parString),
m_begin(m_master_string->cbegin())
{
assert(m_master_string);
namespace px = boost::phoenix;
using boost::spirit::qi::_val;
using boost::spirit::_1;
using boost::spirit::qi::eol;
using boost::spirit::qi::raw;
using boost::string_view;
using boost::spirit::qi::hold;
using boost::spirit::qi::graph;
using boost::spirit::qi::blank;
typedef IniFile::KeyValueMapType::value_type refpair;
section = '[' >> raw[+(graph - ']') >> *(hold[+blank >> +(graph - ']')])]
[_val = px::bind(
&string_view::substr,
px::construct<string_view>(px::ref(*m_master_string)),
px::begin(_1) - px::ref(m_begin), px::size(_1)
)] >> ']';
key = raw[(graph - '[' - '=') >> *(graph - '=') >> *(hold[+blank >> +(graph - '=')])][_val = px::bind(
&string_view::substr,
px::construct<string_view>(px::ref(*m_master_string)),
px::begin(_1) - px::ref(m_begin), px::size(_1)
)];
key_value = key[px::bind(&refpair::first, _val) = _1] >> '=' >>
raw[*(graph - eol) >> *(hold[+blank >> +(graph - eol)])][px::bind(&refpair::second, _val) = px::bind(
&string_view::substr,
px::construct<string_view>(px::ref(*m_master_string)),
px::begin(_1) - px::ref(m_begin), px::size(_1)
)];
key_values = -(key_value % (+eol));
start = *(*eol >> section >> +eol >> key_values >> *eol);
}
IniFile::IniMapType parse_ini (const std::string* parIni, bool& parParseOk, int& parParsedCharCount) {
using boost::spirit::qi::blank;
using boost::spirit::qi::blank_type;
IniGrammar<std::string::const_iterator, blank_type> gramm(parIni);
IniFile::IniMapType result;
parParseOk = false;
parParsedCharCount = 0;
std::string::const_iterator start_it = parIni->cbegin();
//TODO: make a skipper that also skips comments eg: blank | lit("//") >> *(char_ - eol)
const bool parse_ok = boost::spirit::qi::phrase_parse(
start_it,
parIni->cend(),
gramm,
blank,
result
);
parParseOk = parse_ok and (parIni->cend() == start_it);
parParsedCharCount = std::distance(parIni->cbegin(), start_it);
assert(parParsedCharCount >= 0);
return result;
}
} //unnamed namespace
IniFile::IniFile (std::istream_iterator<char> parInputFrom, std::istream_iterator<char> parInputEnd) :
IniFile(std::string(parInputFrom, parInputEnd))
{
}
IniFile::IniFile (std::string&& parIniData) :
m_raw_ini(std::move(parIniData)),
m_map(parse_ini(&m_raw_ini, m_parse_ok, m_parsed_chars))
{
}
IniFile::IniFile (IniFile&& parOther) {
auto* const old_data_ptr = parOther.m_raw_ini.data();
m_raw_ini = std::move(parOther.m_raw_ini);
if (m_raw_ini.data() == old_data_ptr)
m_map = std::move(parOther.m_map);
else
m_map = parse_ini(&m_raw_ini, m_parse_ok, m_parsed_chars);
}
IniFile::~IniFile() noexcept = default;
} //namespace tawashi

View file

@ -1,58 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kakoune/safe_ptr.hh"
#include <boost/container/flat_map.hpp>
#include <iterator>
#include <boost/utility/string_view.hpp>
#include <string>
#include <cassert>
namespace tawashi {
class IniFile : public Kakoune::SafeCountable {
public:
typedef boost::container::flat_map<boost::string_view, boost::string_view> KeyValueMapType;
typedef boost::container::flat_map<boost::string_view, KeyValueMapType> IniMapType;
IniFile (std::istream_iterator<char> parInputFrom, std::istream_iterator<char> parInputEnd);
explicit IniFile (std::string&& parIniData);
IniFile (IniFile&& parOther);
IniFile (const IniFile& parOther) = delete;
~IniFile() noexcept;
IniFile& operator== (IniFile&&) = delete;
IniFile& operator== (const IniFile&) = delete;
bool parse_success() const { return m_parse_ok; }
int parsed_characters() const { return m_parsed_chars; }
const IniMapType& parsed() const;
private:
std::string m_raw_ini;
IniMapType m_map;
int m_parsed_chars;
bool m_parse_ok;
};
inline const IniFile::IniMapType& IniFile::parsed() const {
assert(parse_success());
return m_map;
}
} //namespace tawashi

View file

@ -1,34 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "list_highlight_langs.hpp"
#include "settings_bag.hpp"
#include <srchilite/langmap.h>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/iterator_range_core.hpp>
namespace tawashi {
HighlightLangList list_highlight_langs (const SettingsBag& parSettings) {
srchilite::LangMap lang_map(parSettings.as<std::string>("langmap_dir"), "lang.map");
lang_map.open();
const auto lang_range = boost::make_iterator_range(lang_map.begin(), lang_map.end());
return boost::copy_range<HighlightLangList>(lang_range | boost::adaptors::map_keys);
}
} //namespace tawashi

View file

@ -1,28 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vector>
#include <string>
namespace tawashi {
class SettingsBag;
typedef std::vector<std::string> HighlightLangList;
HighlightLangList list_highlight_langs (const SettingsBag& parSettings);
} //namespace tawashi

View file

@ -1,64 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "num_to_token.hpp"
#include <cassert>
namespace tawashi {
namespace {
//const int g_any_min = 0;
//const int g_any_max = g_any_min + 'z' - 'a' - 1;
//const int g_vowel_min = g_any_max + 1;
//const int g_vowel_max = g_vowel_min + 5 - 1;
//const int g_consonant_min = g_vowel_max + 1;
//const int g_consonant_max = g_consonant_min + ('z' - 'a') - (g_vowel_max - g_vowel_min) - 1;
//char code_to_char (int parCode) {
// const char vowels[] = {'a', 'i', 'u', 'e', 'o'};
// const char consonants[] = {
// 'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p',
// 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
// };
// static_assert(sizeof(vowels) == g_vowel_max - g_vowel_min + 1, "Wrong vowels count");
// static_assert(sizeof(consonants) == g_consonant_max - g_consonant_min + 1, "Wrong consonants count");
// if (parCode <= g_any_max)
// return static_cast<char>('a' + parCode - g_any_min);
// else if (parCode <= g_vowel_max)
// return vowels[parCode - g_vowel_min];
// else if (parCode <= g_consonant_max)
// return consonants[parCode - g_consonant_min];
// assert(false);
// return 'X';
//}
} //unnamed namespace
std::string num_to_token (int64_t parNum) {
assert(0 < parNum);
std::string retval;
do {
const auto remainder = parNum % ('z' - 'a' + 1);
retval.push_back(static_cast<char>('a' + remainder));
parNum /= ('z' - 'a' + 1);
} while (parNum);
return retval;
}
} //namespace tawashi

View file

@ -1,26 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "duckhandy/compatibility.h"
#include <string>
#include <cstdint>
namespace tawashi {
std::string num_to_token (int64_t parNum) a_pure;
} //namespace tawashi

View file

@ -1,190 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "pastie_response.hpp"
#include "storage.hpp"
#include "settings_bag.hpp"
#include "escapist.hpp"
#include "cgi_env.hpp"
#include "spdlog.hpp"
#include <ciso646>
#include <srchilite/sourcehighlight.h>
#include <srchilite/langmap.h>
#include <sstream>
#include <algorithm>
#include <cassert>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/range/adaptor/transformed.hpp>
namespace tawashi {
namespace {
const char g_nolang_token[] = "colourless";
std::string highlight_css_path (const SettingsBag& parSettings) {
//TODO: make sure the file exists or throw or do something
return parSettings.as<std::string>("highlight_css");
}
mstch::array pastie_to_numbered_lines (boost::string_view parPastie) {
using boost::string_view;
using string_view_iterator = string_view::const_iterator;
using boost::split_iterator;
using boost::token_finder;
using boost::adaptors::transformed;
using MatchRange = boost::iterator_range<string_view_iterator>;
int line_num = 1;
return boost::copy_range<mstch::array>(
boost::make_iterator_range(
split_iterator<string_view_iterator>(parPastie, token_finder([](char c){return '\n'==c;})),
split_iterator<string_view_iterator>()
) |
transformed([&line_num,parPastie](const MatchRange& r) {
return mstch::map {
{"number", line_num++},
{"line", parPastie.substr(r.begin() - parPastie.begin(), r.size())}
};
})
);
}
bool is_valid_token (const boost::string_view& parToken, uint32_t parMaxLen) {
if (parToken.empty())
return false;
if (parMaxLen > 0 and parToken.size() > parMaxLen)
return false;
auto it_mark = std::find(parToken.begin(), parToken.end(), '?');
if (parToken.begin() == it_mark)
return false;
for (auto it_ch = parToken.begin(); it_ch != it_mark; ++it_ch) {
if (*it_ch < 'a' or *it_ch > 'z') {
spdlog::get("statuslog")->info(
"Token's byte {} is invalid; value={}",
it_ch - parToken.begin(),
static_cast<int>(*it_ch)
);
return false;
}
}
return true;
}
} //unnamed namespace
PastieResponse::PastieResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
) :
Response(parSettings, parStreamOut, parCgiEnv, true),
m_langmap_dir(parSettings->as<std::string>("langmap_dir")),
m_plain_text(false),
m_syntax_highlight(true),
m_pastie_not_found(false),
m_token_invalid(false)
{
}
HttpHeader PastieResponse::on_process() {
if (m_pastie_not_found)
return make_error_redirect(ErrorReasons::PastieNotFound);
if (m_token_invalid)
return make_error_redirect(ErrorReasons::InvalidToken);
auto get = cgi_env().query_string_split();
const std::string& query_str(cgi_env().query_string());
if (get["m"] == "plain" or query_str.empty()) {
m_plain_text = true;
return make_header_type_text_utf8();
}
else if (query_str == g_nolang_token) {
m_syntax_highlight = false;
}
else {
srchilite::LangMap lang_map(m_langmap_dir, "lang.map");
lang_map.open();
m_lang_file.clear();
if (not query_str.empty())
m_lang_file = lang_map.getFileName(query_str);
if (m_lang_file.empty())
m_lang_file = "default.lang";
}
return make_header_type_html();
}
void PastieResponse::on_mustache_prepare (mstch::map& parContext) {
boost::string_view token = cgi::drop_arguments(cgi_env().request_uri_relative());
boost::optional<std::string> pastie = this->storage().retrieve_pastie(token);
if (not is_valid_token(token, settings().as<uint32_t>("max_token_length"))) {
m_token_invalid = true;
return;
}
if (not pastie) {
m_pastie_not_found = true;
return;
}
srchilite::SourceHighlight highlighter;
highlighter.setDataDir(settings().as<std::string>("langmap_dir"));
highlighter.setGenerateEntireDoc(false);
highlighter.setGenerateLineNumbers(true);
#if defined(NDEBUG)
highlighter.setOptimize(true);
#else
highlighter.setOptimize(false);
#endif
highlighter.setCanUseStdOut(false);
highlighter.setTabSpaces(4);
highlighter.setStyleCssFile(highlight_css_path(settings()));
highlighter.setGenerateLineNumbers(false);
std::string processed_pastie;
if (m_syntax_highlight) {
//TODO: redirect to "pastie not found" if !pastie
processed_pastie = std::move(*pastie);
}
else {
Escapist houdini;
std::ostringstream oss;
oss << R"(<pre><tt><font color="#EDEDED">)";
oss << houdini.escape_html(*pastie) << "</font></tt></pre>\n";
processed_pastie = oss.str();
}
if (not m_plain_text and m_syntax_highlight) {
std::istringstream iss(std::move(processed_pastie));
std::ostringstream oss;
highlighter.highlight(iss, oss, m_lang_file);
processed_pastie = oss.str();
}
parContext["pastie"] = std::move(processed_pastie);
parContext["pastie_lines"] = pastie_to_numbered_lines(
boost::get<std::string>(parContext["pastie"])
);
}
std::string PastieResponse::on_mustache_retrieve() {
if (m_plain_text)
return "{{pastie}}";
else
return load_mustache();
}
} //namespace tawashi

View file

@ -1,48 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "response.hpp"
#include <string>
#include <boost/utility/string_view.hpp>
namespace tawashi {
class PastieResponse : public Response {
public:
PastieResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_view page_basename() const override { return boost::string_view("pastie"); }
private:
virtual HttpHeader on_process() override;
virtual void on_mustache_prepare (mstch::map& parContext) override;
virtual std::string on_mustache_retrieve() override;
std::string m_lang_file;
std::string m_langmap_dir;
bool m_plain_text;
bool m_syntax_highlight;
bool m_pastie_not_found;
bool m_token_invalid;
};
} //namespace tawashi

View file

@ -1,254 +0,0 @@
/* Copyright 2015, 2016, Michele Santullo
* This file is part of "dindexer".
*
* "dindexer" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "dindexer" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "dindexer". If not, see <http://www.gnu.org/licenses/>.
*/
#include "pathname.hpp"
#include <algorithm>
#include <functional>
#include <ciso646>
#include <iostream>
#include <cassert>
namespace mchlib {
const std::string PathName::m_empty_str("");
namespace {
std::string get_joint_atoms ( const StringPool<char>& parPool, bool parAbs, std::size_t parSkipRight=0 );
std::size_t calc_join_size ( const StringPool<char>& parPool, bool parAbs, std::size_t parSkipRight=0 );
std::size_t get_adjusted_atom_count ( const StringPool<char>& parPool, std::size_t parSkipRight );
std::size_t get_adjusted_atom_count (const StringPool<char>& parPool, std::size_t parSkipRight) {
const auto orig_atom_count = parPool.size();
const auto atom_count = (parSkipRight >= orig_atom_count ? 0 : orig_atom_count - parSkipRight);
return atom_count;
}
std::size_t calc_join_size (const StringPool<char>& parPool, bool parAbs, std::size_t parSkipRight) {
const auto atom_count = get_adjusted_atom_count(parPool, parSkipRight);
if (not atom_count) {
if (parPool.empty() and parAbs) {
return 1;
}
else {
return 0;
}
}
std::size_t reserve = (parAbs ? 1 : 0);
for (std::size_t z = 0; z < atom_count; ++z) {
reserve += parPool[z].size();
}
reserve += atom_count - 1;
return reserve;
}
std::size_t count_grouped (boost::string_view parIn, char parDelim) {
std::size_t retval = 0;
char prev = '\0';
for (auto c : parIn) {
retval += (parDelim == c and prev != parDelim ? 1 : 0);
prev = c;
}
return retval;
}
void split_path (std::vector<boost::string_view>* parOut, boost::string_view parPath) {
auto from = parPath.begin();
boost::string_view::const_iterator next;
const auto end = parPath.end();
const auto beg = parPath.begin();
while (end != (next = std::find(from, end, '/'))) {
if (next != from) {
parOut->push_back(parPath.substr(from - beg, next - from));
from = next;
}
++from;
}
if (next != from) {
parOut->push_back(parPath.substr(from - beg, next - from));
}
}
std::string get_joint_atoms (const StringPool<char>& parPool, bool parAbs, std::size_t parSkipRight) {
const auto reserve = calc_join_size(parPool, parAbs, parSkipRight);
switch (reserve) {
case 0:
//reserve 0 means the resulting string is empty
return std::string("");
case 1:
//when reserve is 1 and we're talking about an absolute path,
//the resulting string can only be "/"
if (parAbs) {
return std::string("/");
}
};
std::string out;
out.reserve(reserve);
const char* slash = (parAbs ? "/" : "");
const auto atom_count = get_adjusted_atom_count(parPool, parSkipRight);
for (std::size_t z = 0; z < atom_count; ++z) {
out += slash;
const auto& curr_itm = parPool[z];
out.insert(out.end(), curr_itm.begin(), curr_itm.end());
slash = "/";
}
assert(reserve == out.size());
return out;
}
} //unnamed namespace
PathName::PathName (boost::string_view parPath) {
if (not parPath.empty()) {
m_absolute = ('/' == parPath.front());
std::string path(parPath.begin(), parPath.end());
const auto count = count_grouped(path, '/');
const std::size_t trailing = (path.back() == '/' ? 1 : 0);
const std::size_t absolute = (m_absolute ? 1 : 0);
const auto res = count + 1 - trailing - absolute;
std::vector<boost::string_view> atoms;
atoms.reserve(res);
split_path(&atoms, path);
m_pool.insert(atoms, &path);
}
else {
m_original_path = nullptr;
m_absolute = false;
}
}
std::string PathName::path() const {
return get_joint_atoms(m_pool, m_absolute);
}
void PathName::join (const PathName& parOther) {
m_pool.update(parOther.m_pool);
}
const boost::string_view PathName::operator[] (std::size_t parIndex) const {
return *(m_pool.begin() + parIndex);
}
std::size_t PathName::atom_count ( void ) const {
return m_pool.size();
}
void PathName::join (const char* parOther) {
const std::string src(parOther);
const boost::string_view ref(src);
m_pool.insert(ref, &src);
}
void PathName::join (boost::string_view parOther, const std::string* parSource) {
m_pool.insert(parOther, parSource);
}
PathName make_relative_path (const PathName& parBasePath, const PathName& parOtherPath) {
if (not parBasePath.is_absolute() and parOtherPath.is_absolute()) {
return parOtherPath;
}
std::size_t common_atoms = 0;
{
const std::size_t shortest = std::min(parOtherPath.atom_count(), parBasePath.atom_count());
for (std::size_t z = 0; z < shortest; ++z) {
if (parOtherPath[z] == parBasePath[z]) {
++common_atoms;
}
else {
break;
}
}
}
PathName retval("");
const auto ellipses_count = parBasePath.atom_count() - common_atoms;
for (std::size_t z = 0; z < ellipses_count; ++z) {
retval.join("..");
}
const auto remaining_atoms = parOtherPath.atom_count() - common_atoms;
for (std::size_t z = 0; z < remaining_atoms; ++z) {
retval.join(parOtherPath[z + common_atoms], parOtherPath.get_stringref_source(z + common_atoms));
}
return retval;
}
const std::string* PathName::get_stringref_source (std::size_t parIndex) const {
return m_pool.get_stringref_source(parIndex);
}
std::string PathName::dirname() const {
if (this->atom_count() == 0)
return std::string();
return get_joint_atoms(m_pool, m_absolute, 1);
}
std::ostream& operator<< (std::ostream& parStream, const PathName& parPath) {
parStream << parPath.path();
return parStream;
}
const boost::string_view basename (const PathName& parPath) {
static const char* const empty = "";
const auto sz = parPath.atom_count();
if (not sz) {
return boost::string_view(empty);
}
assert(sz > 0);
return parPath[sz - 1];
}
PathName& PathName::pop_right() {
m_pool.pop();
return *this;
}
bool PathName::operator!= (const PathName& parOther) const {
const auto count = atom_count();
if (count != parOther.atom_count()) {
return true;
}
for (std::size_t z = 0; z < count; ++z) {
if ((*this)[z] != parOther[z]) {
return true;
}
}
return false;
}
bool PathName::operator== (const PathName& parOther) const {
const auto count = atom_count();
if (count != parOther.atom_count()) {
return false;
}
for (std::size_t z = 0; z < count; ++z) {
if ((*this)[z] != parOther[z]) {
return false;
}
}
return true;
}
std::size_t PathName::str_path_size() const {
return calc_join_size(m_pool, is_absolute());
}
} //namespace mchlib

View file

@ -1,67 +0,0 @@
/* Copyright 2015, 2016, Michele Santullo
* This file is part of "dindexer".
*
* "dindexer" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "dindexer" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "dindexer". If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef id279E04E31E2C4D98B8C902781A3CE018
#define id279E04E31E2C4D98B8C902781A3CE018
#include "stringpool.hpp"
#include "kakoune/safe_ptr.hh"
#include <vector>
#include <string>
#include <boost/utility/string_view.hpp>
#include <map>
#include <iostream>
namespace mchlib {
class PathName : public Kakoune::SafeCountable {
public:
PathName ( PathName&& ) = default;
PathName ( const PathName& ) = default;
explicit PathName ( boost::string_view parPath );
~PathName ( void ) noexcept = default;
PathName& operator= ( PathName&& ) = default;
bool is_absolute ( void ) const { return m_absolute; }
std::string path ( void ) const;
std::size_t str_path_size ( void ) const;
const std::string& original_path ( void ) const { return (m_original_path ? *m_original_path : m_empty_str); }
std::size_t atom_count ( void ) const;
const boost::string_view operator[] ( std::size_t parIndex ) const;
void join ( const PathName& parOther );
void join ( const char* parOther );
void join ( boost::string_view parOther, const std::string* parSource );
const std::string* get_stringref_source ( std::size_t parIndex ) const;
std::string dirname ( void ) const;
PathName& pop_right ( void );
bool operator!= ( const PathName& parOther ) const;
bool operator== ( const PathName& parOther ) const;
private:
static const std::string m_empty_str;
StringPool<char> m_pool;
const std::string* m_original_path;
bool m_absolute;
};
PathName make_relative_path ( const PathName& parBasePath, const PathName& parOtherPath );
std::ostream& operator<< ( std::ostream& parStream, const PathName& parPath );
const boost::string_view basename ( const PathName& parPath );
} //namespace mchlib
#endif

View file

@ -1,70 +0,0 @@
/* Copyright 2015, 2016, Michele Santullo
* This file is part of "dindexer".
*
* "dindexer" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "dindexer" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "dindexer". If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef id9CF5E6FA7E334DF09559C2968C494CB9
#define id9CF5E6FA7E334DF09559C2968C494CB9
#include <string>
#include <boost/utility/string_view.hpp>
#include <vector>
#include <utility>
#include <ciso646>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <boost/iterator/transform_iterator.hpp>
namespace mchlib {
template <typename C, typename Str=std::basic_string<C>, typename StrRef=boost::basic_string_view<C>>
class StringPool {
typedef std::pair<StrRef, const Str*> StringListPair;
typedef std::vector<std::pair<Str, std::size_t>> PoolType;
typedef std::vector<StringListPair> StringListType;
typedef std::function<StrRef(const StringListPair&)> FuncGetFirst;
public:
typedef C char_type;
typedef Str string_type;
typedef StrRef stringref_type;
typedef boost::transform_iterator<FuncGetFirst, typename StringListType::const_iterator> const_iterator;
StringPool ( void ) = default;
~StringPool ( void ) noexcept = default;
template <typename ItR>
void update ( ItR parDataBeg, ItR parDataEnd );
void update ( const StringPool& parOther );
void insert ( const std::vector<stringref_type>& parStrings, const string_type* parBaseString );
void insert ( stringref_type parString, const string_type* parBaseString );
const string_type* ptr_to_literal ( const char* parLiteral );
std::size_t size ( void ) const { return m_strings.size(); }
bool empty ( void ) const { return m_strings.empty(); }
const_iterator begin ( void ) const;
const_iterator end ( void ) const;
const string_type* get_stringref_source ( std::size_t parIndex ) const;
const stringref_type& operator[] ( std::size_t parIndex ) const;
void pop ( void );
private:
PoolType m_pool;
StringListType m_strings;
};
} //namespace mchlib
#include "stringpool.inl"
#endif

View file

@ -1,140 +0,0 @@
/* Copyright 2015, 2016, Michele Santullo
* This file is part of "dindexer".
*
* "dindexer" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "dindexer" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "dindexer". If not, see <http://www.gnu.org/licenses/>.
*/
namespace mchlib {
namespace implem {
template <typename StrRef>
std::pair<StrRef, bool> clone_ifp (const StrRef& parClone, StrRef parSource) {
const auto offset = parSource.find(parClone);
if (parSource.npos != offset) {
return std::make_pair(parSource.substr(offset, parClone.size()), true);
}
else {
return std::make_pair(parClone, false);
}
}
} //namespace implem
template <typename C, typename Str, typename StrRef>
auto StringPool<C, Str, StrRef>::ptr_to_literal (const char* parLiteral) -> const string_type* {
if (not parLiteral)
return nullptr;
for (const auto& p : m_pool) {
if (m_pool.first == parLiteral) {
return &m_pool.first;
}
}
return nullptr;
}
template <typename C, typename Str, typename StrRef>
template <typename ItR>
void StringPool<C, Str, StrRef>::update (ItR parDataBeg, ItR parDataEnd) {
typedef std::pair<string_type, std::size_t> PoolPair;
while (parDataBeg != parDataEnd) {
const auto& remote_str = parDataBeg->first;
const auto* remote_source_str = parDataBeg->second;
bool cloned = false;
for (auto& local_src : m_pool) {
const string_type& local_str = local_src.first;
auto& local_ref_count = local_src.second;
auto cloned_result = implem::clone_ifp<StrRef>(remote_str, local_str);
cloned = cloned_result.second;
const auto& cloned_str = cloned_result.first;
if (cloned) {
++local_ref_count;
m_strings.push_back(StringListPair(cloned_str, &local_str));
break;
}
}
if (not cloned) {
m_pool.push_back(PoolPair(*remote_source_str, static_cast<std::size_t>(1)));
const auto offset = remote_str.data() - remote_source_str->data();
m_strings.push_back(StringListPair(stringref_type(m_pool.back().first).substr(offset, remote_str.size()), &m_pool.back().first));
}
++parDataBeg;
}
}
template <typename C, typename Str, typename StrRef>
void StringPool<C, Str, StrRef>::update (const StringPool& parOther) {
this->update(parOther.m_strings.begin(), parOther.m_strings.end());
}
template <typename C, typename Str, typename StrRef>
auto StringPool<C, Str, StrRef>::begin() const -> const_iterator {
return const_iterator(m_strings.cbegin(), [](const StringListPair& parItm) { return parItm.first; });
}
template <typename C, typename Str, typename StrRef>
auto StringPool<C, Str, StrRef>::end() const -> const_iterator {
return const_iterator(m_strings.cend(), [](const StringListPair& parItm) { return parItm.first; });
}
template <typename C, typename Str, typename StrRef>
void StringPool<C, Str, StrRef>::insert (const std::vector<stringref_type>& parStrings, const string_type* parBaseString) {
StringListType dummy;
dummy.reserve(parStrings.size());
for (const auto& itm : parStrings) {
dummy.push_back(StringListPair(itm, parBaseString));
}
this->update(dummy.begin(), dummy.end());
}
template <typename C, typename Str, typename StrRef>
void StringPool<C, Str, StrRef>::insert (stringref_type parString, const string_type* parBaseString) {
StringListType dummy;
dummy.reserve(1);
dummy.push_back(StringListPair(parString, parBaseString));
this->update(dummy.begin(), dummy.end());
}
template <typename C, typename Str, typename StrRef>
auto StringPool<C, Str, StrRef>::get_stringref_source (std::size_t parIndex) const -> const string_type* {
return m_strings[parIndex].second;
}
template <typename C, typename Str, typename StrRef>
auto StringPool<C, Str, StrRef>::operator[] (std::size_t parIndex) const -> const stringref_type& {
return m_strings[parIndex].first;
}
template <typename C, typename Str, typename StrRef>
void StringPool<C, Str, StrRef>::pop() {
if (m_strings.empty()) {
return;
}
for (auto z = m_pool.size(); z > 0; --z) {
auto& pool_itm = m_pool[z - 1];
if (&pool_itm.first == m_strings.back().second) {
m_strings.resize(m_strings.size() - 1);
--pool_itm.second;
if (0 == pool_itm.second) {
m_pool.erase(m_pool.begin() + (z - 1));
}
break;
}
}
return;
}
} //namespace mchlib

View file

@ -1,42 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "quick_submit_paste_response.hpp"
namespace tawashi {
QuickSubmitPasteResponse::QuickSubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
) :
SubmitPasteResponse(parSettings, parStreamOut, parCgiEnv)
{
}
void QuickSubmitPasteResponse::on_mustache_prepare (mstch::map& parContext) {
parContext["redirect_to_address"] = m_redirect_to;
}
std::string QuickSubmitPasteResponse::on_mustache_retrieve() {
return "{{base_uri}}/{{redirect_to_address}}\n";
}
HttpHeader QuickSubmitPasteResponse::make_success_response (std::string&& parPastieParam) {
m_redirect_to = std::move(parPastieParam);
return HttpHeader();
}
} //namespace tawashi

View file

@ -1,44 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "submit_paste_response.hpp"
#include <boost/optional.hpp>
#include <boost/utility/string_view.hpp>
#include <cassert>
namespace tawashi {
class QuickSubmitPasteResponse : public SubmitPasteResponse {
public:
QuickSubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_view page_basename() const override { assert(false); return boost::string_view(""); }
virtual HttpHeader make_success_response (std::string&& parPastieParam) override;
private:
virtual void on_mustache_prepare (mstch::map& parContext) override;
virtual std::string on_mustache_retrieve() override;
std::string m_redirect_to;
};
} //namespace tawashi

View file

@ -1,260 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "response.hpp"
#include "settings_bag.hpp"
#include "duckhandy/stringize.h"
#include "duckhandy/lexical_cast.hpp"
#include "pathname/pathname.hpp"
#include "list_highlight_langs.hpp"
#include "cgi_env.hpp"
#include "num_conv.hpp"
#include "tawashi_config.h"
#include <utility>
#include <cassert>
#include <fstream>
#include <sstream>
#include <functional>
#include <boost/optional.hpp>
#include <cstdint>
#include <spdlog/spdlog.h>
namespace tawashi {
namespace {
//boost::string_ref fetch_page_basename (const cgi::Env& parEnv) {
// const boost::string_ref& path = parEnv.path_info();
// const std::size_t last_slash = path.rfind('/');
// const std::size_t last_dot = path.rfind('.');
// const std::size_t start_index = (path.npos == last_slash ? 0 : last_slash + 1);
// const std::size_t substr_len = (path.size() - start_index - (last_dot == path.npos ? 0 : path.size() - last_dot));
// assert(start_index <= path.size());
// assert(substr_len < path.size() and substr_len - path.size() - start_index);
// return path.substr(start_index, substr_len);
//}
std::string make_root_path (const SettingsBag& parSettings) {
auto retval = parSettings["website_root"];
if (retval.empty()) {
return "";
}
else {
return mchlib::PathName(retval).path() + '/';
}
}
boost::optional<std::string> load_whole_file (const std::string& parWebsiteRoot, const char* parSuffix, const boost::string_view& parName, bool parThrow) {
std::ostringstream oss;
oss << parWebsiteRoot << parName << parSuffix;
spdlog::get("statuslog")->info("Trying to load \"{}\"", oss.str());
std::ifstream if_mstch(oss.str(), std::ios::binary | std::ios::in);
if (not if_mstch) {
spdlog::get("statuslog")->warn("Couldn't open file \"{}\"", oss.str());
if (parThrow)
throw std::runtime_error(std::string("File \"") + oss.str() + "\" not found");
else
return boost::optional<std::string>();
}
std::ostringstream buffer;
buffer << if_mstch.rdbuf();
return boost::make_optional(buffer.str());
}
mstch::array make_mstch_langmap (const SettingsBag& parSettings) {
mstch::array retval;
for (auto&& lang : list_highlight_langs(parSettings)) {
retval.push_back(mstch::map{{"language_name", std::move(lang)}});
}
return retval;
}
std::string disable_mstch_escaping (const std::string& parStr) {
return parStr;
};
boost::string_view make_host_path (const SettingsBag& parSettings) {
boost::string_view host_path = parSettings.at("host_path");
if (not host_path.empty() and host_path[host_path.size() - 1] == '/')
host_path = host_path.substr(0, host_path.size() - 1);
return host_path;
}
std::string make_base_uri (const Kakoune::SafePtr<SettingsBag>& parSettings, const Kakoune::SafePtr<cgi::Env>& parCgiEnv) {
assert(parSettings);
assert(parCgiEnv);
std::ostringstream oss;
if (parCgiEnv->https())
oss << "https://";
else
oss << "http://";
oss << parSettings->at("host_name");
boost::string_view host_port = parSettings->at("host_port");
if (not host_port.empty()) {
if (host_port == "from_downstream") {
const uint16_t port = parCgiEnv->server_port();
if ((80 != port and not parCgiEnv->https()) or 443 != port and parCgiEnv->https()) {
oss << ':' << port;
}
}
else if (not host_port.empty() and seems_valid_number<uint16_t>(host_port)) {
oss << ':' << host_port;
}
}
oss << make_host_path(*parSettings);
return oss.str();
}
} //unnamed namespace
Response::Response (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv,
bool parWantRedis
) :
m_storage(parSettings),
//m_page_basename(fetch_page_basename(m_cgi_env)),
m_cgi_env(parCgiEnv),
m_settings(parSettings),
m_website_root(make_root_path(*parSettings)),
m_base_uri(make_base_uri(m_settings, m_cgi_env)),
m_stream_out(parStreamOut)
{
assert(m_cgi_env);
assert(m_stream_out);
if (parWantRedis) {
m_storage.connect_async();
}
mstch::config::escape = &disable_mstch_escaping;
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
statuslog->info("Preparing response for {} request; query_string=\"{}\"; size={}",
cgi_env().request_method()._to_string(),
cgi_env().query_string(),
cgi_env().content_length()
);
}
Response::~Response() noexcept = default;
HttpHeader Response::on_process() {
return HttpHeader();
}
void Response::on_mustache_prepare (mstch::map&) {
}
void Response::send() {
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
statuslog->info("Sending response");
SPDLOG_TRACE(statuslog, "Preparing mustache dictionary");
mstch::map mustache_context {
{"version", std::string{STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "." STRINGIZE(VERSION_PATCH)}},
{"base_uri", base_uri()},
{"host_path", make_host_path(this->settings())},
{"languages", make_mstch_langmap(*m_settings)}
};
m_storage.finalize_connection();
SPDLOG_TRACE(statuslog, "Raising event on_process");
HttpHeader http_header = this->on_process();
*m_stream_out << http_header;
if (http_header.body_required()) {
SPDLOG_TRACE(statuslog, "Raising event on_mustache_prepare");
this->on_mustache_prepare(mustache_context);
SPDLOG_TRACE(statuslog, "Rendering in mustache");
*m_stream_out << mstch::render(
on_mustache_retrieve(),
mustache_context,
std::bind(
&load_whole_file,
std::cref(m_website_root),
".mustache",
std::placeholders::_1,
false
)
);
}
SPDLOG_TRACE(statuslog, "Flushing output");
m_stream_out->flush();
}
std::string Response::on_mustache_retrieve() {
return load_mustache();
}
const cgi::Env& Response::cgi_env() const {
return *m_cgi_env;
}
const cgi::PostMapType& Response::cgi_post() const {
return cgi::read_post(std::cin, cgi_env(), settings().as<uint32_t>("max_post_size"));
}
const std::string& Response::base_uri() const {
return m_base_uri;
}
std::string Response::load_mustache() const {
boost::optional<std::string> content = load_whole_file(m_website_root, ".html.mstch", page_basename(), true);
return *content;
}
const Storage& Response::storage() const {
assert(m_storage.is_connected());
return m_storage;
}
const SettingsBag& Response::settings() const {
assert(m_settings);
return *m_settings;
}
HttpHeader Response::make_redirect (HttpStatusCodes parCode, const std::string& parLocation) {
std::ostringstream oss;
oss << base_uri() << '/' << parLocation;
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
statuslog->info("Redirecting to page \"{}\"", oss.str());
return HttpHeader(parCode, oss.str());
}
HttpHeader Response::make_error_redirect (ErrorReasons parReason) {
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
const HttpStatusCodes redir_code = HttpStatusCodes::Code302_Found;
statuslog->info("Redirecting to error page, code={} reason={}", redir_code, parReason);
std::ostringstream oss;
oss << "error.cgi?reason=" << parReason._to_integral();
return make_redirect(redir_code, oss.str());
}
} //namespace tawashi

View file

@ -1,75 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mstch/mstch.hpp"
#include "kakoune/safe_ptr.hh"
#include "http_header.hpp"
#include "error_reasons.hpp"
#include "storage.hpp"
#include "cgi_post.hpp"
#include <string>
#include <iostream>
#include <boost/utility/string_view.hpp>
#include <memory>
namespace tawashi {
class SettingsBag;
namespace cgi {
class Env;
} //namespace cgi
class Response {
public:
virtual ~Response() noexcept;
void send();
protected:
Response (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv,
bool parWantRedis
);
const cgi::Env& cgi_env() const;
tawashi_virtual_testing const cgi::PostMapType& cgi_post() const;
const std::string& base_uri() const;
virtual boost::string_view page_basename() const = 0;
tawashi_virtual_testing const Storage& storage() const;
const SettingsBag& settings() const;
virtual std::string load_mustache() const;
HttpHeader make_redirect (HttpStatusCodes parCode, const std::string& parLocation);
HttpHeader make_error_redirect (ErrorReasons parReason);
private:
virtual HttpHeader on_process();
virtual void on_mustache_prepare (mstch::map& parContext);
virtual std::string on_mustache_retrieve();
Storage m_storage;
Kakoune::SafePtr<cgi::Env> m_cgi_env;
Kakoune::SafePtr<SettingsBag> m_settings;
std::string m_website_root;
std::string m_base_uri;
std::ostream* m_stream_out;
};
} //namespace tawashi

View file

@ -1,91 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "response_factory.hpp"
#include "settings_bag.hpp"
#include "cgi_env.hpp"
#include <functional>
#include <boost/container/flat_map.hpp>
#include <spdlog/spdlog.h>
#include <algorithm>
#include <array>
namespace tawashi {
namespace {
} //unnamed namespace
struct ResponseFactory::LocalData {
Kakoune::SafePtr<SettingsBag> settings;
boost::container::flat_map<std::string, ResponseMakerFunc> makers_get;
boost::container::flat_map<std::string, ResponseMakerFunc> makers_post;
std::array<ResponseMakerFunc, RequestMethodType::_size()> jolly_makers;
Kakoune::SafePtr<cgi::Env> cgi_env;
};
ResponseFactory::ResponseFactory (const Kakoune::SafePtr<SettingsBag>& parSettings, const Kakoune::SafePtr<cgi::Env>& parCgiEnv) :
m_local_data(std::make_unique<LocalData>())
{
m_local_data->settings = parSettings;
m_local_data->cgi_env = parCgiEnv;
std::fill(m_local_data->jolly_makers.begin(), m_local_data->jolly_makers.end(), nullptr);
}
ResponseFactory::~ResponseFactory() noexcept = default;
std::unique_ptr<Response> ResponseFactory::make_response (const boost::string_view& parName, RequestMethodType parReqType) {
std::string name(parName.data(), parName.size());
spdlog::get("statuslog")->info(
"making response object for \"{}\" method {}",
name,
parReqType._to_string()
);
const auto& makers = (static_cast<RequestMethodType>(RequestMethodType::POST) == parReqType ? m_local_data->makers_post : m_local_data->makers_get);
auto maker_it = makers.find(name);
if (makers.end() != maker_it) {
return maker_it->second(m_local_data->settings, m_local_data->cgi_env);
}
else if (m_local_data->jolly_makers[parReqType]) {
spdlog::get("statuslog")->info("no exact match found for \"{}\", assuming it's a pastie's token", name);
return m_local_data->jolly_makers[parReqType](m_local_data->settings, m_local_data->cgi_env);
}
else {
spdlog::get("statuslog")->critical("no exact match found for \"{}\" with method {} and no jolly maker given, this should not happen", name, parReqType._to_string());
return std::unique_ptr<Response>();
}
}
void ResponseFactory::register_maker (std::string&& parName, ResponseMakerFunc parMaker) {
m_local_data->makers_get[parName] = parMaker;
m_local_data->makers_post[std::move(parName)] = parMaker;
}
void ResponseFactory::register_maker (std::string&& parName, RequestMethodType parReqType, ResponseMakerFunc parMaker) {
switch (parReqType) {
case RequestMethodType::GET:
m_local_data->makers_get[std::move(parName)] = parMaker;
break;
case RequestMethodType::POST:
m_local_data->makers_post[std::move(parName)] = parMaker;
break;
};
}
void ResponseFactory::register_jolly_maker (ResponseMakerFunc parMaker, RequestMethodType parReqType) {
m_local_data->jolly_makers[parReqType] = parMaker;
}
} //namespace tawashi

View file

@ -1,49 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "response.hpp"
#include "kakoune/safe_ptr.hh"
#include "request_method_type.hpp"
#include <memory>
namespace tawashi {
class SettingsBag;
namespace cgi {
class Env;
} //namespace cgi
class ResponseFactory {
public:
typedef std::function<std::unique_ptr<Response>(const Kakoune::SafePtr<SettingsBag>&, const Kakoune::SafePtr<cgi::Env>& parCgiEnv)> ResponseMakerFunc;
explicit ResponseFactory (const Kakoune::SafePtr<SettingsBag>& parSettings, const Kakoune::SafePtr<cgi::Env>& parCgiEnv);
~ResponseFactory() noexcept;
std::unique_ptr<Response> make_response(const boost::string_view& parName, RequestMethodType parReqType);
void register_maker (std::string&& parName, ResponseMakerFunc parMaker);
void register_maker (std::string&& parName, RequestMethodType parReqType, ResponseMakerFunc parMaker);
void register_jolly_maker (ResponseMakerFunc parMaker, RequestMethodType parReqType);
private:
struct LocalData;
std::unique_ptr<LocalData> m_local_data;
};
} //namespace tawashi

View file

@ -1,100 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "settings_bag.hpp"
#include "duckhandy/lexical_cast.hpp"
#include "spdlog.hpp"
#include <ciso646>
#include <cassert>
#include <cstdint>
#include <sstream>
#include <iostream>
namespace tawashi {
namespace {
const IniFile::KeyValueMapType* get_tawashi_node (const IniFile& parIni, boost::string_view parSectionName) {
auto it_found = parIni.parsed().find(parSectionName);
if (parIni.parsed().end() != it_found) {
return &it_found->second;
}
else {
std::cerr << "Couldn't find section [" << parSectionName << "] in the settings file\n";
static const IniFile::KeyValueMapType empty_key_values;
return &empty_key_values;
}
}
} //unnamed namespace
SettingsBag::SettingsBag (const Kakoune::SafePtr<IniFile>& parIni, boost::string_view parSectionName) :
m_ini(parIni),
m_values(get_tawashi_node(*parIni, parSectionName))
{
assert(m_values);
}
SettingsBag::~SettingsBag() noexcept = default;
const boost::string_view& SettingsBag::operator[] (boost::string_view parIndex) const {
auto it_found = m_values->find(parIndex);
if (m_values->end() != it_found)
return it_found->second;
else
return m_defaults.at(parIndex);
}
void SettingsBag::add_default (boost::string_view parKey, boost::string_view parValue) {
assert(m_defaults.find(parKey) == m_defaults.end());
m_defaults[parKey] = parValue;
}
template <>
std::string SettingsBag::as (boost::string_view parIndex) const {
auto& setting = this->at(parIndex);
return std::string(setting);
}
template <>
bool SettingsBag::as (boost::string_view parIndex) const {
auto& setting = this->at(parIndex);
if (setting == "true" or setting == "yes" or setting == "1" or setting == "on") {
return true;
}
else if (setting == "false" or setting == "no" or setting == "0" or setting == "off") {
return false;
}
else {
std::ostringstream oss;
oss << "Bad conversion: can't convert \"" << setting << "\" to bool";
throw std::runtime_error(oss.str());
}
}
template <>
uint16_t SettingsBag::as (boost::string_view parIndex) const {
return dhandy::lexical_cast<uint16_t>(this->at(parIndex));
}
template <>
uint32_t SettingsBag::as (boost::string_view parIndex) const {
return dhandy::lexical_cast<uint32_t>(this->at(parIndex));
}
template std::string SettingsBag::as<std::string> (boost::string_view parIndex) const;
template bool SettingsBag::as<bool> (boost::string_view parIndex) const;
template uint16_t SettingsBag::as<uint16_t> (boost::string_view parIndex) const;
template uint32_t SettingsBag::as<uint32_t> (boost::string_view parIndex) const;
} //namespace tawashi

View file

@ -1,59 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ini_file.hpp"
#include "kakoune/safe_ptr.hh"
#if defined(SPDLOG_DEBUG_ON)
# include "spdlog.hpp"
#endif
#include <map>
#include <boost/utility/string_view.hpp>
#include <functional>
#include <string>
namespace tawashi {
class SettingsBag : public Kakoune::SafeCountable {
typedef std::map<boost::string_view, boost::string_view> MapType;
public:
SettingsBag (const Kakoune::SafePtr<IniFile>& parIni, boost::string_view parSectionName);
~SettingsBag() noexcept;
const boost::string_view& operator[] (boost::string_view parIndex) const;
const boost::string_view& at (boost::string_view parIndex) const;
template <typename T> T as (boost::string_view parIndex) const;
void add_default (boost::string_view parKey, boost::string_view parValue);
private:
MapType m_defaults;
Kakoune::SafePtr<IniFile> m_ini;
const IniFile::KeyValueMapType* m_values;
};
template <>
inline boost::string_view SettingsBag::as (boost::string_view parIndex) const {
return (*this)[parIndex];
}
inline const boost::string_view& SettingsBag::at (boost::string_view parIndex) const {
#if defined(SPDLOG_DEBUG_ON)
SPDLOG_DEBUG(spdlog::get("statuslog"), "Retrieving setting \"{}\"", parIndex);
#endif
return (*this)[parIndex];
}
} //namespace tawashi

View file

@ -1,159 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "storage.hpp"
#include "settings_bag.hpp"
#include "incredis/incredis.hpp"
#include "num_to_token.hpp"
#include "tawashi_config.h"
#include "duckhandy/stringize.h"
#include "spdlog.hpp"
#include "truncated_string.hpp"
#include <cassert>
#include <ciso646>
#include <string>
#include <utility>
#include <algorithm>
namespace tawashi {
namespace {
redis::IncRedis make_incredis (const SettingsBag& parSettings) {
using redis::IncRedis;
if (parSettings["redis_mode"] == "inet") {
return IncRedis(
parSettings.as<std::string>("redis_server"),
parSettings.as<uint16_t>("redis_port")
);
}
else if (parSettings["redis_mode"] == "sock") {
return IncRedis(parSettings.as<std::string>("redis_sock"));
}
else {
throw std::runtime_error("Unknown setting for \"redis_mode\", valid settings are \"inet\" or \"sock\"");
}
}
Storage::SubmissionResult make_submission_result (std::string&& parToken) {
return Storage::SubmissionResult { std::move(parToken), boost::optional<ErrorReasons>() };
}
Storage::SubmissionResult make_submission_result (ErrorReasons parError) {
return Storage::SubmissionResult { std::string(), boost::make_optional(parError) };
}
} //unnamed namespace
Storage::Storage (const Kakoune::SafePtr<SettingsBag>& parSettings) :
m_redis(nullptr),
m_settings(parSettings)
{
}
Storage::~Storage() = default;
void Storage::connect_async() {
using redis::IncRedis;
assert(not m_redis);
m_redis = std::make_unique<redis::IncRedis>(make_incredis(*m_settings));
m_redis->connect();
SPDLOG_TRACE(spdlog::get("statuslog"), "Trying to connect to Redis asynchronously");
}
bool Storage::is_connected() const {
return m_redis and m_redis->is_connected();
}
void Storage::finalize_connection() {
SPDLOG_TRACE(spdlog::get("statuslog"), "Asked Storage to finalize the Redis connection");
if (m_redis) {
SPDLOG_TRACE(spdlog::get("statuslog"), "Finalizing Redis connection");
m_redis->wait_for_connect();
auto batch = m_redis->make_batch();
batch.select(m_settings->as<uint32_t>("redis_db"));
batch.client_setname("tawashi_v" STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "." STRINGIZE(VERSION_PATCH));
batch.throw_if_failed();
}
}
Storage::SubmissionResult Storage::submit_pastie (
const boost::string_view& parText,
uint32_t parExpiry,
const boost::string_view& parLang,
const std::string& parRemoteIP
) const {
if (not is_connected())
return make_submission_result(ErrorReasons::RedisDisconnected);
assert(m_redis);
auto& redis = *m_redis;
if (redis.get(parRemoteIP)) {
//please wait and submit again
return make_submission_result(ErrorReasons::UserFlooding);
}
auto statuslog = spdlog::get("statuslog");
if (statuslog->should_log(spdlog::level::info)) {
statuslog->info(
"Submitting pastie of size {} to redis -> \"{}\"",
parText.size(),
truncated_string(parText, 30)
);
}
const auto next_id = redis.incr("paste_counter");
std::string token = num_to_token(next_id);
assert(not token.empty());
if (redis.hmset(token,
"pastie", parText,
"max_ttl", dhandy::lexical_cast<std::string>(parExpiry),
"lang", parLang)
) {
redis.set(parRemoteIP, "");
redis.expire(parRemoteIP, m_settings->as<uint32_t>("resubmit_wait"));
if (redis.expire(token, parExpiry))
return make_submission_result(std::move(token));
}
return make_submission_result(ErrorReasons::PastieNotSaved);
}
boost::optional<std::string> Storage::retrieve_pastie (const boost::string_view& parToken) const {
using opt_string = redis::IncRedis::opt_string;
using opt_string_list = redis::IncRedis::opt_string_list;
opt_string_list pastie_reply = m_redis->hmget(parToken, "pastie");
opt_string pastie = (pastie_reply and not pastie_reply->empty() ? (*pastie_reply)[0] : opt_string());
#if defined(SPDLOG_DEBUG_ON)
{
auto statuslog = spdlog::get("statuslog");
if (pastie)
statuslog->debug("Retrieving pastie with token \"{}\" gave a result of size {}", parToken, pastie->size());
else
statuslog->debug("Retrieving pastie with token \"{}\" gave no results", parToken);
}
#endif
return pastie;
}
#if defined(TAWASHI_WITH_TESTING)
const SettingsBag& Storage::settings() const {
return *m_settings;
}
#endif
} //namespace tawashi

View file

@ -1,65 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kakoune/safe_ptr.hh"
#include "error_reasons.hpp"
#include <memory>
#include <cstdint>
#include <boost/utility/string_view.hpp>
#include <boost/optional.hpp>
#include <string>
namespace redis {
class IncRedis;
} //namespace redis
namespace tawashi {
class SettingsBag;
class Storage {
public:
struct SubmissionResult {
std::string token;
boost::optional<ErrorReasons> error;
};
explicit Storage (const Kakoune::SafePtr<SettingsBag>& parSettings);
tawashi_virtual_testing ~Storage();
tawashi_virtual_testing void connect_async();
tawashi_virtual_testing bool is_connected() const;
tawashi_virtual_testing void finalize_connection();
tawashi_virtual_testing SubmissionResult submit_pastie (
const boost::string_view& parText,
uint32_t parExpiry,
const boost::string_view& parLang,
const std::string& parRemoteIP
) const;
tawashi_virtual_testing boost::optional<std::string> retrieve_pastie (const boost::string_view& parToken) const;
#if defined(TAWASHI_WITH_TESTING)
const SettingsBag& settings() const;
#endif
private:
std::unique_ptr<redis::IncRedis> m_redis;
Kakoune::SafePtr<SettingsBag> m_settings;
};
} //namespace tawashi

View file

@ -1,177 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "submit_paste_response.hpp"
#include "storage.hpp"
#include "cgi_post.hpp"
#include "settings_bag.hpp"
#include "duckhandy/compatibility.h"
#include "duckhandy/lexical_cast.hpp"
#include "tawashi_exception.hpp"
#include "ip_utils.hpp"
#include <ciso646>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#include <cstdint>
#include <spdlog/spdlog.h>
namespace tawashi {
namespace {
const char g_post_key[] = "pastie";
const char g_language_key[] = "lang";
const char g_duration_key[] = "ttl";
class MissingPostVarError : public TawashiException {
public:
explicit MissingPostVarError(const boost::string_view& parKey) :
TawashiException(
ErrorReasons::MissingPostVariable,
"Error retrieving POST variable \"" + std::string(parKey.begin(), parKey.end()) + "\""
)
{}
};
template <std::size_t N>
inline boost::string_view make_string_view (const char (&parStr)[N]) a_always_inline;
template <std::size_t N>
boost::string_view make_string_view (const char (&parStr)[N]) {
static_assert(N > 0, "wat?");
return boost::string_view(parStr, N - 1);
}
boost::string_view get_value_from_post (const cgi::PostMapType& parPost, boost::string_view parKey) {
std::string key(parKey);
auto post_data_it = parPost.find(key);
if (parPost.end() == post_data_it)
throw MissingPostVarError(parKey);
return post_data_it->second;
}
boost::string_view get_value_from_post_log_failure (const cgi::PostMapType& parPost, boost::string_view parKey) {
try {
return get_value_from_post(parPost, parKey);
}
catch (const MissingPostVarError& e) {
spdlog::get("statuslog")->info(e.what());
return boost::string_view();
}
}
} //unnamed namespace
#if defined(TAWASHI_WITH_TESTING)
SubmitPasteResponse::SubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv,
bool parInitStorage
) :
Response(parSettings, parStreamOut, parCgiEnv, parInitStorage)
{
}
#endif
SubmitPasteResponse::SubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
) :
Response(parSettings, parStreamOut, parCgiEnv, true)
{
}
HttpHeader SubmitPasteResponse::on_process() {
boost::string_view pastie;
boost::string_view lang;
boost::string_view duration;
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
const SettingsBag& settings = this->settings();
try {
auto& post = this->cgi_post();
pastie = get_value_from_post(post, make_string_view(g_post_key));
lang = get_value_from_post_log_failure(post, make_string_view(g_language_key));
duration = get_value_from_post_log_failure(post, make_string_view(g_duration_key));
}
catch (const UnsupportedContentTypeException& err) {
statuslog->info(
"Unsupported content type exception: \"{}\"",
err.what()
);
return make_error_redirect(ErrorReasons::UnsupportedContentType);
}
catch (const TawashiException& e) {
statuslog->error(e.what());
return make_error_redirect(e.reason());
}
const auto max_sz = settings.as<uint32_t>("max_pastie_size");
if (pastie.size() < settings.as<uint32_t>("min_pastie_size")) {
return make_error_redirect(ErrorReasons::PostLengthNotInRange);
}
if (max_sz and pastie.size() > max_sz) {
if (settings.as<bool>("truncate_long_pasties")) {
pastie = pastie.substr(0, max_sz);
}
else {
return make_error_redirect(ErrorReasons::PostLengthNotInRange);
}
}
//TODO: replace boost's lexical_cast with mine when I have some checks
//over invalid inputs
const uint32_t duration_int = std::max(std::min((duration.empty() ? 86400U : boost::lexical_cast<uint32_t>(duration)), 2628000U), 1U);
StringOrHeader submit_result = submit_to_storage(pastie, duration_int, lang);
const auto& token = submit_result.first;
if (token) {
std::ostringstream oss;
oss << *token;
if (not lang.empty())
oss << '?' << lang;
std::string redirect = oss.str();
statuslog->info("Pastie token=\"{}\" redirect=\"{}\"", *token, redirect);
return this->make_success_response(std::move(redirect));
}
else {
statuslog->info("Empty pastie token (possibly due to a previous failure)");
return submit_result.second;
}
}
auto SubmitPasteResponse::submit_to_storage (
const boost::string_view& parText,
uint32_t parExpiry,
const boost::string_view& parLang
) -> StringOrHeader {
auto& storage = this->storage();
std::string remote_ip = guess_real_remote_ip(cgi_env());
Storage::SubmissionResult submission_res = storage.submit_pastie(parText, parExpiry, parLang, remote_ip);
if (not submission_res.error)
return std::make_pair(boost::make_optional(std::move(submission_res.token)), HttpHeader());
else
return std::make_pair(boost::optional<std::string>(), make_error_redirect(*submission_res.error));
}
HttpHeader SubmitPasteResponse::make_success_response (std::string&& parPastieParam) {
return this->make_redirect(HttpStatusCodes::Code303_SeeOther, std::move(parPastieParam));
}
} //namespace tawashi

View file

@ -1,55 +0,0 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "response.hpp"
#include <string>
#include <boost/optional.hpp>
#include <boost/utility/string_view.hpp>
#include <cassert>
#include <utility>
namespace tawashi {
class SubmitPasteResponse : public Response {
public:
#if defined(TAWASHI_WITH_TESTING)
SubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv,
bool parInitStorage
);
#endif
SubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_view page_basename() const override { assert(false); return boost::string_view(""); }
virtual HttpHeader make_success_response (std::string&& parPastieParam);
private:
typedef std::pair<boost::optional<std::string>, HttpHeader> StringOrHeader;
virtual HttpHeader on_process() override;
StringOrHeader submit_to_storage (const boost::string_view& parText, uint32_t parExpiry, const boost::string_view& parLang);
};
} //namespace tawashi