diff --git a/cmake/mastodonppConfig.cmake.in b/cmake/mastodonppConfig.cmake.in index 4dbde82..e703c6a 100644 --- a/cmake/mastodonppConfig.cmake.in +++ b/cmake/mastodonppConfig.cmake.in @@ -1,5 +1,5 @@ include(CMakeFindDependencyMacro) -find_dependency(CURL 7.32 REQUIRED) +find_dependency(CURL 7.56 REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index f4da6ff..0daf771 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -263,6 +263,18 @@ private: */ void setup_curl(); + /*! + * @brief Replace parameter in URI. + * + * @param uri Reference to the URI. + * @param parameter The parameter. + * + * @return true if parameter was replaced. + * + * @since 0.1.0 + */ + bool replace_parameter_in_uri(string &uri, const parameterpair ¶meter); + /*! * @brief Add parameters to URI. * @@ -272,6 +284,23 @@ private: * @since 0.1.0 */ void add_parameters_to_uri(string &uri, const parametermap ¶meters); + + /*! + * @brief Convert parametermap to `*curl_mime`. + * + * For more information consult [curl_mime_init(3)] + * (https://curl.haxx.se/libcurl/c/curl_mime_init.html). Calls + * replace_parameter_in_uri(). + * + * @param uri Reference to the URI. + * @param parameters The parametermap. + * + * @return `*curl_mime`. + * + * @since 0.1.0 + */ + curl_mime *parameters_to_curl_mime(string &uri, + const parametermap ¶meters); }; } // namespace mastodonpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9ede8d..a7c16ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ include(GNUInstallDirs) -find_package(CURL 7.32 REQUIRED) +find_package(CURL 7.56 REQUIRED) # Write version in header. configure_file ("version.hpp.in" diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index f675cca..6746fe1 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -68,7 +68,7 @@ CURLWrapper::~CURLWrapper() noexcept void CURLWrapper::set_proxy(const string_view proxy) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - CURLcode code = curl_easy_setopt(_connection, CURLOPT_PROXY, proxy); + CURLcode code{curl_easy_setopt(_connection, CURLOPT_PROXY, proxy)}; if (code != CURLE_OK) { throw CURLException{code, "Failed to set proxy", _curl_buffer_error}; @@ -101,8 +101,11 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri, } case http_method::POST: { + curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - code = curl_easy_setopt(_connection, CURLOPT_POST, 1L); + code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + break; } case http_method::PATCH: @@ -247,27 +250,38 @@ void CURLWrapper::setup_curl() curl_easy_setopt(_connection, CURLOPT_MAXREDIRS, 10L); } +bool CURLWrapper::replace_parameter_in_uri(string &uri, + const parameterpair ¶meter) +{ + static constexpr array replace + { + "id", "nickname", "nickname_or_id", + "hashtag", "permission_group" + }; + if (any_of(replace.begin(), replace.end(), + [¶meter](const auto &s) { return s == parameter.first; })) + { + const auto pos{uri.find('<')}; + if (pos != string::npos) + { + uri.replace(pos, parameter.first.size() + 2, + get(parameter.second)); + return true; + } + } + + return false; +} + void CURLWrapper::add_parameters_to_uri(string &uri, const parametermap ¶meters) { // Replace with the value of parameter “id” and so on. for (const auto ¶m : parameters) { - static constexpr array replace_in_uri - { - "id", "nickname", "nickname_or_id", - "hashtag", "permission_group" - }; - if (any_of(replace_in_uri.begin(), replace_in_uri.end(), - [¶m](const auto &s) { return s == param.first; })) + if (replace_parameter_in_uri(uri, param)) { - const auto pos{uri.find('<')}; - if (pos != string::npos) - { - uri.replace(pos, param.first.size() + 2, - get(param.second)); - continue; - } + continue; } static bool first{true}; @@ -298,4 +312,64 @@ void CURLWrapper::add_parameters_to_uri(string &uri, } } +curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri, + const parametermap ¶meters) +{ + debuglog << "Building HTTP form.\n"; + + curl_mime *mime{curl_mime_init(_connection)}; + for (const auto ¶m : parameters) + { + if (replace_parameter_in_uri(uri, param)) + { + continue; + } + + curl_mimepart *part{curl_mime_addpart(mime)}; + if (part == nullptr) + { + throw CURLException{"Could not build HTTP form."}; + } + + CURLcode code; + if (holds_alternative(param.second)) + { + code = curl_mime_name(part, param.first.data()); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + + code = curl_mime_data(part, get(param.second).data(), + CURL_ZERO_TERMINATED); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + debuglog << "Set form part: " << param.first << " = " + << get(param.second) << '\n'; + } + else + { + for (const auto &arg : get>(param.second)) + { + const string name{string(param.first) += "[]"}; + code = curl_mime_name(part, name.c_str()); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + code = curl_mime_data(part, arg.data(), CURL_ZERO_TERMINATED); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + debuglog << "Set form part: " << name << " = " << arg << '\n'; + } + } + } + + return mime; +} + } // namespace mastodonpp