1
0
Fork 0
mirror of https://github.com/KingDuckZ/kamokan.git synced 2025-08-03 12:50:02 +00:00

Rename tawashi to duckbin.

The library that used to be "tawashi_implem" now
is simply called tawashi.
This commit is contained in:
King_DuckZ 2017-06-02 21:53:27 +01:00
parent 2f00014758
commit 9cda58d0c0
67 changed files with 156 additions and 128 deletions

View file

@ -1,27 +1,70 @@
project(tawashi LANGUAGES CXX)
project(tawashi VERSION 0.1.11 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)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(${PROJECT_NAME}
main.cpp
)
target_link_libraries(${PROJECT_NAME}
PRIVATE tawashi_implem
#hack - add duckhandy to the project instead of picking from inside redis
PRIVATE duckhandy
)
set_target_properties(
${PROJECT_NAME}
PROPERTIES SUFFIX .cgi
add_library(${PROJECT_NAME} STATIC
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
)
target_include_directories(${PROJECT_NAME}
PRIVATE ${TAWASHI_GEN_INCLUDE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include
PUBLIC ${TAWASHI_SOURCE_ROOT}/lib/kakoune
PUBLIC ${TAWASHI_SOURCE_ROOT}/lib/mstch/include
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)
install(TARGETS ${PROJECT_NAME}
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib/static
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
)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/tawashi_config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/tawashi_config.h"
)

246
src/tawashi/cgi_env.cpp Normal file
View file

@ -0,0 +1,246 @@
/* 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 "tawashi_exception.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>
#include <boost/algorithm/string/predicate.hpp>
#include <spdlog/spdlog.h>
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>();
}
std::size_t calculate_skip_path_length (const boost::string_ref& parPathInfo, const boost::string_ref& parBasePath) {
const std::size_t base_path_tr_slash = (not parBasePath.empty() and parBasePath[parBasePath.size() - 1] == '/' ? 1 : 0);
boost::string_ref base_path = parBasePath.substr(0, parBasePath.size() - base_path_tr_slash);
SPDLOG_TRACE(spdlog::get("statuslog"), "calculating skip prefix for PATH_INFO=\"{}\", base path=\"{}\", parBasePath=\"{}\", base path trailing slash={}",
std::string(parPathInfo.begin(), parPathInfo.end()),
std::string(base_path.begin(), base_path.end()),
std::string(parBasePath.begin(), parBasePath.end()),
base_path_tr_slash
);
if (boost::starts_with(parPathInfo, base_path)) {
//account for the trailing slash in either base path and path info
return std::min(parBasePath.size(), parPathInfo.size());
}
else {
std::string str_base_path(parBasePath.begin(), parBasePath.end());
std::string str_path(parPathInfo.begin(), parPathInfo.end());
spdlog::get("statuslog")->error(
"base path is not a prefix of PATH_INFO: base path={}, PATH_INFO={}, tawashi will most likely malfunction",
str_base_path,
str_path
);
return 1; //try with the default, maybe the user is lucky and it will work (ie base path = /)
}
}
} //unnamed namespace
Env::Env(const char* const* parEnvList, const boost::string_ref& parBasePath) :
m_cgi_env(cgi_environment_vars(parEnvList)),
m_skip_path_info(calculate_skip_path_length(m_cgi_env[CGIVars::PATH_INFO], parBasePath)),
m_request_method_type(RequestMethodType::_from_string(m_cgi_env[CGIVars::REQUEST_METHOD].data()))
{
{
const std::string& content_type = m_cgi_env.at(CGIVars::CONTENT_TYPE);
int parsed_chars;
bool parse_ok;
m_split_mime = string_to_mime(&content_type, parse_ok, parsed_chars);
if (not parse_ok) {
std::string err_msg = "Parsing failed at position " +
std::to_string(parsed_chars) + " for input \"" +
content_type + "\"";
throw TawashiException(ErrorReasons::InvalidContentType, boost::string_ref(err_msg));
}
}
}
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]);
}
boost::string_ref Env::path_info() const {
const std::string& path = m_cgi_env[CGIVars::PATH_INFO];
assert(m_skip_path_info <= path.size());
return boost::string_ref(path).substr(m_skip_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::http_client_ip() const {
return m_cgi_env[CGIVars::HTTP_CLIENT_IP];
}
const std::string& Env::http_x_forwarded_for() const {
return m_cgi_env[CGIVars::HTTP_X_FORWARDED_FOR];
}
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];
}
RequestMethodType Env::request_method() const {
return m_request_method_type;
}
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];
}
bool Env::https() const {
const std::string& val = m_cgi_env[CGIVars::HTTPS];
return val.size() == 2 and (
val == "on" or val == "ON" or val == "oN" or val == "On"
);
}
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;
}
const SplitMime& Env::content_type_split() const {
return m_split_mime;
}
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

83
src/tawashi/cgi_env.hpp Normal file
View file

@ -0,0 +1,83 @@
/* 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 "kakoune/safe_ptr.hh"
#include "request_method_type.hpp"
#include "mime_split.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 Kakoune::SafeCountable {
public:
struct VersionInfo {
boost::string_ref name;
uint16_t major;
uint16_t minor;
};
typedef boost::container::flat_map<std::string, std::string> GetMapType;
Env (const char* const* parEnvList, const boost::string_ref& parBasePath);
~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;
boost::string_ref path_info() const;
const std::string& path_translated() const;
const std::string& query_string() const;
const std::string& http_client_ip() const;
const std::string& http_x_forwarded_for() 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;
RequestMethodType request_method() const;
const std::string& script_name() const;
const std::string& server_name() const;
bool https() 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;
const SplitMime& content_type_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;
std::size_t m_skip_path_info;
RequestMethodType m_request_method_type;
SplitMime m_split_mime;
};
} //namespace cgi
} //namespace tawashi

View file

@ -0,0 +1,91 @@
/* 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 "sanitized_utf8.hpp"
#include "string_lengths.hpp"
#include <utility>
#include <unordered_map>
#include <boost/utility/string_ref.hpp>
#include <cassert>
#include <cstring>
#include <boost/functional/hash.hpp>
#include <cstddef>
#if !defined(NDEBUG)
# include <cstring>
#endif
namespace std {
template<>
struct hash<boost::string_ref> {
std::size_t operator() (const boost::string_ref& parStr) const {
return boost::hash_range(parStr.begin(), parStr.end());
}
};
} //namespace std
namespace tawashi {
namespace {
std::unordered_map<boost::string_ref, boost::string_ref> get_unrefined_env_vars (const char* const* parEnvList) {
using boost::string_ref;
assert(parEnvList);
std::size_t count = 0;
while (*(parEnvList + count)) {
++count;
}
std::unordered_map<string_ref, string_ref> retval;
retval.reserve(count);
for (std::size_t z = 0; z < count; ++z) {
const char* const equal_sign = std::strchr(parEnvList[z], '=');
assert('=' == *equal_sign);
assert(equal_sign >= parEnvList[z]);
const std::size_t key_length = static_cast<std::size_t>(equal_sign - parEnvList[z]);
const std::size_t whole_length = std::strlen(parEnvList[z] + key_length) + key_length;
assert(std::strlen(parEnvList[z]) == whole_length);
assert(whole_length >= key_length + 1);
retval[string_ref(parEnvList[z], key_length)] = string_ref(parEnvList[z] + key_length + 1, whole_length - key_length - 1);
}
return retval;
}
} //unnamed namespace
std::vector<std::string> cgi_environment_vars (const char* const* parEnvList) {
using boost::string_ref;
std::vector<std::string> retlist;
retlist.reserve(CGIVars::_size());
auto unrefined_env_vars = get_unrefined_env_vars(parEnvList);
auto enum_str_lengths = string_lengths<CGIVars>();
std::size_t z = 0;
for (CGIVars var : CGIVars::_values()) {
#if !defined(NDEBUG)
assert(std::strlen(var._to_string()) == enum_str_lengths[z]);
#endif
auto it_found = unrefined_env_vars.find(boost::string_ref(var._to_string(), enum_str_lengths[z]));
if (unrefined_env_vars.cend() != it_found)
retlist.push_back(sanitized_utf8(it_found->second));
else
retlist.push_back(std::string());
++z;
}
return retlist;
}
} //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 <vector>
#include <string>
#include "enum.h"
namespace tawashi {
SLOW_ENUM(CGIVars, std::size_t,
AUTH_TYPE = 0,
CONTENT_LENGTH,
CONTENT_TYPE,
DOCUMENT_ROOT, //The root directory of your server
GATEWAY_INTERFACE,
HTTP_CLIENT_IP,
HTTP_COOKIE, //The visitor's cookie, if one is set
HTTP_HOST, //The hostname of your server
HTTP_REFERER, //The URL of the page that called your script
HTTP_X_FORWARDED_FOR,
HTTPS, //"on" if the script is being called through a secure server
HTTP_USER_AGENT, //The browser type of your visitor
PATH, //The system path your server is running under
PATH_INFO,
PATH_TRANSLATED,
QUERY_STRING, //The query string (see GET, below)
REMOTE_ADDR, //The IP address of the visitor
REMOTE_HOST, //The hostname of the visitor (if your server has reverse-name-lookups on; otherwise this is the IP address again)
REMOTE_IDENT,
REMOTE_PORT, //The port the visitor is connected to on the web server
REMOTE_USER, //The visitor's user name (for .htaccess-protected pages)
REQUEST_METHOD, //GET or POST
REQUEST_URI, //The interpreted pathname of the requested document or CGI (relative to the document root)
SCRIPT_FILENAME, //The full pathname of the current CGI
SCRIPT_NAME, //The interpreted pathname of the current CGI (relative to the document root)
SERVER_ADMIN, //The email address for your server's webmaster
SERVER_NAME, //Your server's fully qualified domain name (eg: www.example.com)
SERVER_PORT, //The port number your server is listening on
SERVER_PROTOCOL,
SERVER_SOFTWARE //The server software you're using (such as Apache 1.3)
);
std::vector<std::string> cgi_environment_vars (const char* const* parEnvList);
} //namespace tawashi

94
src/tawashi/cgi_post.cpp Normal file
View file

@ -0,0 +1,94 @@
/* 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 {
UnsupportedContentTypeException::UnsupportedContentTypeException (const boost::string_ref& parMessage) :
TawashiException(ErrorReasons::UnsupportedContentType, parMessage)
{
}
namespace cgi {
namespace {
bool valid_content_type (const Env& parEnv) {
if (parEnv.content_type_split().type != "application" or
parEnv.content_type_split().subtype !=
"x-www-form-urlencoded") {
return false;
}
return true;
}
std::string read_n (std::istream& parSrc, std::size_t parSize) {
if (0 == parSize)
return std::string();
std::string original_data;
original_data.reserve(parSize);
std::copy_n(
std::istream_iterator<char>(parSrc),
parSize,
std::back_inserter(original_data)
);
return sanitized_utf8(original_data);
}
} //unnamed namespace
const PostMapType& read_post (std::istream& parSrc, const Env& parEnv) {
return read_post(parSrc, parEnv, parEnv.content_length());
}
const PostMapType& read_post (std::istream& parSrc, const Env& parEnv, std::size_t parMaxLen) {
static bool already_read = false;
static PostMapType map;
static std::string original_data;
if (not already_read) {
assert(original_data.empty());
assert(map.empty());
if (not valid_content_type(parEnv)) {
throw UnsupportedContentTypeException(parEnv.content_type());
}
const auto input_len = std::min(parMaxLen, parEnv.content_length());
original_data = read_n(parSrc, input_len);
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

40
src/tawashi/cgi_post.hpp Normal file
View file

@ -0,0 +1,40 @@
/* 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 "tawashi_exception.hpp"
#include <boost/container/flat_map.hpp>
#include <string>
#include <istream>
#include <cstddef>
namespace tawashi {
class UnsupportedContentTypeException : public TawashiException {
public:
explicit UnsupportedContentTypeException (const boost::string_ref& parMessage);
};
namespace cgi {
class Env;
typedef boost::container::flat_map<std::string, std::string> PostMapType;
const PostMapType& read_post (std::istream& parSrc, const Env& parEnv);
const PostMapType& read_post (std::istream& parSrc, const Env& parEnv, std::size_t parMaxLen);
} //namespace cgi
} //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/>.
*/
#pragma once
#include "enum.h"
namespace tawashi {
BETTER_ENUM(ErrorReasons, int,
PostLengthNotInRange,
PastieNotSaved,
UserFlooding,
UnkownReason,
RedisDisconnected,
MissingPostVariable,
PastieNotFound,
InvalidContentType,
UnsupportedContentType
)
} //namespace tawashi

View file

@ -0,0 +1,69 @@
/* 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."
};
constexpr const auto lengths = string_lengths(err_descs);
static_assert(err_descs.static_size == lengths.static_size, "Mismatching array sizes between strings and their lengths");
#if !defined(NDEBUG)
for (std::size_t z = 0; z < err_descs.size(); ++z) {
assert(std::strlen(err_descs[z]) == lengths[z]);
}
#endif
parContext["error_message"] = std::string(err_descs[reason_code], lengths[reason_code]);
parContext["error_id"] = std::to_string(reason_code);
}
} //namespace tawashi

View file

@ -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 <boost/utility/string_ref.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_ref page_basename() const override { return boost::string_ref("error"); }
private:
virtual void on_mustache_prepare (mstch::map& parContext) override;
};
} //namespace tawashi

92
src/tawashi/escapist.cpp Normal file
View file

@ -0,0 +1,92 @@
/* 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_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_escape_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

47
src/tawashi/escapist.hpp Normal file
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 <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_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

137
src/tawashi/http_header.cpp Normal file
View file

@ -0,0 +1,137 @@
/* 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 "http_header.hpp"
#include "duckhandy/lexical_cast.hpp"
#include "duckhandy/sequence_bt.hpp"
#include "sprout/array/array.hpp"
#include <utility>
#include <spdlog/spdlog.h>
#include <ciso646>
namespace tawashi {
namespace {
constexpr const char* get_status_code_desc (HttpStatusCodes parCode) {
switch (parCode) {
case HttpStatusCodes::Code301_MovedPermanently: return "Moved Permanently";
case HttpStatusCodes::Code302_Found: return "Found";
case HttpStatusCodes::Code303_SeeOther: return "See Other";
case HttpStatusCodes::Code400_BadRequest: return "Bad Request";
case HttpStatusCodes::Code403_Forbidden: return "Forbidden";
case HttpStatusCodes::Code404_NotFound: return "Not Found";
case HttpStatusCodes::Code413_PayloadTooLarge: return "Payload Too Large";
case HttpStatusCodes::Code429_TooManyRequests: return "Too Many Requests";
case HttpStatusCodes::Code431_RequestHeaderFieldsTooLarge: return "Request Header Fields Too Large";
case HttpStatusCodes::Code500_InternalServerError: return "Internal Server Error";
case HttpStatusCodes::Code501_NotImplemented: return "Not Implemented";
case HttpStatusCodes::Code503_ServiceUnavailable: return "Service Unavailable";
}
return "INVALID STATUS CODE";
}
constexpr auto g_status_code_descriptions = ::better_enums::make_map(get_status_code_desc);
} //unnamed namespace
HttpHeader::HttpHeader() :
m_mime {"text", "html", {}},
m_status_code(HttpStatusCodes::CodeNone),
m_header_type(ContentType)
{
}
HttpHeader::HttpHeader (Types parType, HttpStatusCodes parCode, SplitMime&& parMime) :
m_mime(std::move(parMime)),
m_status_code(parCode),
m_header_type(parType)
{
}
HttpHeader::HttpHeader (HttpStatusCodes parCode, std::string&& parRedirectLocation) :
m_redirect_location(std::move(parRedirectLocation)),
m_status_code(parCode),
m_header_type(Location)
{
}
void HttpHeader::set_status (HttpStatusCodes parCode) {
m_status_code = parCode;
}
void HttpHeader::unset_status() {
m_status_code = HttpStatusCodes::CodeNone;
}
void HttpHeader::set_type (Types parType, SplitMime&& parParameter) {
m_header_type = parType;
m_mime = std::move(parParameter);
}
bool HttpHeader::body_required() const {
return type() == ContentType;
}
std::ostream& operator<< (std::ostream& parStream, const HttpHeader& parHeader) {
const HttpStatusCodes code_none = HttpStatusCodes::CodeNone;
if (parHeader.status_code() != code_none) {
parStream <<
"Status: " <<
parHeader.status_code()._to_integral() <<
' ' <<
g_status_code_descriptions[parHeader.status_code()] <<
'\n'
;
}
switch (parHeader.type()) {
case HttpHeader::ContentType:
SPDLOG_TRACE(spdlog::get("statuslog"), "Response is a Content-type (data)");
parStream << "Content-type: " << mime_to_string(parHeader) << '\n';
break;
case HttpHeader::Location:
SPDLOG_TRACE(spdlog::get("statuslog"), "Response is a Location (redirect)");
parStream << "Location: " << parHeader.redirect_location() << '\n';
break;
}
parStream << '\n';
return parStream;
}
HttpHeader make_header_type_html() {
return HttpHeader(
HttpHeader::ContentType,
HttpStatusCodes::CodeNone,
SplitMime { "text", "html", {}}
);
}
HttpHeader make_header_type_text_utf8() {
return HttpHeader(
HttpHeader::ContentType,
HttpStatusCodes::CodeNone,
SplitMime {"text", "plain", MimeParametersMapType {{"charset", "utf-8"}}}
);
}
std::string mime_to_string (const HttpHeader& parHeader) {
bool write_ok;
std::string retval = mime_to_string(parHeader.mime(), write_ok);
if (not write_ok) {
assert(false);
return std::string();
}
return retval;
}
} //namespace tawashi

View file

@ -0,0 +1,78 @@
/* 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 "mime_split.hpp"
#include <cstdint>
#include <string>
#include <ostream>
namespace tawashi {
BETTER_ENUM(HttpStatusCodes, uint16_t,
Code301_MovedPermanently = 301,
Code302_Found = 302,
Code303_SeeOther = 303,
Code400_BadRequest = 400,
Code403_Forbidden = 403,
Code404_NotFound = 404,
Code413_PayloadTooLarge = 413,
Code429_TooManyRequests = 429,
Code431_RequestHeaderFieldsTooLarge = 431,
Code500_InternalServerError = 500,
Code501_NotImplemented = 501,
Code503_ServiceUnavailable = 503,
CodeNone = 0
)
class HttpHeader {
public:
enum Types : uint8_t {
ContentType,
Location,
Status
};
HttpHeader();
HttpHeader (const HttpHeader&) = default;
HttpHeader (HttpHeader&&) = default;
HttpHeader (Types parType, HttpStatusCodes parCode, SplitMime&& parMime);
HttpHeader (HttpStatusCodes parCode, std::string&& parRedirectLocation);
~HttpHeader() noexcept = default;
Types type() const { return m_header_type; }
HttpStatusCodes status_code() const { return m_status_code; }
const std::string& redirect_location() const { return m_redirect_location; }
const SplitMime& mime() const { return m_mime; }
bool body_required() const;
void set_status (HttpStatusCodes parCode);
void unset_status();
void set_type (Types parType, SplitMime&& parParameter);
private:
SplitMime m_mime;
std::string m_redirect_location;
HttpStatusCodes m_status_code;
Types m_header_type;
};
std::ostream& operator<< (std::ostream& parStream, const HttpHeader& parHeader);
HttpHeader make_header_type_html();
HttpHeader make_header_type_text_utf8();
[[gnu::pure]] std::string mime_to_string (const HttpHeader& parHeader);
} //namespace tawashi

View file

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

View file

@ -0,0 +1,37 @@
/* 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:
IndexResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_ref page_basename() const override { return boost::string_ref("index"); }
private:
};
} //namespace tawashi

147
src/tawashi/ini_file.cpp Normal file
View file

@ -0,0 +1,147 @@
/* 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_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;
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_ref;
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_ref::substr,
px::construct<string_ref>(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_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[*(graph - eol) >> *(hold[+blank >> +(graph - 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)
)];
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

58
src/tawashi/ini_file.hpp Normal file
View file

@ -0,0 +1,58 @@
/* 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>
#include <cassert>
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;
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

83
src/tawashi/ip_utils.cpp Normal file
View file

@ -0,0 +1,83 @@
/* 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 "ip_utils.hpp"
#include "duckhandy/lexical_cast.hpp"
#include "duckhandy/int_to_string_ary.hpp"
#include "cgi_env.hpp"
#include "tawashi_config.h"
#include <spdlog/spdlog.h>
#include <cassert>
#include <algorithm>
#include <ciso646>
#if !defined(TAWASHI_WITH_IP_LOGGING)
extern "C" void tiger (const char* parStr, uint64_t parLength, uint64_t parHash[3], char parPadding);
#endif
namespace tawashi {
namespace {
std::string hash_if_configured (const std::string& parIP) a_always_inline;
#if !defined(TAWASHI_WITH_IP_LOGGING)
std::string hashed_ip (const std::string& parIP) {
using dhandy::tags::hex;
uint64_t hash[3];
tiger(parIP.data(), parIP.size(), hash, 0x80);
auto h1 = dhandy::int_to_string_ary<char, hex>(hash[0]);
auto h2 = dhandy::int_to_string_ary<char, hex>(hash[1]);
auto h3 = dhandy::int_to_string_ary<char, hex>(hash[2]);
std::string retval(2 * sizeof(uint64_t) * 3, '0');
assert(h1.size() <= 2 * sizeof(uint64_t));
std::copy(h1.begin(), h1.end(), retval.begin() + 2 * sizeof(uint64_t) * 0 + 2 * sizeof(uint64_t) - h1.size());
assert(h2.size() <= 2 * sizeof(uint64_t));
std::copy(h2.begin(), h2.end(), retval.begin() + 2 * sizeof(uint64_t) * 1 + 2 * sizeof(uint64_t) - h2.size());
assert(h3.size() <= 2 * sizeof(uint64_t));
std::copy(h3.begin(), h3.end(), retval.begin() + 2 * sizeof(uint64_t) * 2 + 2 * sizeof(uint64_t) - h3.size());
SPDLOG_DEBUG(spdlog::get("statuslog"), "IP \"{}\" hashed -> \"{}\"", parIP, retval);
assert(retval.size() == 16 * 3);
return retval;
}
#endif
inline std::string hash_if_configured (const std::string& parIP) {
#if defined(TAWASHI_WITH_IP_LOGGING)
return parIP;
#else
return hashed_ip(parIP);
#endif
}
} //unnamed namespace
//see: https://stackoverflow.com/questions/18799808/how-do-i-count-unique-visitors-to-my-site
std::string guess_real_remote_ip (const cgi::Env& parCgiEnv) {
if (not parCgiEnv.http_client_ip().empty()) {
return hash_if_configured(parCgiEnv.http_client_ip());
}
else if (not parCgiEnv.http_x_forwarded_for().empty()) {
return hash_if_configured(parCgiEnv.http_x_forwarded_for());
}
else {
return hash_if_configured(parCgiEnv.remote_addr());
}
}
} //namespace tawashi

29
src/tawashi/ip_utils.hpp Normal file
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 <string>
namespace tawashi {
namespace cgi {
class Env;
} //namespace cgi
std::string guess_real_remote_ip (const cgi::Env& parCgiEnv) a_pure;
} //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

52
src/tawashi/logger.hpp Normal file
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

@ -1,183 +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 "tawashiConfig.h"
#include "submit_paste_response.hpp"
#include "quick_submit_paste_response.hpp"
#include "pastie_response.hpp"
#include "index_response.hpp"
#include "error_response.hpp"
#include "response_factory.hpp"
#include "cgi_env.hpp"
#include "ini_file.hpp"
#include "safe_stack_object.hpp"
#include "pathname/pathname.hpp"
#include "duckhandy/compatibility.h"
#include "settings_bag.hpp"
#include "logging_levels.hpp"
#include "request_method_type.hpp"
#include <spdlog/spdlog.h>
#include <string>
#include <fstream>
#include <iterator>
#include <ciso646>
#include <iostream>
//www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4150.pdf
namespace {
std::string config_file_path() a_pure;
std::string config_file_path() {
mchlib::PathName config_path(TAWASHI_CONFIG_PATH);
mchlib::PathName full_path("");
if (config_path.is_absolute()) {
full_path = std::move(config_path);
}
else {
full_path = mchlib::PathName(TAWASHI_PATH_PREFIX);
full_path.join(config_path);
}
full_path.join(TAWASHI_CONFIG_FILE);
return full_path.path();
}
template <typename T>
std::unique_ptr<tawashi::Response> make_response (
const Kakoune::SafePtr<tawashi::SettingsBag>& parSettings,
const Kakoune::SafePtr<tawashi::cgi::Env>& parCgiEnv
) {
return static_cast<std::unique_ptr<tawashi::Response>>(
std::make_unique<T>(parSettings, &std::cout, parCgiEnv)
);
}
void fill_defaults (tawashi::SettingsBag& parSettings) {
parSettings.add_default("redis_server", "127.0.0.1");
parSettings.add_default("redis_port", "6379");
parSettings.add_default("redis_mode", "sock");
parSettings.add_default("redis_sock", "/tmp/redis.sock");
parSettings.add_default("redis_db", "0");
parSettings.add_default("host_name", "127.0.0.1");
parSettings.add_default("host_port", "");
parSettings.add_default("host_path", "/");
parSettings.add_default("website_root", "");
parSettings.add_default("langmap_dir", "/usr/share/source-highlight");
parSettings.add_default("min_pastie_size", "10");
parSettings.add_default("max_pastie_size", "10000");
parSettings.add_default("truncate_long_pasties", "false");
parSettings.add_default("logging_level", "err");
parSettings.add_default("resubmit_wait", "10");
parSettings.add_default("log_file", "-");
parSettings.add_default("highlight_css", "sh_darkness.css");
parSettings.add_default("max_post_size", "1048576");
}
void print_buildtime_info() {
std::cout << "NDEBUG defined: ";
#if defined(NDEBUG)
std::cout << "yes (Release build)";
#else
std::cout << "no (Debug build)";
#endif
std::cout << '\n';
std::cout << "TAWASHI_CONFIG_FILE: \"" << TAWASHI_CONFIG_FILE << "\"\n";
std::cout << "TAWASHI_CONFIG_PATH: \"" << TAWASHI_CONFIG_PATH << "\"\n";
std::cout << "TAWASHI_PATH_PREFIX: \"" << TAWASHI_PATH_PREFIX << "\"\n";
std::cout << "VERSION_MAJOR: " << VERSION_MAJOR << '\n';
std::cout << "VERSION_MINOR: " << VERSION_MINOR << '\n';
std::cout << "VERSION_PATCH: " << VERSION_PATCH << '\n';
std::cout << "config_file_path(): \"" << config_file_path() << "\"\n";
}
curry::SafeStackObject<tawashi::IniFile> load_ini() {
using curry::SafeStackObject;
using tawashi::IniFile;
using std::istream_iterator;
std::ifstream conf(config_file_path());
conf >> std::noskipws;
return SafeStackObject<IniFile>(istream_iterator<char>(conf), istream_iterator<char>());
}
std::shared_ptr<spdlog::logger> setup_logging (const tawashi::SettingsBag& parSettings) {
//Prepare the logger
spdlog::set_pattern("[%Y-%m-%d %T %z] - %v");
spdlog::set_level(spdlog::level::trace); //set to maximum possible here
boost::string_ref log_path = parSettings["log_file"];
const bool log_to_stderr = (log_path == boost::string_ref("-"));
auto statuslog = (log_to_stderr ?
spdlog::stderr_logger_st("statuslog") :
spdlog::basic_logger_st("statuslog", std::string(log_path.begin(), log_path.end()), false)
);
auto logging_level = tawashi::LoggingLevels::_from_string_nocase(parSettings.as<std::string>("logging_level").c_str());
spdlog::set_level(static_cast<decltype(spdlog::level::trace)>(logging_level._to_integral()));
return statuslog;
}
} //unnamed namespace
int main (int parArgc, char* parArgv[], char* parEnvp[]) {
using curry::SafeStackObject;
using tawashi::IndexResponse;
using tawashi::SubmitPasteResponse;
using tawashi::QuickSubmitPasteResponse;
using tawashi::PastieResponse;
using tawashi::ErrorResponse;
using tawashi::Response;
using tawashi::RequestMethodType;
if (2 == parArgc and boost::string_ref(parArgv[1]) == "--show-paths") {
print_buildtime_info();
return 0;
}
SafeStackObject<tawashi::IniFile> ini = load_ini();
auto settings = SafeStackObject<tawashi::SettingsBag>(ini);
fill_defaults(*settings);
auto statuslog = setup_logging(*settings);
SPDLOG_DEBUG(statuslog, "tawashi started");
int retval = 0;
try {
statuslog->info("Loaded config: \"{}\"", config_file_path());
auto cgi_env = SafeStackObject<tawashi::cgi::Env>(parEnvp, settings->at("host_path"));
tawashi::ResponseFactory resp_factory(settings, cgi_env);
SPDLOG_TRACE(statuslog, "Registering makers in the response factory");
resp_factory.register_maker("index.cgi", RequestMethodType::GET, &make_response<IndexResponse>);
resp_factory.register_maker("", RequestMethodType::GET, &make_response<IndexResponse>);
resp_factory.register_maker("", RequestMethodType::POST, &make_response<QuickSubmitPasteResponse>);
resp_factory.register_maker("paste.cgi", RequestMethodType::POST, &make_response<SubmitPasteResponse>);
resp_factory.register_maker("error.cgi", RequestMethodType::GET, &make_response<ErrorResponse>);
resp_factory.register_jolly_maker(&make_response<PastieResponse>, RequestMethodType::GET);
std::unique_ptr<Response> response = resp_factory.make_response(
cgi_env->path_info(),
cgi_env->request_method()
);
if (response)
response->send();
}
catch (const std::exception& e) {
statuslog->critical("Uncaught exception in main(): \"{}\"", e.what());
retval = 1;
}
SPDLOG_DEBUG(statuslog, "tawashi done, quitting with {}", retval);
return retval;
}

225
src/tawashi/mime_split.cpp Normal file
View file

@ -0,0 +1,225 @@
/* 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 "mime_split.hpp"
#include <boost/spirit/include/qi_core.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/qi_plus.hpp>
#include <boost/spirit/include/qi_lit.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_raw.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_lexeme.hpp>
#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_char_class.hpp>
#include <boost/spirit/include/qi_kleene.hpp>
#include <boost/spirit/include/qi_alternative.hpp>
#include <boost/spirit/include/qi_difference.hpp>
#include <boost/spirit/include/karma_char.hpp>
#include <boost/spirit/include/karma_generate.hpp>
#include <boost/spirit/include/karma_operator.hpp>
#include <boost/spirit/include/karma_kleene.hpp>
#include <boost/spirit/include/karma_stream.hpp>
#include <boost/spirit/include/karma_string.hpp>
#include <boost/spirit/include/karma_alternative.hpp>
#include <boost/spirit/include/karma_rule.hpp>
#include <boost/spirit/include/karma_eps.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/function/lazy_prelude.hpp>
#include <boost/phoenix/core.hpp>
#include <boost/phoenix/object/construct.hpp>
#include <boost/phoenix/stl/container.hpp>
#include <boost/phoenix/bind/bind_member_function.hpp>
#include <boost/phoenix/operator/arithmetic.hpp>
#include <boost/phoenix/operator/self.hpp>
#include <boost/phoenix/stl/algorithm/transformation.hpp>
#include <cassert>
#include <iterator>
#include <sstream>
// The Internet Media Type [9 <#ref-9>] of the attached entity. The syntax is
// the same as the HTTP Content-Type header.
//
// CONTENT_TYPE = "" | media-type
// media-type = type "/" subtype *( ";" parameter)
// type = token
// subtype = token
// parameter = attribute "=" value
// attribute = token
// value = token | quoted-string
//
// The type, subtype and parameter attribute names are not
// case-sensitive. Parameter values may be case sensitive. Media
// types and their use in HTTP are described section 3.6 <#section-3.6> of the
// HTTP/1.0 specification [3 <#ref-3>]. Example:
//
// application/x-www-form-urlencoded
//
// There is no default value for this variable. If and only if it is
// unset, then the script may attempt to determine the media type
// from the data received. If the type remains unknown, then
// application/octet-stream should be assumed.
BOOST_FUSION_ADAPT_STRUCT(
tawashi::SplitMime,
(boost::string_ref, type)
(boost::string_ref, subtype)
(tawashi::MimeParametersMapType, parameters)
);
namespace tawashi {
namespace {
template <typename Iterator, typename Skipper>
struct MimeGrammar : boost::spirit::qi::grammar<Iterator, tawashi::SplitMime(), Skipper> {
explicit MimeGrammar (const std::string* parString);
boost::spirit::qi::rule<Iterator, SplitMime(), Skipper> content_type;
boost::spirit::qi::rule<Iterator, SplitMime(), Skipper> media_type;
boost::spirit::qi::rule<Iterator, boost::string_ref(), Skipper> type;
boost::spirit::qi::rule<Iterator, boost::string_ref(), Skipper> subtype;
boost::spirit::qi::rule<Iterator, MimeParametersMapType::value_type(), Skipper> parameter;
boost::spirit::qi::rule<Iterator, boost::string_ref(), Skipper> attribute;
boost::spirit::qi::rule<Iterator, boost::string_ref(), Skipper> value;
boost::spirit::qi::rule<Iterator, boost::string_ref(), Skipper> quoted_string;
boost::spirit::qi::rule<Iterator, boost::string_ref(), Skipper> token;
const std::string* m_master_string;
Iterator m_begin;
};
template <typename Iterator, typename Skipper>
MimeGrammar<Iterator, Skipper>::MimeGrammar (const std::string* parString) :
MimeGrammar::base_type(content_type),
m_master_string(parString),
m_begin(m_master_string->cbegin())
{
namespace px = boost::phoenix;
using boost::spirit::ascii::space;
using boost::spirit::qi::char_;
using boost::spirit::qi::lit;
using boost::spirit::qi::alnum;
using boost::spirit::qi::raw;
using boost::spirit::qi::_val;
using boost::spirit::qi::lexeme;
using boost::string_ref;
using boost::spirit::_1;
content_type = -media_type;
media_type = type >> "/" >> subtype >> *(lit(";") >> parameter);
type = token.alias();
subtype = token.alias();
parameter = attribute >> "=" >> value;
attribute = token.alias();
value = token | quoted_string;
token = raw[+(alnum | 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)
)
];
quoted_string = raw[
lexeme[
lit('"') >>
*(char_ - '"') >>
'"'
]
][_val = px::bind(
&string_ref::substr, px::construct<string_ref>(px::ref(*m_master_string)),
px::begin(_1) + 1 - px::ref(m_begin),
px::size(_1) - 2
)];
}
struct simple_token_checker {
typedef bool result_type;
template <typename V>
bool operator() (const V& parIn) const {
return std::find(std::begin(parIn), std::end(parIn), ' ') == std::end(parIn) and
std::find(std::begin(parIn), std::end(parIn), '\t') == std::end(parIn);
}
};
} //unnamed namespace
SplitMime string_to_mime (const std::string* parMime, bool& parParseOk, int& parParsedCharCount) {
using boost::spirit::qi::blank;
using boost::spirit::qi::blank_type;
MimeGrammar<std::string::const_iterator, blank_type> gramm(parMime);
SplitMime result;
parParseOk = false;
parParsedCharCount = 0;
std::string::const_iterator start_it = parMime->cbegin();
const bool parse_ok = boost::spirit::qi::phrase_parse(
start_it,
parMime->cend(),
gramm,
blank,
result
);
parParseOk = parse_ok and (parMime->cend() == start_it);
parParsedCharCount = std::distance(parMime->cbegin(), start_it);
assert(parParsedCharCount >= 0);
return result;
}
std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk) {
namespace px = boost::phoenix;
using boost::string_ref;
using boost::spirit::karma::generate;
using boost::spirit::karma::char_;
using boost::spirit::karma::string;
using boost::spirit::karma::rule;
using boost::spirit::karma::alnum;
using boost::spirit::karma::eps;
using boost::spirit::_val;
if (parMime.type.empty() or parMime.subtype.empty()) {
parWriteOk = false;
return std::string();
}
px::function<simple_token_checker> is_simple_token;
std::string retval;
std::back_insert_iterator<std::string> out_iter(retval);
rule<std::back_insert_iterator<std::string>, string_ref()> token;
rule<std::back_insert_iterator<std::string>, string_ref()> quoted_string;
rule<std::back_insert_iterator<std::string>, string_ref()> param_value;
token %= eps(is_simple_token(_val)) << *(alnum | char_("._-"));
quoted_string %= '"' << string << '"';
param_value %= token | quoted_string;
parWriteOk = generate(
out_iter,
string << "/" << string << *(
"; " << string << "=" << param_value
),
parMime
);
return retval;
}
} //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/>.
*/
#pragma once
#include <boost/utility/string_ref.hpp>
#include <boost/container/flat_map.hpp>
#include <string>
namespace tawashi {
typedef boost::container::flat_map<boost::string_ref, boost::string_ref> MimeParametersMapType;
struct SplitMime {
boost::string_ref type;
boost::string_ref subtype;
MimeParametersMapType parameters;
};
SplitMime string_to_mime (const std::string* parMime, bool& parParseOk, int& parParsedCharCount);
std::string mime_to_string (const SplitMime& parMime, bool& parWriteOk);
} //namespace tawashi

34
src/tawashi/num_conv.hpp Normal file
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/>.
*/
#pragma once
#include <ciso646>
#include <duckhandy/lexical_cast.hpp>
namespace tawashi {
template <typename T>
inline bool seems_valid_number (const boost::string_ref& parValue) {
const std::size_t skip_sign = (sprout::is_signed<T>::value and not parValue.empty() and parValue[0] == '-' ? 1 : 0);
for (std::size_t z = skip_sign; z < parValue.size(); ++z) {
const char c = parValue[z];
if (c < '0' or c > '9')
return false;
}
return parValue.size() - skip_sign <= dhandy::tags::dec<T>::count_digits_bt(sprout::numeric_limits<T>::max());
}
} //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,134 @@
/* 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 "cgi_env.hpp"
#include <ciso646>
#include <srchilite/sourcehighlight.h>
#include <srchilite/langmap.h>
#include <sstream>
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");
}
} //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)
{
}
HttpHeader PastieResponse::on_process() {
if (m_pastie_not_found) {
return make_error_redirect(ErrorReasons::PastieNotFound);
}
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) {
using opt_string = redis::IncRedis::opt_string;
using opt_string_list = redis::IncRedis::opt_string_list;
boost::string_ref token = cgi_env().path_info();
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());
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);
}
std::string PastieResponse::on_mustache_retrieve() {
if (m_plain_text)
return "{{pastie}}";
else
return load_mustache();
}
} //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 "response.hpp"
#include <string>
#include <boost/utility/string_ref.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_ref page_basename() const override { return boost::string_ref("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;
};
} //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,42 @@
/* Copyright 2017, Michele Santullo
* This file is part of "tawashi".
*
* "tawashi" is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* "tawashi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with "tawashi". If not, see <http://www.gnu.org/licenses/>.
*/
#include "quick_submit_paste_response.hpp"
namespace tawashi {
QuickSubmitPasteResponse::QuickSubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
) :
SubmitPasteResponse(parSettings, parStreamOut, parCgiEnv)
{
}
void QuickSubmitPasteResponse::on_mustache_prepare (mstch::map& parContext) {
parContext["redirect_to_address"] = m_redirect_to;
}
std::string QuickSubmitPasteResponse::on_mustache_retrieve() {
return "{{base_uri}}/{{redirect_to_address}}\n";
}
HttpHeader QuickSubmitPasteResponse::make_success_response (std::string&& parPastieParam) {
m_redirect_to = std::move(parPastieParam);
return HttpHeader();
}
} //namespace tawashi

