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:
parent
44350478c1
commit
6c357f1dc7
35 changed files with 74 additions and 37 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue