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

Split tawashi into lib+executable to enable unit testing

This commit is contained in:
King_DuckZ 2017-05-04 19:52:18 +01:00
commit e1687c96ff
45 changed files with 175 additions and 78 deletions

View file

@ -0,0 +1,57 @@
project(tawashi_implem LANGUAGES CXX)
find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options filesystem system)
find_package(SourceHighlight REQUIRED)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME} STATIC
split_get_vars.cpp
response.cpp
submit_paste_response.cpp
get_env.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
)
target_include_directories(${PROJECT_NAME}
PRIVATE ${TAWASHI_GEN_INCLUDE_DIR}
PUBLIC ${TAWASHI_SOURCE_ROOT}/lib/kakoune
PUBLIC ${TAWASHI_SOURCE_ROOT}/lib/mstch/include
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)
target_include_directories(${PROJECT_NAME} SYSTEM
PUBLIC ${Boost_INCLUDE_DIRS}
PUBLIC ${TAWASHI_SOURCE_ROOT}/lib/better-enums
PRIVATE ${SourceHighlight_INCLUDE_DIR}
PRIVATE ${TAWASHI_SOURCE_ROOT}/lib/utf8_v2_3_4/source
PUBLIC ${TAWASHI_SOURCE_ROOT}/lib/spdlog/include
)
target_link_libraries(${PROJECT_NAME}
PRIVATE ${Boost_LIBRARIES}
PRIVATE incredis
PRIVATE ${SourceHighlight_LIBRARIES}
PUBLIC mstch
PRIVATE houdini
PRIVATE pthread
)
target_compile_definitions(${PROJECT_NAME}
PRIVATE BOOST_SPIRIT_USE_PHOENIX_V3=1
PUBLIC $<$<CONFIG:Debug>:SPDLOG_DEBUG_ON>
PUBLIC $<$<CONFIG:Debug>:SPDLOG_TRACE_ON>
)
target_compile_options(${PROJECT_NAME}
PRIVATE -fdiagnostics-color=always
)

View file

@ -0,0 +1,182 @@
/* 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 "cgi_env.hpp"
#include "cgi_environment_vars.hpp"
#include "duckhandy/lexical_cast.hpp"
#include <cassert>
#include <ciso646>
#include <boost/spirit/include/qi_core.hpp>
#include <boost/spirit/include/qi_numeric.hpp>
#include <boost/spirit/include/qi_plus.hpp>
#include <boost/spirit/include/qi_raw.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/phoenix/object/construct.hpp>
#include <boost/phoenix/bind/bind_member_function.hpp>
#include <boost/phoenix/operator.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/phoenix/stl/container.hpp>
BOOST_FUSION_ADAPT_STRUCT(
tawashi::cgi::Env::VersionInfo,
(boost::string_ref, name)
(uint16_t, major)
(uint16_t, minor)
);
namespace tawashi {
namespace cgi {
namespace {
boost::optional<Env::VersionInfo> split_version (const std::string& parString) {
namespace px = boost::phoenix;
using boost::spirit::ascii::space;
using boost::spirit::qi::raw;
using boost::spirit::qi::char_;
using boost::string_ref;
using VerNum = boost::spirit::qi::uint_parser<uint16_t, 10, 1, 1>;
using RuleType = boost::spirit::qi::rule<std::string::const_iterator, string_ref(), boost::spirit::ascii::space_type>;
using boost::spirit::_1;
using boost::spirit::qi::_val;
using boost::phoenix::begin;
using boost::phoenix::size;
using boost::phoenix::construct;
using boost::make_optional;
using boost::optional;
assert(not parString.empty());
auto beg = parString.cbegin();
RuleType protocol = raw[+(char_ - '/')][_val = px::bind(&string_ref::substr, construct<string_ref>(px::ref(parString)), begin(_1) - px::ref(beg), size(_1))];
VerNum ver_num;
auto it_curr = parString.cbegin();
Env::VersionInfo retval;
const bool parse_ret = boost::spirit::qi::phrase_parse(
it_curr,
parString.end(),
protocol >> '/' >> ver_num >> '.' >> ver_num,
space,
retval
);
if (parse_ret and parString.end() == it_curr)
return make_optional(retval);
else
return optional<Env::VersionInfo>();
}
} //unnamed namespace
Env::Env() :
m_cgi_env(cgi_environment_vars())
{
}
Env::~Env() noexcept = default;
const std::string& Env::auth_type() const {
return m_cgi_env[CGIVars::AUTH_TYPE];
}
std::size_t Env::content_length() const {
using dhandy::lexical_cast;
const std::string& value = m_cgi_env[CGIVars::CONTENT_LENGTH];
return (value.empty() ? 0U : lexical_cast<std::size_t>(value));
}
const std::string& Env::content_type() const {
return m_cgi_env[CGIVars::CONTENT_TYPE];
}
auto Env::gateway_interface() const -> boost::optional<VersionInfo> {
return split_version(m_cgi_env[CGIVars::GATEWAY_INTERFACE]);
}
const std::string& Env::path_info() const {
return m_cgi_env[CGIVars::PATH_INFO];
}
const std::string& Env::path_translated() const {
return m_cgi_env[CGIVars::PATH_TRANSLATED];
}
const std::string& Env::query_string() const {
return m_cgi_env[CGIVars::QUERY_STRING];
}
const std::string& Env::remote_addr() const {
return m_cgi_env[CGIVars::REMOTE_ADDR];
}
const std::string& Env::remote_host() const {
return m_cgi_env[CGIVars::REMOTE_HOST];
}
const std::string& Env::remote_ident() const {
return m_cgi_env[CGIVars::REMOTE_IDENT];
}
const std::string& Env::remote_user() const {
return m_cgi_env[CGIVars::REMOTE_USER];
}
const std::string& Env::request_method() const {
return m_cgi_env[CGIVars::REQUEST_METHOD];
}
const std::string& Env::script_name() const {
return m_cgi_env[CGIVars::SCRIPT_NAME];
}
const std::string& Env::server_name() const {
return m_cgi_env[CGIVars::SERVER_NAME];
}
uint16_t Env::server_port() const {
using dhandy::lexical_cast;
const std::string& value = m_cgi_env[CGIVars::SERVER_PORT];
return (value.empty() ? 0U : lexical_cast<uint16_t>(value));
}
auto Env::server_protocol() const -> boost::optional<VersionInfo> {
return split_version(m_cgi_env[CGIVars::SERVER_PROTOCOL]);
}
const std::string& Env::server_software() const {
return m_cgi_env[CGIVars::SERVER_SOFTWARE];
}
Env::GetMapType Env::query_string_split() const {
GetMapType retval;
const auto urlencoded_values = split_env_vars(m_cgi_env[CGIVars::QUERY_STRING]);
retval.reserve(urlencoded_values.size());
for (auto& itm : urlencoded_values) {
retval[m_houdini.unescape_url(itm.first)] = m_houdini.unescape_url(itm.second);
}
return retval;
}
std::ostream& Env::print_all (std::ostream& parStream, const char* parNewline) const {
for (std::size_t z = 0; z < m_cgi_env.size(); ++z) {
parStream << CGIVars::_from_integral(z) <<
" = \"" << m_cgi_env[z] << '"' << parNewline;
}
return parStream;
}
} //namespace cgi
} //namespace tawashi

View file

@ -0,0 +1,73 @@
/* 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 "split_get_vars.hpp"
#include "duckhandy/compatibility.h"
#include "escapist.hpp"
#include <vector>
#include <string>
#include <boost/utility/string_ref.hpp>
#include <cstdint>
#include <iostream>
#include <boost/optional.hpp>
#include <boost/container/flat_map.hpp>
namespace tawashi {
namespace cgi {
class Env {
public:
struct VersionInfo {
boost::string_ref name;
uint16_t major;
uint16_t minor;
};
typedef boost::container::flat_map<std::string, std::string> GetMapType;
Env();
~Env() noexcept;
const std::string& auth_type() const;
std::size_t content_length() const;
const std::string& content_type() const;
boost::optional<VersionInfo> gateway_interface() const a_pure;
const std::string& path_info() const;
const std::string& path_translated() const;
const std::string& query_string() const;
const std::string& remote_addr() const;
const std::string& remote_host() const;
const std::string& remote_ident() const;
const std::string& remote_user() const;
const std::string& request_method() const;
const std::string& script_name() const;
const std::string& server_name() const;
uint16_t server_port() const a_pure;
boost::optional<VersionInfo> server_protocol() const a_pure;
const std::string& server_software() const;
GetMapType query_string_split() const a_pure;
std::ostream& print_all (std::ostream& parStream, const char* parNewline) const;
private:
std::vector<std::string> m_cgi_env;
Escapist m_houdini;
};
} //namespace cgi
} //namespace tawashi

View file

@ -0,0 +1,35 @@
/* 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 "cgi_environment_vars.hpp"
#include "get_env.hpp"
#include <utility>
namespace tawashi {
std::vector<std::string> cgi_environment_vars() {
using boost::string_ref;
std::vector<std::string> retlist;
retlist.reserve(CGIVars::_size());
for (CGIVars var : CGIVars::_values()) {
auto value = get_env_as<std::string>(var._to_string(), "");
retlist.push_back(std::move(value));
}
return retlist;
}
} //namespace tawashi

View file

@ -0,0 +1,47 @@
/* 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>
#include "enum.h"
namespace tawashi {
BETTER_ENUM(CGIVars, std::size_t,
AUTH_TYPE = 0,
HTTP_USER_AGENT,
CONTENT_LENGTH,
CONTENT_TYPE,
GATEWAY_INTERFACE,
PATH_INFO,
PATH_TRANSLATED,
QUERY_STRING,
REMOTE_ADDR,
REMOTE_HOST,
REMOTE_IDENT,
REMOTE_USER,
REQUEST_METHOD,
SCRIPT_NAME,
SERVER_NAME,
SERVER_PORT,
SERVER_PROTOCOL,
SERVER_SOFTWARE
);
std::vector<std::string> cgi_environment_vars();
} //namespace tawashi

View file

@ -0,0 +1,68 @@
/* 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 "cgi_post.hpp"
#include "cgi_env.hpp"
#include "split_get_vars.hpp"
#include "escapist.hpp"
#include "sanitized_utf8.hpp"
#include <iostream>
#include <iterator>
#include <algorithm>
#include <string>
#include <cassert>
#include <ciso646>
namespace tawashi {
namespace cgi {
namespace {
} //unnamed namespace
const PostMapType& read_post (const Env& parEnv) {
static bool already_read = false;
static PostMapType map;
static std::string original_data;
if (not already_read) {
assert(original_data.empty());
assert(map.empty());
const auto input_len = parEnv.content_length();
if (input_len > 0) {
original_data.reserve(input_len);
std::copy_n(
std::istream_iterator<char>(std::cin),
input_len,
std::back_inserter(original_data)
);
original_data = sanitized_utf8(original_data);
Escapist houdini;
for (auto& itm : split_env_vars(original_data)) {
std::string key(houdini.unescape_url(itm.first));
std::string val(houdini.unescape_url(itm.second));
map[std::move(key)] = std::move(val);
}
}
already_read = true;
}
return map;
}
} //namespace cgi
} //namespace tawashi

View file

@ -0,0 +1,32 @@
/* 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 <boost/container/flat_map.hpp>
#include <string>
namespace tawashi {
namespace cgi {
class Env;
typedef boost::container::flat_map<std::string, std::string> PostMapType;
const PostMapType& read_post (const Env& parEnv);
} //namespace cgi
} //namespace tawashi

View file

@ -0,0 +1,74 @@
/* 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 "escapist.hpp"
#include "houdini.h"
#include <cstddef>
#include <cassert>
namespace tawashi {
Escapist::Escapist() :
m_gh_buf(new(&m_gh_buf_mem) gh_buf GH_BUF_INIT)
{
assert(reinterpret_cast<uintptr_t>(&m_gh_buf_mem) == reinterpret_cast<uintptr_t>(m_gh_buf));
static_assert(sizeof(implem::DummyGHBuf) == sizeof(gh_buf), "Dummy struct has the wrong size");
static_assert(sizeof(gh_buf) == sizeof(m_gh_buf_mem), "Static memory for gh_buf has the wrong size");
static_assert(alignof(gh_buf) == alignof(m_gh_buf_mem), "Static memory for gh_buf has the wrong alignment");
}
Escapist::~Escapist() noexcept {
gh_buf_free(static_cast<gh_buf*>(m_gh_buf));
static_cast<gh_buf*>(m_gh_buf)->~gh_buf();
}
std::string Escapist::unescape_url (const boost::string_ref& parURL) const {
if (parURL.empty())
return std::string();
assert(m_gh_buf);
gh_buf* const buf = static_cast<gh_buf*>(m_gh_buf);
const int escaped = houdini_unescape_url(
buf,
reinterpret_cast<const uint8_t*>(parURL.data()),
parURL.size()
);
if (0 == escaped)
return std::string(parURL.data(), parURL.size());
else
return std::string(buf->ptr, buf->size);
}
std::string Escapist::escape_html (const boost::string_ref& parHtml) const {
if (parHtml.empty())
return std::string();
assert(m_gh_buf);
gh_buf* const buf = static_cast<gh_buf*>(m_gh_buf);
const int escaped = houdini_escape_html0(
buf,
reinterpret_cast<const uint8_t*>(parHtml.data()),
parHtml.size(),
1
);
if (0 == escaped)
return std::string(parHtml.data(), parHtml.size());
else
return std::string(buf->ptr, buf->size);
}
} //namespace tawashi

View file

@ -0,0 +1,46 @@
/* 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 <boost/utility/string_ref.hpp>
#include <string>
#include <memory>
namespace tawashi {
namespace implem {
//not actually used, only needed to get the size of the actual gh_buf
//without including its full header file
struct DummyGHBuf{
char *ptr;
size_t asize, size;
};
} //namespace implem
class Escapist {
public:
Escapist();
~Escapist() noexcept;
std::string unescape_url (const boost::string_ref& parURL) const;
std::string escape_html (const boost::string_ref& parHtml) const;
private:
std::aligned_storage<sizeof(implem::DummyGHBuf), alignof(implem::DummyGHBuf)>::type m_gh_buf_mem;
void* m_gh_buf;
};
} //namespace tawashi

View file

@ -0,0 +1,48 @@
/* 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 "get_env.hpp"
#include "duckhandy/lexical_cast.hpp"
#include "sanitized_utf8.hpp"
#include <cstdlib>
namespace tawashi {
boost::optional<std::string> get_env (const char* parName) {
using boost::string_ref;
using boost::make_optional;
using boost::optional;
const char* const raw_getvar = secure_getenv(parName);
if (raw_getvar)
return sanitized_utf8(boost::string_ref(raw_getvar));
else
return optional<std::string>();
}
template <>
std::string get_env_as (const char* parName, const std::string& parDefault) {
auto var = get_env(parName);
return (var ? *var : parDefault);
}
template <>
std::size_t get_env_as (const char* parName, const std::size_t& parDefault) {
using dhandy::lexical_cast;
auto var = get_env(parName);
return (var ? lexical_cast<std::size_t>(*var) : parDefault);
}
} //namespace tawashi

View file

@ -0,0 +1,39 @@
/* 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 <boost/utility/string_ref.hpp>
#include <string>
#if !defined(_GNU_SOURCE)
# define _GNU_SOURCE
#endif
#include <cstddef>
#include <utility>
#include <boost/optional.hpp>
namespace tawashi {
boost::optional<std::string> get_env (const char* parName);
template <typename A>
A get_env_as (const char* parName, const A& parDefault);
template <>
std::string get_env_as (const char* parName, const std::string& parDefault);
template <>
std::size_t get_env_as (const char* parName, const std::size_t& parDefault);
} //namespace tawashi

View file

@ -0,0 +1,27 @@
/* 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) :
Response(Response::ContentType, "text/html", "index", parSettings, false)
{
}
} //namespace tawashi

View file

@ -0,0 +1,30 @@
/* 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_ref.hpp>
namespace tawashi {
class IndexResponse : public Response {
public:
explicit IndexResponse (const Kakoune::SafePtr<SettingsBag>& parSettings);
private:
};
} //namespace tawashi

View file

@ -0,0 +1,135 @@
/* 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_plus.hpp>
#include <boost/spirit/include/qi_raw.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_kleene.hpp>
#include <boost/spirit/include/qi_rule.hpp>
#include <boost/spirit/include/qi_as_string.hpp>
#include <boost/spirit/include/qi_eol.hpp>
#include <boost/spirit/include/qi_grammar.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>
namespace tawashi {
namespace {
typedef boost::string_ref 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_head;
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::ascii::space;
using boost::spirit::qi::_val;
using boost::spirit::_1;
using boost::spirit::qi::char_;
using boost::spirit::qi::eol;
using boost::spirit::qi::raw;
using boost::string_ref;
typedef IniFile::KeyValueMapType::value_type refpair;
section_head = '[' >> raw[+(char_ - ']')][_val = px::bind(
&string_ref::substr,
px::construct<string_ref>(px::ref(*m_master_string)),
px::begin(_1) - px::ref(m_begin), px::size(_1)
)] >> ']' >> eol;
key = raw[+(char_ - '=')][_val = px::bind(
&string_ref::substr,
px::construct<string_ref>(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[*(char_ - eol)][px::bind(&refpair::second, _val) = px::bind(
&string_ref::substr,
px::construct<string_ref>(px::ref(*m_master_string)),
px::begin(_1) - px::ref(m_begin), px::size(_1)
)] >> eol;
key_values = *key_value;
start = *(section_head >> key_values);
}
IniFile::IniMapType parse_ini (const std::string* parIni) {
using boost::spirit::qi::blank;
using boost::spirit::qi::blank_type;
IniGrammar<std::string::const_iterator, blank_type> gramm(parIni);
IniFile::IniMapType result;
std::string::const_iterator start_it = parIni->cbegin();
/*const bool parse_ok =*/ boost::spirit::qi::phrase_parse(
start_it,
parIni->cend(),
gramm,
blank,
result
);
//assert(parse_ok and (parIni->cend() == start_it));
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))
{
}
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);
}
IniFile::~IniFile() noexcept {
}
} //namespace tawashi

View file

@ -0,0 +1,47 @@
/* 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_ref.hpp>
#include <string>
namespace tawashi {
class IniFile : public Kakoune::SafeCountable {
public:
typedef boost::container::flat_map<boost::string_ref, boost::string_ref> KeyValueMapType;
typedef boost::container::flat_map<boost::string_ref, 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;
const IniMapType& parsed() const { return m_map; }
private:
std::string m_raw_ini;
IniMapType m_map;
};
} //namespace tawashi

View file

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

View file

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

View file

@ -0,0 +1,52 @@
/* 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 <ostream>
#include <boost/utility/string_ref.hpp>
#include <cassert>
#include <ciso646>
namespace tawashi {
class Logger {
public:
explicit Logger (std::ostream* parStream);
~Logger() noexcept;
template <typename... Args>
void log (int parLevel, const boost::string_ref& parFormat, Args&&... parArgs);
private:
static const constexpr int LogLevels = 3;
std::array<std::ostream*, LogLevels> m_outs;
};
template <typename... Args>
void Logger::log (int parLevel, const boost::string_ref& parFormat, Args&&... parArgs) {
assert(parLevel >= 0 and parLevel < LogLevels);
if (nullptr == m_outs[parLevel])
return;
bool percentage_seq = false;
for (auto chara : parFormat) {
if (percentage_seq) {
}
else {
}
}
}
} //namespace tawashi

View file

@ -0,0 +1,33 @@
/* 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 "enum.h"
#include <spdlog/spdlog.h>
namespace tawashi {
BETTER_ENUM(LoggingLevels, int,
trace = spdlog::level::trace,
debug = spdlog::level::debug,
info = spdlog::level::info,
warn = spdlog::level::warn,
err = spdlog::level::err,
critical = spdlog::level::critical,
off = spdlog::level::off
);
} //namespace tawashi

View file

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

View file

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

View file

@ -0,0 +1,100 @@
/* 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 "incredis/incredis.hpp"
#include "settings_bag.hpp"
#include "escapist.hpp"
#include <ciso646>
#include <srchilite/sourcehighlight.h>
#include <srchilite/langmap.h>
#include <sstream>
namespace tawashi {
namespace {
const char g_nolang_token[] = "plaintext";
} //unnamed namespace
PastieResponse::PastieResponse (const Kakoune::SafePtr<SettingsBag>& parSettings) :
Response(Response::ContentType, "text/html", "text", parSettings, true),
m_langmap_dir(parSettings->as<std::string>("langmap_dir")),
m_plain_text(false),
m_syntax_highlight(true)
{
}
void PastieResponse::on_process() {
auto env = cgi_env().query_string_split();
const std::string& query_str(cgi_env().query_string());
if (env["m"] == "plain" or query_str.empty()) {
this->change_type(Response::ContentType, "text/plain; charset=utf-8");
m_plain_text = true;
}
else if (query_str == g_nolang_token) {
m_syntax_highlight = false;
}
else {
srchilite::LangMap lang_map(m_langmap_dir, "lang.map");
lang_map.open();
if (not query_str.empty())
m_lang_file = lang_map.getFileName(query_str);
else
m_lang_file = "default.lang";
}
}
void PastieResponse::on_mustache_prepare (mstch::map& parContext) {
using opt_string = redis::IncRedis::opt_string;
using opt_string_list = redis::IncRedis::opt_string_list;
auto token = boost::string_ref(cgi_env().path_info()).substr(1);
auto& redis = this->redis();
opt_string_list pastie_reply = redis.hmget(token, "pastie");
opt_string pastie = (pastie_reply and not pastie_reply->empty() ? (*pastie_reply)[0] : opt_string());
srchilite::SourceHighlight highlighter;
highlighter.setDataDir(settings().as<std::string>("langmap_dir"));
highlighter.setGenerateEntireDoc(false);
highlighter.setGenerateLineNumbers(true);
const auto lang = m_lang_file;
std::string processed_pastie;
if (m_syntax_highlight) {
processed_pastie = std::move(*pastie);
}
else {
Escapist houdini;
processed_pastie = houdini.escape_html(*pastie);
}
if (not m_plain_text and m_syntax_highlight) {
std::istringstream iss(std::move(processed_pastie));
std::ostringstream oss;
highlighter.highlight(iss, oss, lang);
processed_pastie = oss.str();
}
parContext["pastie"] = std::move(processed_pastie);
}
std::string PastieResponse::on_mustache_retrieve() {
if (m_plain_text)
return "{{pastie}}";
else
return load_mustache();
}
} //namespace tawashi

View file

@ -0,0 +1,39 @@
/* 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_ref.hpp>
namespace tawashi {
class PastieResponse : public Response {
public:
explicit PastieResponse (const Kakoune::SafePtr<SettingsBag>& parSettings);
private:
virtual void 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;
};
} //namespace tawashi

View file

@ -0,0 +1,254 @@
/* 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_ref 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_ref>* parOut, boost::string_ref parPath) {
auto from = parPath.begin();
boost::string_ref::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_ref 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_ref> 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_ref 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_ref ref(src);
m_pool.insert(ref, &src);
}
void PathName::join (boost::string_ref 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_ref basename (const PathName& parPath) {
static const char* const empty = "";
const auto sz = parPath.atom_count();
if (not sz) {
return boost::string_ref(empty);
}
assert(sz > 0);
return parPath[sz - 1];
}
PathName& PathName::pop_right() {
m_pool.pop();
return *this;
}
bool PathName::operator!= (const PathName& parOther) const {
const auto count = atom_count();
if (count != parOther.atom_count()) {
return true;
}
for (std::size_t z = 0; z < count; ++z) {
if ((*this)[z] != parOther[z]) {
return true;
}
}
return false;
}
bool PathName::operator== (const PathName& parOther) const {
const auto count = atom_count();
if (count != parOther.atom_count()) {
return false;
}
for (std::size_t z = 0; z < count; ++z) {
if ((*this)[z] != parOther[z]) {
return false;
}
}
return true;
}
std::size_t PathName::str_path_size() const {
return calc_join_size(m_pool, is_absolute());
}
} //namespace mchlib

View file

@ -0,0 +1,67 @@
/* 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_ref.hpp>
#include <map>
#include <iostream>
namespace mchlib {
class PathName : public Kakoune::SafeCountable {
public:
PathName ( PathName&& ) = default;
PathName ( const PathName& ) = default;
explicit PathName ( boost::string_ref 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_ref operator[] ( std::size_t parIndex ) const;
void join ( const PathName& parOther );
void join ( const char* parOther );
void join ( boost::string_ref 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_ref basename ( const PathName& parPath );
} //namespace mchlib
#endif

View file

@ -0,0 +1,70 @@
/* 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_ref.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_ref<C>>
class StringPool {
typedef std::pair<StrRef, const Str*> StringListPair;
typedef std::vector<std::pair<Str, std::size_t>> PoolType;
typedef std::vector<StringListPair> StringListType;
typedef std::function<StrRef(const StringListPair&)> FuncGetFirst;
public:
typedef C char_type;
typedef Str string_type;
typedef StrRef stringref_type;
typedef boost::transform_iterator<FuncGetFirst, typename StringListType::const_iterator> const_iterator;
StringPool ( void ) = default;
~StringPool ( void ) noexcept = default;
template <typename ItR>
void update ( ItR parDataBeg, ItR parDataEnd );
void update ( const StringPool& parOther );
void insert ( const std::vector<stringref_type>& parStrings, const string_type* parBaseString );
void insert ( stringref_type parString, const string_type* parBaseString );
const string_type* ptr_to_literal ( const char* parLiteral );
std::size_t size ( void ) const { return m_strings.size(); }
bool empty ( void ) const { return m_strings.empty(); }
const_iterator begin ( void ) const;
const_iterator end ( void ) const;
const string_type* get_stringref_source ( std::size_t parIndex ) const;
const stringref_type& operator[] ( std::size_t parIndex ) const;
void pop ( void );
private:
PoolType m_pool;
StringListType m_strings;
};
} //namespace mchlib
#include "stringpool.inl"
#endif

View file

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

View file

@ -0,0 +1,231 @@
/* 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 "incredis/incredis.hpp"
#include "settings_bag.hpp"
#include "tawashiConfig.h"
#include "duckhandy/stringize.h"
#include "pathname/pathname.hpp"
#include "list_highlight_langs.hpp"
#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() + '/';
}
}
redis::IncRedis make_incredis (const tawashi::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\"");
}
}
boost::optional<std::string> load_whole_file (const std::string& parWebsiteRoot, const char* parSuffix, const std::string& parName, bool parThrow) {
std::ostringstream oss;
oss << parWebsiteRoot << parName << parSuffix;
spdlog::get("statuslog")->debug("Trying to load \"{}\"", oss.str());
std::ifstream if_mstch(oss.str(), std::ios::binary | std::ios::in);
if (not if_mstch) {
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;
};
} //unnamed namespace
Response::Response (Types parRespType, std::string&& parValue, std::string&& parPageBaseName, const Kakoune::SafePtr<SettingsBag>& parSettings, bool parWantRedis) :
m_resp_value(std::move(parValue)),
//m_page_basename(fetch_page_basename(m_cgi_env)),
m_settings(parSettings),
m_website_root(make_root_path(*parSettings)),
m_page_basename(std::move(parPageBaseName)),
m_resp_type(parRespType),
m_header_sent(false)
{
if (parWantRedis) {
m_redis = std::make_unique<redis::IncRedis>(make_incredis(*parSettings));
m_redis->connect();
}
mstch::config::escape = &disable_mstch_escaping;
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
statuslog->info("Preparing Response for {} request to {}; size: {}",
cgi_env().request_method(),
cgi_env().query_string(),
cgi_env().content_length()
);
}
Response::~Response() noexcept = default;
void Response::on_process() {
}
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", m_settings->as<std::string>("base_uri")},
{"languages", make_mstch_langmap(*m_settings)}
};
if (m_redis) {
SPDLOG_TRACE(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();
}
SPDLOG_TRACE(statuslog, "Raising event on_process");
this->on_process();
SPDLOG_TRACE(statuslog, "Raising event on_mustache_prepare");
this->on_mustache_prepare(mustache_context);
m_header_sent = true;
switch (m_resp_type) {
case ContentType:
SPDLOG_TRACE(statuslog, "Response is a Content-type (data)");
std::cout << "Content-type: " << m_resp_value << "\n\n";
break;
case Location:
SPDLOG_TRACE(statuslog, "Response is a Location (redirect)");
std::cout << "Location: " << m_resp_value << "\n\n";
break;
}
SPDLOG_TRACE(statuslog, "Rendering in mustache");
std::cout << 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");
std::cout.flush();
}
std::string Response::on_mustache_retrieve() {
return load_mustache();
}
const cgi::Env& Response::cgi_env() const {
return m_cgi_env;
}
void Response::change_type (Types parRespType, std::string&& parValue) {
assert(not m_header_sent);
assert(not parValue.empty());
m_resp_type = parRespType;
m_resp_value = std::move(parValue);
}
const boost::string_ref& Response::base_uri() const {
return m_settings->at("base_uri");
}
const std::string& Response::page_basename() const {
return m_page_basename;
}
std::string Response::load_mustache() const {
boost::optional<std::string> content = load_whole_file(m_website_root, ".html.mstch", page_basename(), true);
return *content;
}
redis::IncRedis& Response::redis() const {
assert(m_redis);
return *m_redis;
}
const SettingsBag& Response::settings() const {
assert(m_settings);
return *m_settings;
}
} //namespace tawashi

View file

@ -0,0 +1,70 @@
/* 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 "cgi_env.hpp"
#include "mstch/mstch.hpp"
#include "kakoune/safe_ptr.hh"
#include <string>
#include <iostream>
#include <boost/utility/string_ref.hpp>
#include <memory>
namespace redis {
class IncRedis;
} //namespace redis
namespace tawashi {
class SettingsBag;
class Response {
public:
virtual ~Response() noexcept;
void send();
protected:
enum Types {
ContentType,
Location
};
Response (Types parRespType, std::string&& parValue, std::string&& parPageBaseName, const Kakoune::SafePtr<SettingsBag>& parSettings, bool parWantRedis);
const cgi::Env& cgi_env() const;
void change_type (Types parRespType, std::string&& parValue);
const boost::string_ref& base_uri() const;
const std::string& page_basename() const;
redis::IncRedis& redis() const;
const SettingsBag& settings() const;
virtual std::string load_mustache() const;
private:
virtual void on_process();
virtual void on_mustache_prepare (mstch::map& parContext);
virtual std::string on_mustache_retrieve();
cgi::Env m_cgi_env;
std::string m_resp_value;
Kakoune::SafePtr<SettingsBag> m_settings;
std::string m_website_root;
std::string m_page_basename;
Types m_resp_type;
std::unique_ptr<redis::IncRedis> m_redis;
bool m_header_sent;
};
} //namespace tawashi

View file

@ -0,0 +1,64 @@
/* 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 <functional>
#include <boost/container/flat_map.hpp>
namespace tawashi {
namespace {
} //unnamed namespace
struct ResponseFactory::LocalData {
Kakoune::SafePtr<SettingsBag> settings;
boost::container::flat_map<std::string, ResponseMakerFunc> makers;
ResponseMakerFunc jolly_maker;
};
ResponseFactory::ResponseFactory (const Kakoune::SafePtr<SettingsBag>& parSettings) :
m_local_data(std::make_unique<LocalData>())
{
m_local_data->settings = parSettings;
}
ResponseFactory::~ResponseFactory() noexcept = default;
std::unique_ptr<Response> ResponseFactory::make_response (const boost::string_ref& parName) {
//spdlog::get("statuslog")->info("making response object for \"{}\"", parName);
auto maker_it = m_local_data->makers.find(std::string(parName.data(), parName.size()));
if (m_local_data->makers.end() != maker_it) {
return maker_it->second(m_local_data->settings);
}
else if (m_local_data->jolly_maker) {
return m_local_data->jolly_maker(m_local_data->settings);
}
else {
assert(false);
return std::unique_ptr<Response>();
}
}
void ResponseFactory::register_maker (std::string&& parName, ResponseMakerFunc parMaker) {
m_local_data->makers[std::move(parName)] = parMaker;
}
void ResponseFactory::register_jolly_maker (ResponseMakerFunc parMaker) {
m_local_data->jolly_maker = parMaker;
}
} //namespace tawashi

View file

@ -0,0 +1,43 @@
/* 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 <memory>
namespace tawashi {
class SettingsBag;
class ResponseFactory {
public:
typedef std::function<std::unique_ptr<Response>(const Kakoune::SafePtr<SettingsBag>&)> ResponseMakerFunc;
explicit ResponseFactory (const Kakoune::SafePtr<SettingsBag>& parSettings);
~ResponseFactory() noexcept;
std::unique_ptr<Response> make_response(const boost::string_ref& parName);
void register_maker (std::string&& parName, ResponseMakerFunc parMaker);
void register_jolly_maker (ResponseMakerFunc parMaker);
private:
struct LocalData;
std::unique_ptr<LocalData> m_local_data;
};
} //namespace tawashi

View file

@ -0,0 +1,96 @@
/*
Copyright 2016, 2017 Michele "King_DuckZ" Santullo
This file is part of MyCurry.
MyCurry 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.
MyCurry 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 MyCurry. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kakoune/safe_ptr.hh"
#include <utility>
namespace curry {
template <typename T>
class SafeStackObject {
public:
typedef Kakoune::SafePtr<T> safe_ptr;
SafeStackObject();
SafeStackObject (SafeStackObject&& parOther);
SafeStackObject (const SafeStackObject& parOther) = delete;
template <typename... Args> explicit SafeStackObject (Args&&... parArgs);
~SafeStackObject() noexcept = default;
SafeStackObject& operator= (SafeStackObject&& parOther) = delete;
SafeStackObject& operator= (const SafeStackObject& parOther) = delete;
operator Kakoune::SafePtr<T>&();
T& operator*();
safe_ptr& operator->();
private:
T m_obj;
safe_ptr m_obj_ptr;
};
template <typename T>
SafeStackObject<T>::SafeStackObject() :
m_obj(),
m_obj_ptr(&m_obj)
{
}
template <typename T>
SafeStackObject<T>::SafeStackObject (SafeStackObject&& parOther) :
m_obj(std::move(parOther.m_obj)),
m_obj_ptr(&m_obj)
{
}
template <typename T>
template <typename... Args>
SafeStackObject<T>::SafeStackObject (Args&&... parArgs) :
m_obj(std::forward<Args>(parArgs)...),
m_obj_ptr(&m_obj)
{
}
//template <typename T>
//SafeStackObject& SafeStackObject<T>::operator= (SafeStackObject&& parOther) {
// m_obj = std::move(parOther.m_obj);
// m_obj_ptr = std::move(parOther.m_obj_ptr);
// m_ob
//}
//template <typename T>
//SafeStackObject& SafeStackObject<T>::operator= (const SafeStackObject& parOther) {
//}
template <typename T>
SafeStackObject<T>::operator Kakoune::SafePtr<T>&() {
return m_obj_ptr;
}
template <typename T>
T& SafeStackObject<T>::operator*() {
return *m_obj_ptr;
}
template <typename T>
auto SafeStackObject<T>::operator->() -> safe_ptr& {
return m_obj_ptr;
}
} //namespace curry

View file

@ -0,0 +1,29 @@
/* 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 "sanitized_utf8.hpp"
#include "utf8.h"
#include <iterator>
namespace tawashi {
std::string sanitized_utf8 (const boost::string_ref& parStr) {
std::string sanitized;
sanitized.reserve(parStr.size());
utf8::replace_invalid(parStr.begin(), parStr.end(), std::back_inserter(sanitized));
return sanitized;
}
} //namespace tawashi

View file

@ -0,0 +1,25 @@
/* 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 <boost/utility/string_ref.hpp>
#include <string>
namespace tawashi {
std::string sanitized_utf8 (const boost::string_ref& parStr);
} //namespace tawashi

View file

@ -0,0 +1,99 @@
/* 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 <ciso646>
#include <cassert>
#include <cstdint>
#include <sstream>
#include <spdlog/spdlog.h>
namespace tawashi {
namespace {
const IniFile::KeyValueMapType* get_tawashi_node (const IniFile& parIni) {
auto it_found = parIni.parsed().find("tawashi");
if (parIni.parsed().end() != it_found) {
return &it_found->second;
}
else {
spdlog::get("statuslog")->warn("Couldn't find section [tawashi] in the settings file");
static const IniFile::KeyValueMapType empty_key_values;
return &empty_key_values;
}
}
} //unnamed namespace
SettingsBag::SettingsBag (const Kakoune::SafePtr<IniFile>& parIni) :
m_ini(parIni),
m_values(get_tawashi_node(*parIni))
{
assert(m_values);
}
SettingsBag::~SettingsBag() noexcept = default;
const boost::string_ref& SettingsBag::operator[] (boost::string_ref 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_ref parKey, boost::string_ref parValue) {
assert(m_defaults.find(parKey) == m_defaults.end());
m_defaults[parKey] = parValue;
}
template <>
std::string SettingsBag::as (boost::string_ref parIndex) const {
auto& setting = this->at(parIndex);
return std::string(setting.data(), setting.size());
}
template <>
bool SettingsBag::as (boost::string_ref 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_ref parIndex) const {
return dhandy::lexical_cast<uint16_t>(this->at(parIndex));
}
template <>
uint32_t SettingsBag::as (boost::string_ref parIndex) const {
return dhandy::lexical_cast<uint32_t>(this->at(parIndex));
}
template std::string SettingsBag::as<std::string> (boost::string_ref parIndex) const;
template bool SettingsBag::as<bool> (boost::string_ref parIndex) const;
template uint16_t SettingsBag::as<uint16_t> (boost::string_ref parIndex) const;
template uint32_t SettingsBag::as<uint32_t> (boost::string_ref parIndex) const;
} //namespace tawashi

View file

@ -0,0 +1,59 @@
/* 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"
#include <map>
#include <boost/utility/string_ref.hpp>
#include <functional>
#include <string>
#if defined(SPDLOG_DEBUG_ON)
# include <spdlog/spdlog.h>
#endif
namespace tawashi {
class SettingsBag : public Kakoune::SafeCountable {
typedef std::map<boost::string_ref, boost::string_ref> MapType;
public:
explicit SettingsBag (const Kakoune::SafePtr<IniFile>& parIni);
~SettingsBag() noexcept;
const boost::string_ref& operator[] (boost::string_ref parIndex) const;
const boost::string_ref& at (boost::string_ref parIndex) const;
template <typename T> T as (boost::string_ref parIndex) const;
void add_default (boost::string_ref parKey, boost::string_ref parValue);
private:
MapType m_defaults;
Kakoune::SafePtr<IniFile> m_ini;
const IniFile::KeyValueMapType* m_values;
};
template <>
inline boost::string_ref SettingsBag::as (boost::string_ref parIndex) const {
return (*this)[parIndex];
}
inline const boost::string_ref& SettingsBag::at (boost::string_ref parIndex) const {
#if defined(SPDLOG_DEBUG_ON)
SPDLOG_DEBUG(spdlog::get("statuslog"), "Retrieving setting \"{}\"", std::string(parIndex.data(), parIndex.size()));
#endif
return (*this)[parIndex];
}
} //namespace tawashi

View file

@ -0,0 +1,28 @@
/* 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 <spdlog/spdlog.h>
#include <boost/utility/string_ref.hpp>
namespace spdlog {
template <typename OStream>
inline OStream& operator<< (OStream& parOS, const boost::string_ref& parStr) {
return parOS << parStr;
}
} //namespace spdlog

View file

@ -0,0 +1,60 @@
/* 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 "split_get_vars.hpp"
#include <boost/algorithm/string/finder.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <ciso646>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/algorithm/find.hpp>
namespace tawashi {
RawKeyValueList split_env_vars (const std::string& parList) {
using MatchRange = boost::iterator_range<std::string::const_iterator>;
using boost::token_finder;
using boost::adaptors::transformed;
using boost::adaptors::filtered;
using boost::string_ref;
using boost::split_iterator;
using boost::make_iterator_range;
using boost::range::find;
//See:
//https://stackoverflow.com/questions/27999941/how-to-use-boostsplit-with-booststring-ref-in-boost-1-55
//http://www.boost.org/doc/libs/1_60_0/doc/html/boost/algorithm/token_finder.html
//https://stackoverflow.com/questions/20781090/difference-between-boostsplit-vs-boostiter-split
return boost::copy_range<RawKeyValueList>(
make_iterator_range(
split_iterator<std::string::const_iterator>(parList, token_finder([](char c){return '&'==c;})),
split_iterator<std::string::const_iterator>()
) |
filtered([](const MatchRange& r){ return not r.empty(); }) |
transformed([](const MatchRange& r){
auto eq = find(r, '=');
if (r.empty())
return std::pair<string_ref, string_ref>();
if (r.end() == eq)
return std::make_pair(string_ref(&*r.begin(), r.size()), string_ref());
else
return std::make_pair(string_ref(&*r.begin(), eq - r.begin()), string_ref(&*(eq + 1), r.size() - (eq - r.begin() + 1)));
})
);
}
} //namespace tawashi

View file

@ -0,0 +1,29 @@
/* 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 <boost/utility/string_ref.hpp>
#include <vector>
#include <utility>
namespace tawashi {
typedef std::vector<std::pair<boost::string_ref, boost::string_ref>> RawKeyValueList;
RawKeyValueList split_env_vars ( const std::string& parCommaSeparatedList ) a_pure;
} //namespace tawashi

View file

@ -0,0 +1,130 @@
/* 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 "incredis/incredis.hpp"
#include "cgi_post.hpp"
#include "num_to_token.hpp"
#include "settings_bag.hpp"
#include "duckhandy/compatibility.h"
#include "duckhandy/lexical_cast.hpp"
#include <ciso646>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <boost/lexical_cast.hpp>
namespace tawashi {
namespace {
const char g_post_key[] = "pastie";
const char g_language_key[] = "lang";
const char g_duration_key[] = "ttl";
class MissingPostVarError : public std::runtime_error {
public:
MissingPostVarError (const std::string& parMsg) : std::runtime_error(parMsg) {}
};
template <std::size_t N>
inline boost::string_ref make_string_ref (const char (&parStr)[N]) a_always_inline;
template <std::size_t N>
boost::string_ref make_string_ref (const char (&parStr)[N]) {
static_assert(N > 0, "wat?");
return boost::string_ref(parStr, N - 1);
}
boost::string_ref get_value_from_post (const cgi::PostMapType& parPost, boost::string_ref parKey) {
std::string key(parKey.data(), parKey.size());
auto post_data_it = parPost.find(key);
if (parPost.end() == post_data_it) {
std::ostringstream oss;
oss << "can't find POST data field \"" << parKey << '"';
throw MissingPostVarError(oss.str());
}
return post_data_it->second;
}
} //unnamed namespace
SubmitPasteResponse::SubmitPasteResponse (const Kakoune::SafePtr<SettingsBag>& parSettings) :
Response(Response::ContentType, "text/plain", "paste", parSettings, true)
{
}
void SubmitPasteResponse::on_process() {
auto post = cgi::read_post(cgi_env());
boost::string_ref pastie;
boost::string_ref lang;
boost::string_ref duration;
try {
pastie = get_value_from_post(post, make_string_ref(g_post_key));
}
catch (const MissingPostVarError& e) {
m_error_message = e.what();
return;
}
try {
lang = get_value_from_post(post, make_string_ref(g_language_key));
duration = get_value_from_post(post, make_string_ref(g_duration_key));
}
catch (const MissingPostVarError&) {
}
const SettingsBag& settings = this->settings();
const auto max_sz = settings.as<uint32_t>("max_pastie_size");
if (pastie.size() < settings.as<uint32_t>("min_pastie_size"))
return;
if (max_sz and pastie.size() > max_sz) {
if (settings.as<bool>("truncate_long_pasties"))
pastie = pastie.substr(0, max_sz);
else
return;
}
//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);
boost::optional<std::string> token = submit_to_redis(pastie, duration_int, lang);
if (token) {
std::ostringstream oss;
oss << base_uri() << '/' << *token;
if (not lang.empty())
oss << '?' << lang;
this->change_type(Response::Location, oss.str());
}
}
boost::optional<std::string> SubmitPasteResponse::submit_to_redis (const boost::string_ref& parText, uint32_t parExpiry, const boost::string_ref& parLang) const {
auto& redis = this->redis();
if (not redis.is_connected())
return boost::optional<std::string>();
const auto next_id = redis.incr("paste_counter");
const 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)
) {
if (redis.expire(token, parExpiry))
return boost::make_optional(token);
}
return boost::optional<std::string>();
}
} //namespace tawashi

View file

@ -0,0 +1,36 @@
/* 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_ref.hpp>
namespace tawashi {
class SubmitPasteResponse : public Response {
public:
explicit SubmitPasteResponse (const Kakoune::SafePtr<SettingsBag>& parSettings);
private:
virtual void on_process() override;
boost::optional<std::string> submit_to_redis (const boost::string_ref& parText, uint32_t parExpiry, const boost::string_ref& parLang) const;
std::string m_error_message;
};
} //namespace tawashi