View file

@ -0,0 +1,44 @@
/* 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_ref.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_ref page_basename() const override { assert(false); return boost::string_ref(""); }
virtual HttpHeader make_success_response (std::string&& parPastieParam) override;
private:
virtual void on_mustache_prepare (mstch::map& parContext) override;
virtual std::string on_mustache_retrieve() override;
std::string m_redirect_to;
};
} //namespace tawashi

View file

@ -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/>.
*/
#pragma once
#include "enum.h"
namespace tawashi {
BETTER_ENUM(RequestMethodType, int,
GET,
POST
)
} //namespace tawashi

281
src/tawashi/response.cpp Normal file
View file

@ -0,0 +1,281 @@
/* 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 "tawashi_config.h"
#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 <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 to_string (const boost::string_ref& parStr) {
return std::string(parStr.data(), parStr.size());
}
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 boost::string_ref& 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_ref make_host_path (const SettingsBag& parSettings) {
boost::string_ref 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_ref 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_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_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; 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", std::string(base_uri())},
{"host_path", to_string(make_host_path(this->settings()))},
{"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");
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 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;
}
redis::IncRedis& Response::redis() const {
assert(m_redis);
return *m_redis;
}
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;
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

75
src/tawashi/response.hpp Normal file
View file

@ -0,0 +1,75 @@
/* 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 <string>
#include <iostream>
#include <boost/utility/string_ref.hpp>
#include <memory>
namespace redis {
class IncRedis;
} //namespace redis
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;
const std::string& base_uri() const;
virtual boost::string_ref page_basename() const = 0;
redis::IncRedis& redis() 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();
Kakoune::SafePtr<cgi::Env> m_cgi_env;
Kakoune::SafePtr<SettingsBag> m_settings;
std::string m_website_root;
std::string m_base_uri;
std::unique_ptr<redis::IncRedis> m_redis;
std::ostream* m_stream_out;
};
} //namespace tawashi

View file

@ -0,0 +1,91 @@
/* 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_ref& parName, RequestMethodType parReqType) {
std::string name(parName.data(), parName.size());
spdlog::get("statuslog")->info(
"making response object for \"{}\" method {}",
name,
parReqType._to_string()
);
const auto& makers = (static_cast<RequestMethodType>(RequestMethodType::POST) == parReqType ? m_local_data->makers_post : m_local_data->makers_get);
auto maker_it = makers.find(name);
if (makers.end() != maker_it) {
return maker_it->second(m_local_data->settings, m_local_data->cgi_env);
}
else if (m_local_data->jolly_makers[parReqType]) {
spdlog::get("statuslog")->info("no exact match found for \"{}\", assuming it's a pastie's token", name);
return m_local_data->jolly_makers[parReqType](m_local_data->settings, m_local_data->cgi_env);
}
else {
spdlog::get("statuslog")->critical("no exact match found for \"{}\" with method {} and no jolly maker given, this should not happen", name, parReqType._to_string());
return std::unique_ptr<Response>();
}
}
void ResponseFactory::register_maker (std::string&& parName, ResponseMakerFunc parMaker) {
m_local_data->makers_get[parName] = parMaker;
m_local_data->makers_post[std::move(parName)] = parMaker;
}
void ResponseFactory::register_maker (std::string&& parName, RequestMethodType parReqType, ResponseMakerFunc parMaker) {
switch (parReqType) {
case RequestMethodType::GET:
m_local_data->makers_get[std::move(parName)] = parMaker;
break;
case RequestMethodType::POST:
m_local_data->makers_post[std::move(parName)] = parMaker;
break;
};
}
void ResponseFactory::register_jolly_maker (ResponseMakerFunc parMaker, RequestMethodType parReqType) {
m_local_data->jolly_makers[parReqType] = parMaker;
}
} //namespace tawashi

View file

@ -0,0 +1,49 @@
/* 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_ref& parName, RequestMethodType parReqType);
void register_maker (std::string&& parName, ResponseMakerFunc parMaker);
void register_maker (std::string&& parName, RequestMethodType parReqType, ResponseMakerFunc parMaker);
void register_jolly_maker (ResponseMakerFunc parMaker, RequestMethodType parReqType);
private:
struct LocalData;
std::unique_ptr<LocalData> m_local_data;
};
} //namespace tawashi

View file

@ -0,0 +1,104 @@
/*
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>&();
template <typename U>
operator Kakoune::SafePtr<U>();
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>
template <typename U>
SafeStackObject<T>::operator Kakoune::SafePtr<U>() {
return Kakoune::SafePtr<U>(&m_obj);
}
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,62 @@
/* 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 <iterator>
#define SANITIZE_WITH_UTFCPP
#if defined(SANITIZE_WITH_UTFCPP)
# include "utf8.h"
# include <algorithm>
#else
# include <glib.h>
# include <cassert>
# include <array>
#endif
namespace tawashi {
std::string sanitized_utf8 (const boost::string_ref& parStr) {
std::string sanitized;
sanitized.reserve(parStr.size());
#if defined(SANITIZE_WITH_UTFCPP)
utf8::replace_invalid(parStr.begin(), parStr.end(), std::back_inserter(sanitized), 0xFFFD);
std::replace(sanitized.begin(), sanitized.end(), '\0', '#');
#else
# error "untested code, don't enable in final builds"
std::array<char, 6> replacement;
const int replacement_len = g_unichar_to_utf8(0xFFFD, replacement.data());
std::size_t beg_offset = 0;
const char* end;
while (not g_utf8_validate(parStr.data() + beg_offset, parStr.size() - beg_offset, &end)) {
assert(beg_offset < parStr.size());
const std::size_t valid_chunk_size = end - (parStr.data() + beg_offset);
sanitized.append(parStr.data() + beg_offset, end);
if (*end)
sanitized.append(replacement.data(), replacement_len);
else
sanitized.append({ '#' });
beg_offset += valid_chunk_size + 1;
assert(beg_offset <= parStr.size());
}
sanitized.append(parStr.data() + beg_offset, end);
assert(g_utf8_validate(sanitized.data(), sanitized.size(), nullptr));
#endif
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 <iostream>
namespace tawashi {
namespace {
const IniFile::KeyValueMapType* get_tawashi_node (const IniFile& parIni, boost::string_ref 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_ref parSectionName) :
m_ini(parIni),
m_values(get_tawashi_node(*parIni, parSectionName))
{
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:
SettingsBag (const Kakoune::SafePtr<IniFile>& parIni, boost::string_ref parSectionName);
~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

28
src/tawashi/spdlog.hpp Normal file
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,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 "sprout/array/array.hpp"
#include "sprout/cstring/strlen.hpp"
#include "duckhandy/sequence_bt.hpp"
#include <cstddef>
#include <cstdint>
namespace tawashi {
typedef uint16_t string_length_type;
namespace implem {
template <std::size_t I, std::size_t S>
inline constexpr string_length_type string_length_at_index (const sprout::array<const char*, S>& parStrings) {
return static_cast<string_length_type>(sprout::strlen(parStrings[I]));
}
template <std::size_t... Indices>
inline constexpr sprout::array<string_length_type, sizeof...(Indices)> string_lengths (const sprout::array<const char*, sizeof...(Indices)>& parStrings, dhandy::bt::index_seq<Indices...>) {
return sprout::array<string_length_type, sizeof...(Indices)> {
string_length_at_index<Indices>(parStrings)...
};
}
template <typename Enum, std::size_t... Indices>
inline constexpr sprout::array<string_length_type, Enum::_size()> string_lengths (const typename Enum::_name_iterable& parEnumIterable, dhandy::bt::index_seq<Indices...>) {
return sprout::array<string_length_type, Enum::_size()> {
static_cast<string_length_type>(sprout::strlen(parEnumIterable[Indices]))...
};
}
} //namespace implem
template <std::size_t S>
inline constexpr sprout::array<string_length_type, S> string_lengths (const sprout::array<const char*, S>& parStrings) {
return implem::string_lengths(parStrings, dhandy::bt::index_range<0, S>());
}
template <typename Enum>
inline constexpr sprout::array<string_length_type, Enum::_size()> string_lengths() {
return implem::string_lengths<Enum>(Enum::_names(), dhandy::bt::index_range<0, Enum::_size()>());
}
} //namespace tawashi

View file

@ -0,0 +1,183 @@
/* 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 "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_ref& parKey) :
TawashiException(
ErrorReasons::MissingPostVariable,
"Error retrieving POST variable \"" + std::string(parKey.begin(), parKey.end()) + "\""
)
{}
};
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)
throw MissingPostVarError(parKey);
return post_data_it->second;
}
boost::string_ref get_value_from_post_log_failure (const cgi::PostMapType& parPost, boost::string_ref parKey) {
try {
return get_value_from_post(parPost, parKey);
}
catch (const MissingPostVarError& e) {
spdlog::get("statuslog")->info(e.what());
return boost::string_ref();
}
}
} //unnamed namespace
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_ref pastie;
boost::string_ref lang;
boost::string_ref duration;
auto statuslog = spdlog::get("statuslog");
assert(statuslog);
const SettingsBag& settings = this->settings();
try {
auto post = cgi::read_post(std::cin, cgi_env(), settings.as<uint32_t>("max_post_size"));
pastie = get_value_from_post(post, make_string_ref(g_post_key));
lang = get_value_from_post_log_failure(post, make_string_ref(g_language_key));
duration = get_value_from_post_log_failure(post, make_string_ref(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_redis(pastie, duration_int, lang);
const auto& token = submit_result.first;
if (token) {
std::ostringstream oss;
oss << *token;
statuslog->info("Pastie token=\"{}\" redirect=\"{}\"", *token, oss.str());
if (not lang.empty())
oss << '?' << lang;
return this->make_success_response(oss.str());
}
else {
statuslog->info("Empty pastie token (possibly due to a previous failure)");
return submit_result.second;
}
}
auto SubmitPasteResponse::submit_to_redis (
const boost::string_ref& parText,
uint32_t parExpiry,
const boost::string_ref& parLang
) -> StringOrHeader {
auto& redis = this->redis();
if (not redis.is_connected()) {
return std::make_pair(boost::optional<std::string>(), make_error_redirect(ErrorReasons::RedisDisconnected));
}
std::string remote_ip = guess_real_remote_ip(cgi_env());
if (redis.get(remote_ip)) {
//please wait and submit again
return std::make_pair(boost::optional<std::string>(), make_error_redirect(ErrorReasons::UserFlooding));
}
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)
) {
redis.set(remote_ip, "");
redis.expire(remote_ip, settings().as<uint32_t>("resubmit_wait"));
if (redis.expire(token, parExpiry))
return std::make_pair(boost::make_optional(token), HttpHeader());
}
return std::make_pair(boost::optional<std::string>(), make_error_redirect(ErrorReasons::PastieNotSaved));
}
HttpHeader SubmitPasteResponse::make_success_response (std::string&& parPastieParam) {
return this->make_redirect(HttpStatusCodes::Code303_SeeOther, std::move(parPastieParam));
}
} //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 "response.hpp"
#include <string>
#include <boost/optional.hpp>
#include <boost/utility/string_ref.hpp>
#include <cassert>
#include <utility>
namespace tawashi {
class SubmitPasteResponse : public Response {
public:
SubmitPasteResponse (
const Kakoune::SafePtr<SettingsBag>& parSettings,
std::ostream* parStreamOut,
const Kakoune::SafePtr<cgi::Env>& parCgiEnv
);
protected:
virtual boost::string_ref page_basename() const override { assert(false); return boost::string_ref(""); }
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_redis (const boost::string_ref& parText, uint32_t parExpiry, const boost::string_ref& parLang);
};
} //namespace tawashi

View file

@ -0,0 +1,23 @@
/* 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
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
#cmakedefine TAWASHI_WITH_IP_LOGGING

View file

@ -0,0 +1,37 @@
/* 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 "tawashi_exception.hpp"
#include <sstream>
namespace tawashi {
namespace {
std::string compose_err_message (ErrorReasons parReason, const boost::string_ref& parMessage) {
std::ostringstream oss;
oss << "Exception with reason " << parReason << ": " << parMessage;
return oss.str();
}
} //unnamed namespace
TawashiException::TawashiException (ErrorReasons parReason, const boost::string_ref& parMessage) :
std::runtime_error(compose_err_message(parReason, parMessage)),
m_reason(parReason)
{
}
TawashiException::~TawashiException() noexcept = default;
} //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/>.
*/
#pragma once
#include "error_reasons.hpp"
#include <stdexcept>
#include <string>
#include <boost/utility/string_ref.hpp>
namespace tawashi {
class TawashiException : public std::runtime_error {
public:
TawashiException (ErrorReasons parReason, const boost::string_ref& parMessage);
~TawashiException() noexcept;
ErrorReasons reason() const { return m_reason; }
private:
ErrorReasons m_reason;
};
} //namespace tawashi

