diff --git a/.drone.yml b/.drone.yml index de5c4ed..3ad6824 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,7 +2,7 @@ pipeline: download: image: plugins/download pull: true - source: https://schlomp.space/attachments/5ab8f994-669a-47f8-8ac7-ed5902ad0339 + source: https://schlomp.space/attachments/e1c1e64b-1192-4037-aad4-95238ad648b0 destination: mastodon-cpp.deb gcc6: diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c2d24..de9099c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required (VERSION 3.7) project (expandurl-mastodon - VERSION 0.9.14 - LANGUAGES CXX -) + VERSION 0.9.14 + LANGUAGES CXX + ) include(GNUInstallDirs) find_package(CURL REQUIRED) @@ -15,14 +15,10 @@ set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") -if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - # uint_fast16_t can be bigger than 16 bit, but that doesn't matter because - # everything but the last 16 bit is padded with zeroes. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format") -endif() +set(CMAKE_CXX_FLAGS_DEBUG + "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wpedantic -ftrapv \ +-fsanitize=undefined -g -Og -fno-omit-frame-pointer") -include_directories(${PROJECT_SOURCE_DIR}/src) include_directories(${PROJECT_BINARY_DIR}) include_directories(${CURL_INCLUDE_DIRS}) @@ -37,26 +33,26 @@ link_directories(${LIBXDG_BASEDIR_LIBRARY_DIRS}) # Write version in header configure_file ( - "${PROJECT_SOURCE_DIR}/src/version.hpp.in" - "${PROJECT_BINARY_DIR}/version.hpp" -) + "${PROJECT_SOURCE_DIR}/src/version.hpp.in" + "${PROJECT_BINARY_DIR}/version.hpp" + ) file(GLOB sources src/*.cpp) add_executable(expandurl-mastodon ${sources}) target_link_libraries(expandurl-mastodon - ${CURLPP_LIBRARIES} ${JSONCPP_LIBRARIES} - ${LIBXDG_BASEDIR_LIBRARIES} mastodon-cpp pthread stdc++fs) + ${CURLPP_LIBRARIES} ${JSONCPP_LIBRARIES} ${LIBXDG_BASEDIR_LIBRARIES} + mastodon-cpp pthread stdc++fs) install(TARGETS expandurl-mastodon DESTINATION ${CMAKE_INSTALL_BINDIR}) set(WITH_MAN "YES" CACHE STRING "WITH_MAN defaults to \"YES\"") if (WITH_MAN) - add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1" - WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" - DEPENDS "${CMAKE_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.1.adoc" - COMMAND ${CMAKE_SOURCE_DIR}/build_manpage.sh - ARGS ${PROJECT_VERSION}) - add_custom_target(run ALL - DEPENDS "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1") - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1 - DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1" + WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" + DEPENDS "${CMAKE_SOURCE_DIR}/${CMAKE_PROJECT_NAME}.1.adoc" + COMMAND ${CMAKE_SOURCE_DIR}/build_manpage.sh + ARGS ${PROJECT_VERSION}) + add_custom_target(run ALL + DEPENDS "${PROJECT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1") + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif() diff --git a/README.md b/README.md index a65150f..4d2e079 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Have a look at the [manpage](https://schlomp.space/tastytea/expandurl-mastodon/s # Copyright ```PLAIN -Copyright © 2018 tastytea . +Copyright © 2018, 2019 tastytea . License GPLv3: GNU GPL version 3 . This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. diff --git a/build_manpage.sh b/build_manpage.sh index 0a1787d..966607e 100755 --- a/build_manpage.sh +++ b/build_manpage.sh @@ -4,8 +4,9 @@ name="expandurl-mastodon" if [ -n "${1}" ]; then dir="$(dirname ${0})" + version="${1}" cp -vf "${dir}/${name}.1.adoc" . - sed -Ei "s/(Revision: +)[0-9]+\.[0-9]\.[0-9]/\1${1}/" "${name}.1.adoc" + sed -Ei "s/(Revision: +)[0-9]+\.[0-9]\.[0-9]/\1${version}/" "${name}.1.adoc" a2x --doctype manpage --format manpage --no-xmllint "${name}.1.adoc" else echo "usage: ${0} VERSION" >&2 diff --git a/src/configjson.cpp b/src/configjson.cpp index a23ba79..75cf0d4 100644 --- a/src/configjson.cpp +++ b/src/configjson.cpp @@ -1,5 +1,5 @@ /* This file is part of expandurl-mastodon. - * Copyright © 2018 tastytea + * Copyright © 2018, 2019 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,7 +42,7 @@ ConfigJSON::ConfigJSON(const string &filename, const string &subdir) _filepath += '/' + filename; } -const bool ConfigJSON::read() +bool ConfigJSON::read() { std::ifstream file(_filepath); if (file.is_open()) @@ -60,7 +60,7 @@ const bool ConfigJSON::read() } } -const bool ConfigJSON::write() +bool ConfigJSON::write() { std::ofstream file(_filepath); if (file.is_open()) diff --git a/src/configjson.hpp b/src/configjson.hpp index bffa0be..044bc8c 100644 --- a/src/configjson.hpp +++ b/src/configjson.hpp @@ -1,5 +1,5 @@ /* This file is part of expandurl-mastodon. - * Copyright © 2018 tastytea + * Copyright © 2018, 2019 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,14 +43,14 @@ public: * * @return `true` on success */ - const bool read(); + bool read(); /*! * @brief Write the file * * @return `true` on success */ - const bool write(); + bool write(); /*! * @brief Returns a reference to the config as Json::Value diff --git a/src/expandurl-mastodon.hpp b/src/expandurl-mastodon.hpp index 9c80573..319d8c4 100644 --- a/src/expandurl-mastodon.hpp +++ b/src/expandurl-mastodon.hpp @@ -1,5 +1,5 @@ /* This file is part of expandurl-mastodon. - * Copyright © 2018 tastytea + * Copyright © 2018, 2019 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,15 +27,15 @@ #include #include "configjson.hpp" +using namespace Mastodon; + using std::string; using Mastodon::API; -using Mastodon::Easy; extern ConfigJSON configfile; void signal_handler(int signum); - /*! * @brief Extract URLs from HTML * @@ -54,7 +54,7 @@ const string expand(const string &url); /*! * @brief Filters out tracking stuff - * + * * Currently removes all arguments beginning with `utm_` * * @param url URL to filter @@ -70,7 +70,7 @@ const string strip(const string &url); * inserted. * */ -const void init_replacements(); +void init_replacements(); class Listener @@ -82,24 +82,24 @@ public: /*! * @brief Starts listening on Mastodon */ - const void start(); + void start(); /*! * @brief Stops listening on Mastodon */ - const void stop(); + void stop(); const std::vector get_new_messages(); const std::vector catchup(); Easy::Status get_status(const string &id); - const bool send_reply(const Easy::Status &to_status, const string &message); + bool send_reply(const Easy::Status &to_status, const string &message); const string get_parent_id(const Easy::Notification ¬if); - const bool stillrunning() const; + bool stillrunning() const; private: string _instance; string _access_token; - std::unique_ptr _masto; + std::unique_ptr _masto; string _stream; std::unique_ptr _ptr; std::thread _thread; @@ -109,10 +109,10 @@ private: string _proxy_password; Json::Value &_config; - const void read_config(); - const bool write_config(); - const bool register_app(); - const void set_proxy(Easy &masto); + void read_config(); + bool write_config(); + bool register_app(); + void set_proxy(Easy::API &masto); }; #endif // EXPANDURL_MASTODON_HPP diff --git a/src/main.cpp b/src/main.cpp index 84b3e0c..55d8205 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* This file is part of expandurl-mastodon. - * Copyright © 2018 tastytea + * Copyright © 2018, 2019 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,14 +18,16 @@ #include #include #include +#include #include #include // getuid() #include #include "configjson.hpp" #include "expandurl-mastodon.hpp" +using namespace Mastodon; + using std::string; -using Mastodon::Easy; bool running = true; ConfigJSON configfile("expandurl-mastodon.json"); @@ -50,7 +52,7 @@ void signal_handler(int signum) } } -int main(int argc, char *argv[]) +int main() { signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); @@ -99,11 +101,11 @@ int main(int argc, char *argv[]) status = listener.get_status(id); if (status.valid()) { - string message = ""; - for (const string &url : get_urls(status.content())) - { - message += url + " \n"; - } + const std::vector vec = get_urls(status.content()); + const string message = + std::accumulate(vec.begin(), vec.end(), string(), + [](const string &s1, const string s2) + { return s1 + s2 + " \n"; }); if (!message.empty()) { if (!listener.send_reply(notif.status(), message)) diff --git a/src/masto.cpp b/src/masto.cpp index e6e894e..8b716c9 100644 --- a/src/masto.cpp +++ b/src/masto.cpp @@ -1,5 +1,5 @@ /* This file is part of expandurl-mastodon. - * Copyright © 2018 tastytea + * Copyright © 2018, 2019 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ using std::cout; using std::string; +using std::uint8_t; Listener::Listener() : _instance("") @@ -59,7 +60,7 @@ Listener::Listener() } } - _masto = std::make_unique(_instance, _access_token); + _masto = std::make_unique(_instance, _access_token); _masto->set_useragent(static_cast("expandurl-mastodon/") + global::version); set_proxy(*_masto); @@ -69,7 +70,7 @@ Listener::~Listener() { } -const void Listener::read_config() +void Listener::read_config() { _instance = _config["account"].asString(); _instance = _instance.substr(_instance.find('@') + 1); @@ -79,44 +80,19 @@ const void Listener::read_config() _proxy_password = _config["proxy"]["password"].asString(); } -const void Listener::start() +void Listener::start() { - constexpr uint_fast8_t delay_after_error = 120; - static std::uint_fast16_t ret; - _thread = std::thread([=] - { - ret = 0; - _running = true; - Easy masto(_instance, _access_token); - masto.set_useragent(static_cast("expandurl-mastodon/") + - global::version); - set_proxy(masto); - ret = masto.get_stream(Mastodon::API::v1::streaming_user, _stream, _ptr); - syslog(LOG_DEBUG, "Connection lost."); - if (ret != 0 && ret != 14) // 14 means canceled by user - { - syslog(LOG_ERR, "Connection terminated: Error %u", ret); - syslog(LOG_INFO, "Waiting for %u seconds", delay_after_error); - std::this_thread::sleep_for(std::chrono::seconds(delay_after_error)); - } - _running = false; - }); - while (!_ptr) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } + Easy::API masto(_instance, _access_token); + _running = true; - if (ret == 0) - { - syslog(LOG_NOTICE, "Connected to %s", _instance.c_str()); - } - else if (ret != 14) - { // If the stream thread sleeps, the main thread should sleep too - std::this_thread::sleep_for(std::chrono::seconds(delay_after_error)); - } + masto.set_useragent(string("expandurl-mastodon/") + global::version); + set_proxy(masto); + masto.get_stream(Mastodon::API::v1::streaming_user, _ptr, _stream); + + syslog(LOG_NOTICE, "Connecting to %s ...", _instance.c_str()); } -const void Listener::stop() +void Listener::stop() { if (!configfile.write()) { @@ -150,14 +126,25 @@ const std::vector Listener::get_new_messages() { for (const Easy::stream_event &event : Easy::parse_stream(_stream)) { - if (event.first == Easy::event_type::Notification) + if (event.type == Easy::event_type::Notification) { - Easy::Notification notif(event.second); + Easy::Notification notif(event.data); if (notif.type() == Easy::notification_type::Mention) { v.push_back(notif); } } + else if (event.type == Easy::event_type::Error) + { + constexpr uint8_t delay_after_error = 120; + syslog(LOG_DEBUG, "Connection lost."); + const Json::Value err; + syslog(LOG_ERR, "Connection terminated: Error %u", + err["error_code"].asUInt()); + syslog(LOG_INFO, "Waiting for %u seconds", delay_after_error); + std::this_thread::sleep_for(std::chrono::seconds(delay_after_error)); + _running = false; + } } _stream.clear(); lastping = system_clock::now(); @@ -183,26 +170,25 @@ const std::vector Listener::catchup() if (last_id != "") { syslog(LOG_DEBUG, "Catching up..."); - API::parametermap parameter = + parameters parameter = { { "since_id", { last_id } }, { "exclude_types", { "follow", "favourite", "reblog" } } }; - string answer; - std::uint_fast16_t ret; + return_call ret;; - ret = _masto->get(API::v1::notifications, parameter, answer); + ret = _masto->get(API::v1::notifications, parameter); - if (ret == 0) + if (ret) { - for (const string str : Easy::json_array_to_vector(answer)) + for (const string str : Easy::json_array_to_vector(ret.answer)) { v.push_back(Easy::Notification(str)); } } else { - syslog(LOG_ERR, "Could not catch up: Error %u", ret); + syslog(LOG_ERR, "Could not catch up: Error %u", ret.error_code); } } @@ -211,26 +197,24 @@ const std::vector Listener::catchup() Mastodon::Easy::Status Listener::get_status(const string &id) { - std::uint_fast16_t ret; - string answer; + return_call ret; - ret = _masto->get(API::v1::statuses_id, {{ "id", { id }}}, - answer); - if (ret == 0) + ret = _masto->get(API::v1::statuses_id, {{ "id", { id }}}); + if (ret) { - return Easy::Status(answer); + return Easy::Status(ret.answer); } else { - syslog(LOG_ERR, "Error %u in %s.", ret, __FUNCTION__); + syslog(LOG_ERR, "Error %u in %s.", ret.error_code, __FUNCTION__); return Easy::Status(); } } -const bool Listener::send_reply(const Easy::Status &to_status, - const string &message) +bool Listener::send_reply(const Easy::Status &to_status, + const string &message) { - std::uint_fast16_t ret = 0; + Easy::return_entity ret; Easy::Status new_status; if (to_status.visibility() == Easy::visibility_type::Public) @@ -246,52 +230,49 @@ const bool Listener::send_reply(const Easy::Status &to_status, new_status.sensitive(to_status.sensitive()); new_status.spoiler_text(to_status.spoiler_text()); - _masto->send_toot(new_status, ret); + ret = _masto->send_post(new_status); - if (ret == 0) + if (ret) { syslog(LOG_DEBUG, "Sent reply"); return true; } else { - syslog(LOG_ERR, "Error %u in %s.", ret, __FUNCTION__); + syslog(LOG_ERR, "Error %u in %s.", ret.error_code, __FUNCTION__); return false; } } const string Listener::get_parent_id(const Easy::Notification ¬if) { - string answer; - std::uint_fast16_t ret; + return_call ret; // Retry up to 2 times for (std::uint_fast8_t retries = 1; retries <= 2; ++retries) { // Fetch full status - ret = _masto->get(API::v1::search, {{ "q", { notif.status().url() }}}, - answer); - if (ret > 0) + ret = _masto->get(API::v1::search, {{ "q", { notif.status().url() }}}); + if (!ret) { syslog(LOG_ERR, "Error %u: Could not fetch status (in %s).", - ret, __FUNCTION__); + ret.error_code, __FUNCTION__); return 0; } ret = _masto->get(API::v1::statuses_id, - {{ "id", { notif.status().id() }}}, - answer); + {{ "id", { notif.status().id() }}}); - if (ret > 0) + if (!ret) { syslog(LOG_ERR, "Error %u: Could not get status (in %s).", - ret, __FUNCTION__); + ret.error_code, __FUNCTION__); return 0; } else { _config["last_id"] = notif.id(); - const Easy::Status s(answer); + const Easy::Status s(ret.answer); // If parent is found, return ID; else retry if (!s.in_reply_to_id().empty()) @@ -309,23 +290,23 @@ const string Listener::get_parent_id(const Easy::Notification ¬if) return 0; } -const bool Listener::stillrunning() const +bool Listener::stillrunning() const { return _running; } -const bool Listener::register_app() +bool Listener::register_app() { cout << "Account (username@instance): "; std::cin >> _instance; _config["account"] = _instance; _instance = _instance.substr(_instance.find('@') + 1); - _masto = std::make_unique(_instance, ""); + _masto = std::make_unique(_instance, ""); _masto->set_useragent(static_cast("expandurl-mastodon/") + global::version); - std::uint_fast16_t ret; + return_call ret; string client_id, client_secret, url; ret = _masto->register_app1("expandurl-mastodon", "urn:ietf:wg:oauth:2.0:oob", @@ -334,7 +315,7 @@ const bool Listener::register_app() client_id, client_secret, url); - if (ret == 0) + if (ret) { string code; cout << "Visit " << url << " to authorize this application.\n"; @@ -345,25 +326,25 @@ const bool Listener::register_app() "urn:ietf:wg:oauth:2.0:oob", code, _access_token); - if (ret == 0) + if (ret) { _config["access_token"] = _access_token; return true; } else { - syslog(LOG_ERR, "register_app2(): %u", ret); + syslog(LOG_ERR, "register_app2(): %u", ret.error_code); } } else { - syslog(LOG_ERR, "register_app1(): %u", ret); + syslog(LOG_ERR, "register_app1(): %u", ret.error_code); } return false; } -const void Listener::set_proxy(Mastodon::Easy &masto) +void Listener::set_proxy(Easy::API &masto) { if (!_proxy.empty()) { diff --git a/src/url.cpp b/src/url.cpp index 046d242..57d0e4a 100644 --- a/src/url.cpp +++ b/src/url.cpp @@ -1,5 +1,5 @@ /* This file is part of expandurl-mastodon. - * Copyright © 2018 tastytea + * Copyright © 2018, 2019 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ const std::vector get_urls(const string &html) // Add URL to vector if it is not a mention.# if (match[2].str().find("mention") == std::string::npos) { - string url = Easy::unescape_html(match[1].str()); + string url = unescape_html(match[1].str()); v.push_back(strip(expand(url))); } buffer = match.suffix().str(); @@ -106,7 +106,7 @@ const string strip(const string &url) return newurl; } -const void init_replacements() +void init_replacements() { using replace_pair = std::pair; Json::Value &config = configfile.get_json();