/* This file is part of mastodonpp. * Copyright © 2020 tastytea * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "instance.hpp" #include "log.hpp" #include #include #include namespace mastodonpp { using std::sort; using std::stoull; using std::exception; using std::regex; using std::regex_search; using std::smatch; uint64_t Instance::get_max_chars() noexcept { constexpr uint64_t default_max_chars{500}; if (_max_chars != 0) { return _max_chars; } try { debuglog << "Querying " << _hostname << " for max_toot_chars…\n"; const auto answer{make_request(http_method::GET, _baseuri + "/api/v1/instance", {})}; if (!answer) { debuglog << "Could not get instance info.\n"; return default_max_chars; } _max_chars = [&answer] { const regex re_chars{R"("max_toot_chars"\s*:\s*([^"]+))"}; smatch match; if (regex_search(answer.body, match, re_chars)) { return static_cast(stoull(match[1].str())); } debuglog << "max_toot_chars not found.\n"; return default_max_chars; }(); debuglog << "Set _max_chars to: " << _max_chars << '\n'; } catch (const exception &e) { debuglog << "Unexpected exception: " << e.what() << '\n'; return default_max_chars; } return _max_chars; } answer_type Instance::get_nodeinfo() { auto answer{make_request(http_method::GET, _baseuri + "/.well-known/nodeinfo", {})}; if (!answer) { debuglog << "NodeInfo not found.\n"; return answer; } vector hrefs; const regex re_href{R"("href"\s*:\s*"([^"]+)\")"}; smatch match; string body = answer.body; while (regex_search(body, match, re_href)) { hrefs.push_back(match[1].str()); debuglog << "Found href: " << hrefs.back() << '\n'; body = match.suffix(); } sort(hrefs.begin(), hrefs.end()); // We assume they are sortable strings. debuglog << "Selecting href: " << hrefs.back() << '\n'; return make_request(http_method::GET, hrefs.back(), {}); } vector Instance::get_post_formats() noexcept { constexpr auto default_value{"text/plain"}; if (!_post_formats.empty()) { return _post_formats; } try { debuglog << "Querying " << _hostname << " for postFormats…\n"; const auto answer{get_nodeinfo()}; if (!answer) { debuglog << "Couldn't get NodeInfo.\n"; _post_formats = {default_value}; return _post_formats; } const regex re_allformats(R"("postFormats"\s*:\s*\[([^\]]+)\])"); smatch match; if (!regex_search(answer.body, match, re_allformats)) { debuglog << "Couldn't find metadata.postFormats.\n"; _post_formats = {default_value}; return _post_formats; } string allformats{match[1].str()}; debuglog << "Found postFormats: " << allformats << '\n'; const regex re_format(R"(\s*"([^"]+)\"\s*,?)"); while (regex_search(allformats, match, re_format)) { _post_formats.push_back(match[1].str()); allformats = match.suffix(); debuglog << "Found postFormat: " << _post_formats.back() << '\n'; } } catch (const std::exception &e) { debuglog << "Unexpected exception: " << e.what() << '\n'; return {default_value}; } return _post_formats; } answer_type Instance::ObtainToken::step_1(const string_view client_name, const string_view scopes, const string_view website) { parametermap parameters { {"client_name", client_name},