1347
src/tawashi/tiger.c Normal file

File diff suppressed because it is too large Load diff

125
src/tawashi/tiger.h Normal file
View file

@ -0,0 +1,125 @@
/**
* Copyright (c) 2012 Francisco Blas Izquierdo Riera (klondike)
* The Tiger algorithm was written by Eli Biham and Ross Anderson and is
* available on the official Tiger algorithm page.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* the algorithm authorsip notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* 4. If this license is not appropriate for you please write me at
* klondike ( a t ) klondike ( d o t ) es to negotiate another license.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
/**
* These are some implementations of tiger made without looking at the original
* reference code to ensure the resulting code can be published under a free
* license. The paper was looked though to know how did tiger work.
*/
/** Implementation details:
* * Here we assume char and unsigned char have size 1. If thats not the case in
* your compiler you may want to replace them by a type that does
*/
#ifndef TIGER_H
#define TIGER_H 1
#if !defined(_MSC_VER) || (_MSC_VER >= 1600)
#include <stdint.h>
#else
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#endif
#if _M_IX86_FP >= 2
#define __SSE2__
#endif
#ifdef __linux
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define IS_LITTLE_ENDIAN
#elif __BYTE_ORDER == __BIG_ENDIAN
#define USE_BIG_ENDIAN
#elif __BYTE_ORDER == __PDP_ENDIAN
#error "If you feel like writting code for PDP endianess go ahead, I'm not doing that"
#else
#error "Unknown endianess"
#endif
#else
//Assume little endian if you know how to detect endianism well on other compilers state it.
#define IS_LITTLE_ENDIAN
#endif
#if defined(_WIN64) || defined(__x86_64__) || defined(__amd64__)
#define HASX64
#endif
/** A word in the tiger hash, 64 bits **/
typedef uint64_t t_word;
/** This one is provided as a commodity for people wanting an easy way to declare result variables **/
typedef t_word t_res[3];
/** Partial calculation as used by tigerp1 and tigerp2 **/
typedef struct {
t_res h; // Hash status
char r[128]; // SALT
t_word n; // Number of characters of r used
t_word hs; // Amount of total data hashed
} t_pres;
/** This one is provided as a commodity for people wanting an easy way to declare block variables **/
typedef t_word t_block[8];
/** Standard tiger calculation, put your string in str and the string length on length and get the result on res **/
void tiger(const char *str, t_word length, t_res res, char pad);
/** Similar to tiger but interleaving accesses to both equally sized strings to reduce overhead and pipeline stalls you get the result of str1 on res1 and the one of str2 on res2 **/
void tiger_2(const char *str1, const char *str2, t_word length, t_res res1, t_res res2, char pad);
#ifdef __SSE2__
/** This is equivalent to tiger_2 but uses SSE2 for the key schduling making it faster **/
void tiger_sse2(const char *str1, const char *str2, t_word length, t_res res1, t_res res2, char pad);
#endif
/** This function is optimized for use on TTHs just send the two concatenated hashes and you will get back the hash with a prepended 0x01 **/
void tiger_49(const char *str, t_res res);
/** This function is optimized for use on TTHs just send the 1024 sized block and you will get back the hash with a prepended 0x00 **/
void tiger_1025(const char *str, t_res res);
/** Interleaved version of tiger_49 you insert two hashes and get back two results **/
void tiger_2_49(const char *str1, const char *str2, t_res res1, t_res res2);
/** Interleaved version of tiger_1025 you insert two hashes and get back two results **/
void tiger_2_1025(const char *str1, const char *str2, t_res res1, t_res res2);
#ifdef __SSE2__
/** SSE2 version of tiger_49 you insert two hashes and get back two results **/
void tiger_sse2_49(const char *str1, const char *str2, t_res res1, t_res res2);
/** SSE2 version of tiger_1025 you insert two hashes and get back two results **/
void tiger_sse2_1025(const char *str1, const char *str2, t_res res1, t_res res2);
#endif
/** First stage of partial tiger calculation to improve password security during storage **/
void tigerp1(const char *password, t_word length, const char *salt, t_pres *pres);
/** Second stage of partial tiger calculation **/
void tigerp2(const t_pres *pres, const char *salt, t_word length, t_res res);
#endif