diff --git a/README.adoc b/README.adoc index 3660d32..186f9c0 100644 --- a/README.adoc +++ b/README.adoc @@ -53,12 +53,12 @@ Have a look at the link:{uri-reference}[reference]. int main() { - mastodonpp::Instance instance{"example.com", {}}; + mastodonpp::Instance instance{"example.com", "123AccessToken123"}; mastodonpp::Connection connection{instance}; const mastodonpp::parametermap parameters { - {"status", "How is the wheather?"}, + {"status", "How is the weather?"}, {"poll[options]", vector{"Nice", "not nice"}}, {"poll[expires_in]", "86400"} }; @@ -88,7 +88,7 @@ emerge -a dev-cpp/mastodonpp === Debian and Ubuntu We automatically generate packages for Debian buster (10) and Ubuntu bionic -(18.04), but only for x86_64 (amd64). Download the them at +(18.04), but only for x86_64 (amd64). Download them at link:{uri-base}/releases[schlomp.space]. [source,shell] @@ -99,7 +99,7 @@ apt install ./libmastodonpp*.deb === CentOS We automatically generate packages for CentOS 8, but only for x86_64 -(amd64). Download the them at link:{uri-base}/releases[schlomp.space]. +(amd64). Download them at link:{uri-base}/releases[schlomp.space]. [source,shell] -------------------------------------------------------------------------------- diff --git a/examples/example03_post_status.cpp b/examples/example03_post_status.cpp index db5545b..62fc919 100644 --- a/examples/example03_post_status.cpp +++ b/examples/example03_post_status.cpp @@ -33,7 +33,7 @@ using std::vector; int main(int argc, char *argv[]) { const vector args(argv, argv + argc); - if (args.size() <= 1) + if (args.size() <= 2) { cerr << "Usage: " << args[0] << " \n"; return 1; @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) constexpr auto poll_seconds{60 * 60 * 24 * 2}; // 2 days. const masto::parametermap parameters { - {"status", "How is the wheather?"}, + {"status", "How is the weather?"}, {"poll[options]", vector{"Nice", "not nice"}}, {"poll[expires_in]", to_string(poll_seconds)} }; diff --git a/examples/example04_post_with_attachment.cpp b/examples/example04_post_with_attachment.cpp new file mode 100644 index 0000000..f40170f --- /dev/null +++ b/examples/example04_post_with_attachment.cpp @@ -0,0 +1,100 @@ +/* This file is part of mastodonpp. + * Copyright © 2020 tastytea + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Post a status (/api/v1/status) with an attachment (/api/v1/media). + +#include "mastodonpp.hpp" + +#include +#include +#include +#include + +namespace masto = mastodonpp; +using std::cout; +using std::cerr; +using std::endl; +using std::string; +using std::to_string; +using std::string_view; +using std::vector; + +int main(int argc, char *argv[]) +{ + const vector args(argv, argv + argc); + if (args.size() <= 3) + { + cerr << "Usage: " << args[0] + << " \n"; + return 1; + } + + try + { + // Initialize an Instance and a Connection. + masto::Instance instance{args[1], args[2]}; + masto::Connection connection{instance}; + const string_view filename{args[3]}; + + // Create attachment. + auto answer{connection.post(masto::API::v1::media, + { + {"file", string("@file:") += filename}, + {"description", "Test."} + })}; + + // Get the ID of the attachment. + // You normally would use a JSON parser, of course. I don't use one + // because I don't want to add a dependency just for an example. + const auto pos{answer.body.find(R"("id":")") + 6}; + const auto endpos{answer.body.find(R"(",)", pos)}; + const auto media_id{answer.body.substr(pos, endpos - pos)}; + cout << "Attachment has ID: " << media_id << endl; + + // Post the status. Note that “media_ids” always has to be a vector. + answer = connection.post(masto::API::v1::statuses, + { + {"status", "Attachment test."}, + {"media_ids", + vector{media_id}} + }); + if (answer) + { + cout << "Successfully posted " << filename << ".\n"; + } + else + { + if (answer.curl_error_code == 0) + { + // If it is no libcurl error, it must be an HTTP error. + cerr << "HTTP status: " << answer.http_status << endl; + } + else + { + // Network errors like “Couldn't resolve host.”. + cerr << "libcurl error " << to_string(answer.curl_error_code) + << ": " << answer.error_message << endl; + } + } + } + catch (const masto::CURLException &e) + { + // Only libcurl errors that are not network errors will be thrown. + // There went probably something wrong with the initialization. + cerr << e.what() << endl; + } + + return 0; +} diff --git a/examples/example05_update_notification_settings.cpp b/examples/example05_update_notification_settings.cpp new file mode 100644 index 0000000..5690265 --- /dev/null +++ b/examples/example05_update_notification_settings.cpp @@ -0,0 +1,84 @@ +/* This file is part of mastodonpp. + * Copyright © 2020 tastytea + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Update notification settings (/api/pleroma/notification_settings). + +#include "mastodonpp.hpp" + +#include +#include +#include +#include + +namespace masto = mastodonpp; +using std::cout; +using std::cerr; +using std::endl; +using std::to_string; +using std::string_view; +using std::vector; + +int main(int argc, char *argv[]) +{ + const vector args(argv, argv + argc); + if (args.size() <= 2) + { + cerr << "Usage: " << args[0] << " \n"; + return 1; + } + + try + { + // Initialize an Instance and a Connection. + masto::Instance instance{args[1], args[2]}; + masto::Connection connection{instance}; + + // Update the settings. + const auto answer{connection.put( + masto::API::pleroma::notification_settings, + { + {"followers", "true"}, + {"follows", "true"}, + {"remote", "true"}, + {"local", "true"}, + })}; + if (answer) + { + cout << answer << endl; + } + else + { + if (answer.curl_error_code == 0) + { + // If it is no libcurl error, it must be an HTTP error. + cerr << "HTTP status: " << answer.http_status << endl; + } + else + { + // Network errors like “Couldn't resolve host.”. + cerr << "libcurl error " << to_string(answer.curl_error_code) + << ": " << answer.error_message << endl; + } + } + } + catch (const masto::CURLException &e) + { + // Only libcurl errors that are not network errors will be thrown. + // There went probably something wrong with the initialization. + cerr << e.what() << endl; + } + + return 0; +} diff --git a/examples/example06_update_name.cpp b/examples/example06_update_name.cpp new file mode 100644 index 0000000..699fc32 --- /dev/null +++ b/examples/example06_update_name.cpp @@ -0,0 +1,83 @@ +/* This file is part of mastodonpp. + * Copyright © 2020 tastytea + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Update account display name settings (/api/v1/accounts/update_credentials). + +#include "mastodonpp.hpp" + +#include +#include +#include +#include + +namespace masto = mastodonpp; +using std::cout; +using std::cerr; +using std::endl; +using std::to_string; +using std::string_view; +using std::vector; + +int main(int argc, char *argv[]) +{ + const vector args(argv, argv + argc); + if (args.size() <= 3) + { + cerr << "Usage: " << args[0] + << " \n"; + return 1; + } + const auto name{args[3]}; + + try + { + // Initialize an Instance and a Connection. + masto::Instance instance{args[1], args[2]}; + masto::Connection connection{instance}; + + // Update the settings. + const auto answer{connection.patch( + masto::API::v1::accounts_update_credentials, + { + {"display_name", name}, + })}; + if (answer) + { + cout << "Successfully changed display name.\n"; + } + else + { + if (answer.curl_error_code == 0) + { + // If it is no libcurl error, it must be an HTTP error. + cerr << "HTTP status: " << answer.http_status << endl; + } + else + { + // Network errors like “Couldn't resolve host.”. + cerr << "libcurl error " << to_string(answer.curl_error_code) + << ": " << answer.error_message << endl; + } + } + } + catch (const masto::CURLException &e) + { + // Only libcurl errors that are not network errors will be thrown. + // There went probably something wrong with the initialization. + cerr << e.what() << endl; + } + + return 0; +} diff --git a/examples/example07_delete_status.cpp b/examples/example07_delete_status.cpp new file mode 100644 index 0000000..fc333b5 --- /dev/null +++ b/examples/example07_delete_status.cpp @@ -0,0 +1,98 @@ +/* This file is part of mastodonpp. + * Copyright © 2020 tastytea + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Post a status (/api/v1/status), then delete it (/api/v1/statuses/:id). + +#include "mastodonpp.hpp" + +#include +#include +#include +#include +#include +#include + +namespace masto = mastodonpp; +using namespace std::chrono_literals; +using std::cout; +using std::cerr; +using std::endl; +using std::to_string; +using std::string_view; +using std::this_thread::sleep_for; +using std::vector; + +int main(int argc, char *argv[]) +{ + const vector args(argv, argv + argc); + if (args.size() <= 2) + { + cerr << "Usage: " << args[0] << " \n"; + return 1; + } + + try + { + // Initialize an Instance and a Connection. + masto::Instance instance{args[1], args[2]}; + masto::Connection connection{instance}; + + // Post a status. + auto answer{connection.post(masto::API::v1::statuses, + {{"status", "Delete me."}})}; + if (answer) + { + cout << "Successfully posted a status.\n"; + + // Get the ID of the post. + // You normally would use a JSON parser, of course. I don't use one + // because I don't want to add a dependency just for an example. + const auto pos{answer.body.rfind(R"("id":")") + 6}; + const auto endpos{answer.body.find(R"(",)", pos)}; + const auto id{answer.body.substr(pos, endpos - pos)}; + cout << "Post has ID: " << id << endl; + cout << "Waiting 10 seconds…\n"; + sleep_for(10s); + + answer = connection.del(masto::API::v1::statuses_id, {{"id", id}}); + if (answer) + { + cout << "Successfully deleted the status.\n"; + } + } + else + { + if (answer.curl_error_code == 0) + { + // If it is no libcurl error, it must be an HTTP error. + cerr << "HTTP status: " << answer.http_status << endl; + } + else + { + // Network errors like “Couldn't resolve host.”. + cerr << "libcurl error " << to_string(answer.curl_error_code) + << ": " << answer.error_message << endl; + } + } + } + catch (const masto::CURLException &e) + { + // Only libcurl errors that are not network errors will be thrown. + // There went probably something wrong with the initialization. + cerr << e.what() << endl; + } + + return 0; +} diff --git a/include/api.hpp b/include/api.hpp index 7c89af4..aab5f41 100644 --- a/include/api.hpp +++ b/include/api.hpp @@ -281,9 +281,9 @@ public: disable_account, account_register, - pleroma_notification_settings, - pleroma_healthcheck, - pleroma_change_email + notification_settings, + healthcheck, + change_email }; /*! diff --git a/include/connection.hpp b/include/connection.hpp index 0ed09eb..761f777 100644 --- a/include/connection.hpp +++ b/include/connection.hpp @@ -161,6 +161,84 @@ public: return post(endpoint, {}); } + /*! + * @brief Make a HTTP PATCH call with parameters. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * @param parameters A map of parameters. + * + * + * @since 0.2.0 + */ + [[nodiscard]] + answer_type patch(const endpoint_variant &endpoint, + const parametermap ¶meters); + + /*! + * @brief Make a HTTP PATCH call. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * + * @since 0.2.0 + */ + [[nodiscard]] + inline answer_type patch(const endpoint_variant &endpoint) + { + return patch(endpoint, {}); + } + + /*! + * @brief Make a HTTP PUT call with parameters. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * @param parameters A map of parameters. + * + * + * @since 0.2.0 + */ + [[nodiscard]] + answer_type put(const endpoint_variant &endpoint, + const parametermap ¶meters); + + /*! + * @brief Make a HTTP PUT call. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * + * @since 0.2.0 + */ + [[nodiscard]] + inline answer_type put(const endpoint_variant &endpoint) + { + return put(endpoint, {}); + } + + /*! + * @brief Make a HTTP DELETE call with parameters. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * @param parameters A map of parameters. + * + * + * @since 0.2.0 + */ + [[nodiscard]] + answer_type del(const endpoint_variant &endpoint, + const parametermap ¶meters); + + /*! + * @brief Make a HTTP DELETE call. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * + * @since 0.2.0 + */ + [[nodiscard]] + inline answer_type del(const endpoint_variant &endpoint) + { + return del(endpoint, {}); + } + /*! * @brief Copy new stream contents and delete the “original”. * diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index 97df5b7..9e7ee48 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -57,12 +57,17 @@ enum class http_method /*! * @brief `std::map` of parameters for %API calls. * + * Note that arrays always have to be specified as vectors, even if they have + * only 1 element. To send a file, use “\@file:” followed by the file + * name as value. + * * Example: * @code * parametermap parameters * { - * {"id", "12"}, - * {"poll[options]", vector{"Yes", "No", "Maybe"}} + * {"poll[expires_in]", "86400"}, + * {"poll[options]", vector{"Yes", "No", "Maybe"}}, + * {"status", "How is the weather?"} * }; * @endcode * @@ -294,6 +299,18 @@ private: */ void add_parameters_to_uri(string &uri, const parametermap ¶meters); + /*! + * @brief Add `*curl_mimepart` to `*curl_mime`. + * + * @param mime Initialized `*curl_mime`. @param name Name of the field. + * @param data Data of the field. If it begins with `\@file:, the + * rest of the ergument is treated as a filename. + * + * @since 0.1.1 + */ + void add_mime_part(curl_mime *mime, + string_view name, string_view data) const; + /*! * @brief Convert parametermap to `*curl_mime`. * diff --git a/include/mastodonpp.hpp b/include/mastodonpp.hpp index 9d00369..d6f8564 100644 --- a/include/mastodonpp.hpp +++ b/include/mastodonpp.hpp @@ -69,7 +69,9 @@ * * @subsection input Input * - * All text input is expected to be UTF-8. + * * All text input is expected to be UTF-8. + * * To send a file, use “\@file:” followed by the file name as value + * in the @link mastodonpp::parametermap parametermap@endlink. * * @section exceptions Exceptions * @@ -99,6 +101,10 @@ * @example example01_instance_info.cpp * @example example02_streaming.cpp * @example example03_post_status.cpp + * @example example04_post_with_attachment.cpp + * @example example05_update_notification_settings.cpp + * @example example06_update_name.cpp + * @example example07_delete_status.cpp */ /*! diff --git a/src/api.cpp b/src/api.cpp index cb60a8b..d56298b 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -31,8 +31,8 @@ const map API::_endpoint_map {v1::apps_verify_credentials, "/api/v1/apps/verify/credentials"}, {v1::accounts, "/api/v1/accounts"}, - {v1::accounts_verify_credentials, "/api/v1/accounts/verify/credentials"}, - {v1::accounts_update_credentials, "/api/v1/accounts/update/credentials"}, + {v1::accounts_verify_credentials, "/api/v1/accounts/verify_credentials"}, + {v1::accounts_update_credentials, "/api/v1/accounts/update_credentials"}, {v1::accounts_id, "/api/v1/accounts/"}, {v1::accounts_id_statuses, "/api/v1/accounts//statuses"}, {v1::accounts_id_followers, "/api/v1/accounts//followers"}, @@ -61,7 +61,7 @@ const map API::_endpoint_map {v1::domain_blocks, "/api/v1/domain/blocks"}, {v1::filters, "/api/v1/filters"}, - {v1::filters_id, "/api/v1/filters/id"}, + {v1::filters_id, "/api/v1/filters/"}, {v1::reports, "/api/v1/reports"}, @@ -73,16 +73,16 @@ const map API::_endpoint_map {v1::endorsements, "/api/v1/endorsements"}, {v1::featured_tags, "/api/v1/featured/tags"}, - {v1::featured_tags_id, "/api/v1/featured/tags/id"}, + {v1::featured_tags_id, "/api/v1/featured/tags/"}, {v1::featured_tags_suggestions, "/api/v1/featured/tags/suggestions"}, {v1::preferences, "/api/v1/preferences"}, {v1::suggestions, "/api/v1/suggestions"}, - {v1::suggestions_account_id, "/api/v1/suggestions/account/id"}, + {v1::suggestions_account_id, "/api/v1/suggestions/account/"}, {v1::statuses, "/api/v1/statuses"}, - {v1::statuses_id, "/api/v1/statuses/id"}, + {v1::statuses_id, "/api/v1/statuses/"}, {v1::statuses_id_context, "/api/v1/statuses//context"}, {v1::statuses_id_reblogged_by, "/api/v1/statuses//reblogged/by"}, {v1::statuses_id_favourited_by, "/api/v1/statuses//favourited/by"}, @@ -98,21 +98,21 @@ const map API::_endpoint_map {v1::statuses_id_unpin, "/api/v1/statuses//unpin"}, {v1::media, "/api/v1/media"}, - {v1::media_id, "/api/v1/media/id"}, + {v1::media_id, "/api/v1/media/"}, - {v1::polls_id, "/api/v1/polls/id"}, + {v1::polls_id, "/api/v1/polls/"}, {v1::polls_id_votes, "/api/v1/polls//votes"}, {v1::scheduled_statuses, "/api/v1/scheduled/statuses"}, - {v1::scheduled_statuses_id, "/api/v1/scheduled/statuses/id"}, + {v1::scheduled_statuses_id, "/api/v1/scheduled/statuses/"}, {v1::timelines_public, "/api/v1/timelines/public"}, {v1::timelines_tag_hashtag, "/api/v1/timelines/tag/"}, {v1::timelines_home, "/api/v1/timelines/home"}, - {v1::timelines_list_list_id, "/api/v1/timelines/list/list/id"}, + {v1::timelines_list_list_id, "/api/v1/timelines/list/list/"}, {v1::conversations, "/api/v1/conversations"}, - {v1::conversations_id, "/api/v1/conversations/id"}, + {v1::conversations_id, "/api/v1/conversations/"}, {v1::conversations_id_read, "/api/v1/conversations//read"}, {v1::lists, "/api/v1/lists"}, @@ -131,7 +131,7 @@ const map API::_endpoint_map {v1::streaming_direct, "/api/v1/streaming/direct"}, {v1::notifications, "/api/v1/notifications"}, - {v1::notifications_id, "/api/v1/notifications/id"}, + {v1::notifications_id, "/api/v1/notifications/"}, {v1::notifications_clear, "/api/v1/notifications/clear"}, {v1::notifications_id_dismiss, "/api/v1/notifications//dismiss"}, @@ -148,7 +148,7 @@ const map API::_endpoint_map {v1::custom_emojis, "/api/v1/custom/emojis"}, {v1::admin_accounts, "/api/v1/admin/accounts"}, - {v1::admin_accounts_id, "/api/v1/admin/accounts/id"}, + {v1::admin_accounts_id, "/api/v1/admin/accounts/"}, {v1::admin_accounts_account_id_action, "/api/v1/admin/accounts/account//action"}, {v1::admin_accounts_id_approve, "/api/v1/admin/accounts//approve"}, @@ -157,11 +157,11 @@ const map API::_endpoint_map {v1::admin_accounts_id_unsilence, "/api/v1/admin/accounts//unsilence"}, {v1::admin_accounts_id_unsuspend, "/api/v1/admin/accounts//unsuspend"}, {v1::admin_reports, "/api/v1/admin/reports"}, - {v1::admin_reports_id, "/api/v1/admin/reports/id"}, + {v1::admin_reports_id, "/api/v1/admin/reports/"}, {v1::admin_reports_id_assign_to_self, "/api/v1/admin/reports//assign/to/self"}, {v1::admin_reports_id_unassign, "/api/v1/admin/reports//unassign"}, - {v1::admin_reports_id_resolve, "/api/v1/admin/reports/resolve"}, + {v1::admin_reports_id_resolve, "/api/v1/admin/reports//resolve"}, {v1::admin_reports_id_reopen, "/api/v1/admin/reports//reopen"}, {v1::pleroma_notifications_read, " /api/v1/pleroma/notifications/read"}, @@ -171,7 +171,7 @@ const map API::_endpoint_map {v1::pleroma_accounts_id_unsubscribe, "/api/v1/pleroma/accounts//unsubscribe"}, {v1::pleroma_accounts_id_favourites, - "/api/v1/pleroma/accounts/:id/favourites"}, + "/api/v1/pleroma/accounts//favourites"}, {v1::pleroma_accounts_update_avatar, "/api/v1/pleroma/accounts/update_avatar"}, {v1::pleroma_accounts_update_banner, @@ -239,10 +239,9 @@ const map API::_endpoint_map {pleroma::disable_account, "/api/pleroma/disable_account"}, {pleroma::account_register, "/api/pleroma/account/register"}, - {pleroma::pleroma_notification_settings, - "/api/pleroma/pleroma/notification_settings"}, - {pleroma::pleroma_healthcheck, "/api/pleroma/pleroma/healthcheck"}, - {pleroma::pleroma_change_email, "/api/pleroma/pleroma/change_email"}, + {pleroma::notification_settings, "/api/pleroma/notification_settings"}, + {pleroma::healthcheck, "/api/pleroma/healthcheck"}, + {pleroma::change_email, "/api/pleroma/change_email"}, }; } // namespace mastodonpp diff --git a/src/connection.cpp b/src/connection.cpp index f74a816..3a10d14 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -61,6 +61,27 @@ answer_type Connection::post(const endpoint_variant &endpoint, endpoint_to_uri(endpoint), parameters); } +answer_type Connection::patch(const endpoint_variant &endpoint, + const parametermap ¶meters) +{ + return make_request(http_method::PATCH, + endpoint_to_uri(endpoint), parameters); +} + +answer_type Connection::put(const endpoint_variant &endpoint, + const parametermap ¶meters) +{ + return make_request(http_method::PUT, + endpoint_to_uri(endpoint), parameters); +} + +answer_type Connection::del(const endpoint_variant &endpoint, + const parametermap ¶meters) +{ + return make_request(http_method::DELETE, + endpoint_to_uri(endpoint), parameters); +} + string Connection::get_new_stream_contents() { buffer_mutex.lock(); diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 91fb39e..f613766 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -111,20 +111,44 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri, } case http_method::PATCH: { + if (!parameters.empty()) + { + curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PATCH"); + break; } case http_method::PUT: { + if (!parameters.empty()) + { + curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - code = curl_easy_setopt(_connection, CURLOPT_UPLOAD, 1L); + code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PUT"); + break; } case http_method::DELETE: { + if (!parameters.empty()) + { + curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) code = curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "DELETE"); + break; } } @@ -293,6 +317,8 @@ bool CURLWrapper::replace_parameter_in_uri(string &uri, { uri.replace(pos, parameter.first.size() + 2, get(parameter.second)); + debuglog << "Replaced :" << parameter.first << " in URI with " + << get(parameter.second) << '\n'; return true; } } @@ -339,12 +365,45 @@ void CURLWrapper::add_parameters_to_uri(string &uri, } } +void CURLWrapper::add_mime_part(curl_mime *mime, + string_view name, string_view data) const +{ + curl_mimepart *part{curl_mime_addpart(mime)}; + if (part == nullptr) + { + throw CURLException{"Could not build HTTP form."}; + } + + CURLcode code{curl_mime_name(part, name.data())}; + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + + if (data.substr(0, 6) == "@file:") + { + const string_view filename{data.substr(6)}; + code = curl_mime_filedata(part, filename.data()); + } + else + { + code = curl_mime_data(part, data.data(), CURL_ZERO_TERMINATED); + } + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + + debuglog << "Set form part: " << name << " = " << data << '\n'; +} + 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)) @@ -352,52 +411,16 @@ curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri, continue; } - CURLcode code; if (holds_alternative(param.second)) { - curl_mimepart *part{curl_mime_addpart(mime)}; - if (part == nullptr) - { - throw CURLException{"Could not build HTTP form."}; - } - - 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'; + add_mime_part(mime, param.first, get(param.second)); } else { for (const auto &arg : get>(param.second)) { - curl_mimepart *part{curl_mime_addpart(mime)}; - if (part == nullptr) - { - throw CURLException{"Could not build HTTP form."}; - } - - 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'; + const string_view name{string(param.first) += "[]"}; + add_mime_part(mime, name, arg); } } }