Added percent-encoding
This commit is contained in:
parent
5bba324385
commit
1844f3571a
@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.7)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
project (mastodon-cpp
|
||||
VERSION 0.1.2
|
||||
VERSION 0.1.3
|
||||
LANGUAGES CXX
|
||||
)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fPIC")
|
||||
|
@ -41,7 +41,7 @@ Install with `make install`.
|
||||
|
||||
# Usage
|
||||
|
||||
The HTML reference can be generated with `build_doc.sh`, if doxygen is installed. Or just look in `src/mastodon-cpp.hpp`. There are examples in `src/examples/`.
|
||||
The HTML reference can be generated with `build_doc.sh`, if doxygen is installed. Or just look in `src/mastodon-cpp.hpp`. There are examples in `src/examples/`. All input is expected to be UTF-8.
|
||||
|
||||
## Most basic example
|
||||
|
||||
@ -86,6 +86,7 @@ If you use a debug build, you get more verbose error messages.
|
||||
* [x] Network stuff
|
||||
* [x] Comprehensive example
|
||||
* Version 0.2.0
|
||||
* [x] Escape user input
|
||||
* [ ] Implement all PATCH calls
|
||||
* [ ] Implement all POST calls
|
||||
* [ ] Implement all DELETE calls
|
||||
@ -93,7 +94,6 @@ If you use a debug build, you get more verbose error messages.
|
||||
* [ ] Handle HTTP statuses 301 & 302
|
||||
* [ ] Support registering as an application
|
||||
* Later
|
||||
* [ ] Escape user input
|
||||
* [ ] Asynchronous I/O
|
||||
|
||||
## Status of implementation
|
||||
|
@ -100,65 +100,66 @@ const std::uint16_t API::get(const Mastodon::API::v1 &call,
|
||||
{
|
||||
string strcall = "";
|
||||
bool firstparam = true;
|
||||
const string argument_encoded = urlencode(argument);
|
||||
|
||||
switch (call)
|
||||
{
|
||||
case v1::accounts_id:
|
||||
strcall = "/api/v1/accounts/" + argument;
|
||||
strcall = "/api/v1/accounts/" + argument_encoded;
|
||||
break;
|
||||
case v1::accounts_id_followers:
|
||||
strcall = "/api/v1/accounts/" + argument + "/followers";
|
||||
strcall = "/api/v1/accounts/" + argument_encoded + "/followers";
|
||||
break;
|
||||
case v1::accounts_id_following:
|
||||
strcall = "/api/v1/accounts/" + argument + "/following";
|
||||
strcall = "/api/v1/accounts/" + argument_encoded + "/following";
|
||||
break;
|
||||
case v1::accounts_id_statuses:
|
||||
strcall = "/api/v1/accounts/" + argument + "/statuses";
|
||||
strcall = "/api/v1/accounts/" + argument_encoded + "/statuses";
|
||||
break;
|
||||
case v1::accounts_relationships:
|
||||
strcall = "/api/v1/accounts/relationships?id=" + argument;
|
||||
strcall = "/api/v1/accounts/relationships?id=" + argument_encoded;
|
||||
firstparam = false;
|
||||
break;
|
||||
case v1::accounts_search:
|
||||
strcall = "/api/v1/accounts/search?q=" + argument;
|
||||
strcall = "/api/v1/accounts/search?q=" + argument_encoded;
|
||||
firstparam = false;
|
||||
break;
|
||||
case v1::accounts_id_lists:
|
||||
strcall = "/api/v1/accounts/" + argument + "/lists";
|
||||
strcall = "/api/v1/accounts/" + argument_encoded + "/lists";
|
||||
break;
|
||||
case v1::lists_id_accounts:
|
||||
strcall = "/api/v1/lists/" + argument + "/accounts";
|
||||
strcall = "/api/v1/lists/" + argument_encoded + "/accounts";
|
||||
break;
|
||||
case v1::lists_id:
|
||||
strcall = "/api/v1/lists/" + argument;
|
||||
strcall = "/api/v1/lists/" + argument_encoded;
|
||||
break;
|
||||
case v1::notifications_id:
|
||||
strcall = "/api/v1/notifications/" + argument;
|
||||
strcall = "/api/v1/notifications/" + argument_encoded;
|
||||
break;
|
||||
case v1::search:
|
||||
strcall = "/api/v1/search?q=" + argument;
|
||||
strcall = "/api/v1/search?q=" + argument_encoded;
|
||||
firstparam = false;
|
||||
break;
|
||||
case v1::statuses_id:
|
||||
strcall = "/api/v1/statuses/" + argument;
|
||||
strcall = "/api/v1/statuses/" + argument_encoded;
|
||||
break;
|
||||
case v1::statuses_id_context:
|
||||
strcall = "/api/v1/statuses/" + argument + "/context";
|
||||
strcall = "/api/v1/statuses/" + argument_encoded + "/context";
|
||||
break;
|
||||
case v1::statuses_id_card:
|
||||
strcall = "/api/v1/statuses/" + argument + "/card";
|
||||
strcall = "/api/v1/statuses/" + argument_encoded + "/card";
|
||||
break;
|
||||
case v1::statuses_id_reblogged_by:
|
||||
strcall = "/api/v1/statuses/" + argument + "/reblogged_by";
|
||||
strcall = "/api/v1/statuses/" + argument_encoded + "/reblogged_by";
|
||||
break;
|
||||
case v1::statuses_id_favourited_by:
|
||||
strcall = "/api/v1/statuses/" + argument + "/favourited_by";
|
||||
strcall = "/api/v1/statuses/" + argument_encoded + "/favourited_by";
|
||||
break;
|
||||
case v1::timelines_tag_hashtag:
|
||||
strcall = "/api/v1/timelines/tag/" + argument;
|
||||
strcall = "/api/v1/timelines/tag/" + argument_encoded;
|
||||
break;
|
||||
case v1::timelines_list_list_id:
|
||||
strcall = "/api/v1/timelines/list/" + argument;
|
||||
strcall = "/api/v1/timelines/list/" + argument_encoded;
|
||||
break;
|
||||
default:
|
||||
ttdebug << "ERROR: Invalid call.\n";
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <iostream>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include "macros.hpp"
|
||||
@ -59,6 +60,7 @@ const std::uint16_t API::http::request_sync(const method &meth,
|
||||
const string &data,
|
||||
string &answer)
|
||||
{
|
||||
ttdebug << "Path is: " << path << '\n';
|
||||
try
|
||||
{
|
||||
tcp::resolver::query query(_instance, "https");
|
||||
@ -154,17 +156,17 @@ const std::uint16_t API::http::request_sync(const method &meth,
|
||||
// Read body
|
||||
boost::system::error_code error;
|
||||
answer = "";
|
||||
std::ostringstream ss;
|
||||
std::ostringstream oss;
|
||||
while (boost::asio::read(_socket, response,
|
||||
boost::asio::transfer_at_least(1), error))
|
||||
{
|
||||
ss << &response;
|
||||
oss << &response;
|
||||
}
|
||||
if (error != boost::asio::error::eof)
|
||||
{
|
||||
throw boost::system::system_error(error);
|
||||
}
|
||||
answer = ss.str();
|
||||
answer = oss.str();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ const string API::maptostr(const parametermap &map, const bool &firstparam)
|
||||
{
|
||||
if (it.second.size() == 1)
|
||||
{
|
||||
result += (delim + it.first + "=" + it.second.front());
|
||||
result += (delim + it.first + "=" + urlencode(it.second.front()));
|
||||
if (delim == '?')
|
||||
{
|
||||
delim = '&';
|
||||
@ -68,7 +68,7 @@ const string API::maptostr(const parametermap &map, const bool &firstparam)
|
||||
{
|
||||
for (const string &str : it.second)
|
||||
{
|
||||
result += (delim + it.first + "[]=" + str);
|
||||
result += (delim + it.first + "[]=" + urlencode(str));
|
||||
if (delim == '?')
|
||||
{
|
||||
delim = '&';
|
||||
@ -80,3 +80,29 @@ const string API::maptostr(const parametermap &map, const bool &firstparam)
|
||||
ttdebug << "Constructed parameter string: " << result << '\n';
|
||||
return result;
|
||||
}
|
||||
|
||||
const std::string API::urlencode(const string &str) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
for (const std::uint8_t &b: str)
|
||||
{
|
||||
// Check for unreserved characters (RFC 3986 section 2.3)
|
||||
if ((b >= 0x30 && b <= 0x39) || // 0-9
|
||||
(b >= 0x41 && b <= 0x5A) || // A-Z
|
||||
(b >= 0x61 && b <= 0x7A) || // a-z
|
||||
b == 0x2D || b == 0x2E || // -, .
|
||||
b == 0x5F || b == 0x7E) // _, ~
|
||||
{
|
||||
oss << b;
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << '%' << std::hex << std::uppercase << (int)(unsigned char)b;
|
||||
}
|
||||
}
|
||||
|
||||
ttdebug << "Unencoded string: " << str << '\n';
|
||||
ttdebug << "Encoded string: " << oss.str() << '\n';
|
||||
return oss.str();
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
namespace Mastodon
|
||||
{
|
||||
/*!
|
||||
* @brief Class for the Mastodon API.
|
||||
* @brief Class for the Mastodon API. All input is expected to be UTF-8.
|
||||
* @section error Error codes
|
||||
* | Code | Explanation |
|
||||
* | --------: |:------------------------------|
|
||||
@ -197,6 +197,21 @@ public:
|
||||
*/
|
||||
const std::string get_useragent() const;
|
||||
|
||||
/*!
|
||||
* @brief Percent-encodes a string. This is done automatically, unless you
|
||||
* make a custom request.
|
||||
*
|
||||
* The only time you should use this, is if you use
|
||||
* get(const std::string &call, std::string &answer).
|
||||
*
|
||||
* See RFC 3986 section 2.1 for more info.
|
||||
*
|
||||
* @param str The string
|
||||
*
|
||||
* @return The percent-encoded string
|
||||
*/
|
||||
const std::string urlencode(const std::string &str) const;
|
||||
|
||||
private:
|
||||
const std::string _instance;
|
||||
const std::string _access_token;
|
||||
|
Reference in New Issue
Block a user