[WIP] Switch from curlpp to POCO.
Compilable, but untested and unfinished.
This commit is contained in:
parent
bd07dc6f9c
commit
f97608ecfa
|
@ -110,8 +110,7 @@ Not included in this list are entities.
|
||||||
| 110 | Connection timed out
|
| 110 | Connection timed out
|
||||||
| 111 | Connection refused (check http_error_code)
|
| 111 | Connection refused (check http_error_code)
|
||||||
| 113 | No route to host / Could not resolve host
|
| 113 | No route to host / Could not resolve host
|
||||||
| 192 | curlpp runtime error
|
| 150 | Encryption error (TODO: CHANGEME!)
|
||||||
| 193 | curlpp logic error
|
|
||||||
| 255 | Unknown error
|
| 255 | Unknown error
|
||||||
|===================================================
|
|===================================================
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ pkg_check_modules(curlpp REQUIRED IMPORTED_TARGET curlpp)
|
||||||
if(WITH_EASY)
|
if(WITH_EASY)
|
||||||
find_package(jsoncpp REQUIRED CONFIG)
|
find_package(jsoncpp REQUIRED CONFIG)
|
||||||
endif()
|
endif()
|
||||||
|
# Some distributions do not contain Poco*Config.cmake recipes.
|
||||||
|
find_package(Poco COMPONENTS Foundation Net NetSSL CONFIG)
|
||||||
|
|
||||||
if(WITH_EASY)
|
if(WITH_EASY)
|
||||||
file(GLOB_RECURSE sources *.cpp *.hpp)
|
file(GLOB_RECURSE sources *.cpp *.hpp)
|
||||||
|
@ -24,6 +26,7 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
target_include_directories(${PROJECT_NAME}
|
target_include_directories(${PROJECT_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
|
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>"
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
|
||||||
PUBLIC
|
PUBLIC
|
||||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
|
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||||
|
@ -36,6 +39,26 @@ else()
|
||||||
PUBLIC pthread PkgConfig::curlpp)
|
PUBLIC pthread PkgConfig::curlpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# If no Poco*Config.cmake recipes are found, look for headers in standard dirs.
|
||||||
|
if(PocoNetSSL_FOUND)
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
PRIVATE Poco::Foundation Poco::Net Poco::NetSSL)
|
||||||
|
else()
|
||||||
|
find_file(Poco_h NAMES "Poco/Poco.h"
|
||||||
|
PATHS "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
|
||||||
|
if("${Poco_h}" STREQUAL "Poco_h-NOTFOUND")
|
||||||
|
message(FATAL_ERROR "Could not find POCO.")
|
||||||
|
else()
|
||||||
|
message(WARNING
|
||||||
|
"Your distribution of POCO doesn't contain the *Config.cmake recipes, "
|
||||||
|
"but the files seem to be in the standard directories. "
|
||||||
|
"Let's hope this works.")
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
PRIVATE PocoFoundation PocoNet PocoNetSSL)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME}
|
install(TARGETS ${PROJECT_NAME}
|
||||||
EXPORT "${PROJECT_NAME}Targets"
|
EXPORT "${PROJECT_NAME}Targets"
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
|
307
src/http.cpp
307
src/http.cpp
|
@ -16,19 +16,36 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <functional> // std::bind
|
#include <functional> // std::bind
|
||||||
#include <list>
|
|
||||||
#include <cstring> // std::strncmp
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <curlpp/Options.hpp>
|
#include <curlpp/Options.hpp>
|
||||||
#include <curlpp/Exception.hpp>
|
#include <curlpp/Exception.hpp>
|
||||||
#include <curlpp/Infos.hpp>
|
#include <curlpp/Infos.hpp>
|
||||||
|
#include <Poco/Net/HTTPSClientSession.h>
|
||||||
|
#include <Poco/Net/HTTPRequest.h>
|
||||||
|
#include <Poco/Net/HTTPResponse.h>
|
||||||
|
#include <Poco/StreamCopier.h>
|
||||||
|
#include <Poco/URI.h>
|
||||||
|
#include <Poco/Environment.h>
|
||||||
|
#include <Poco/Exception.h>
|
||||||
|
#include <Poco/Net/NetException.h>
|
||||||
|
#include <Poco/Net/SSLException.h>
|
||||||
#include "debug.hpp"
|
#include "debug.hpp"
|
||||||
#include "mastodon-cpp.hpp"
|
#include "mastodon-cpp.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
using namespace Mastodon;
|
using namespace Mastodon;
|
||||||
namespace curlopts = curlpp::options;
|
namespace curlopts = curlpp::options;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
|
using std::istream;
|
||||||
|
using std::make_unique;
|
||||||
|
using std::move;
|
||||||
|
using Poco::Net::HTTPSClientSession;
|
||||||
|
using Poco::Net::HTTPRequest;
|
||||||
|
using Poco::Net::HTTPResponse;
|
||||||
|
using Poco::Net::HTTPMessage;
|
||||||
|
using Poco::StreamCopier;
|
||||||
|
using Poco::Environment;
|
||||||
|
|
||||||
API::http::http(const API &api, const string &instance,
|
API::http::http(const API &api, const string &instance,
|
||||||
const string &access_token)
|
const string &access_token)
|
||||||
|
@ -38,23 +55,65 @@ API::http::http(const API &api, const string &instance,
|
||||||
, _cancel_stream(false)
|
, _cancel_stream(false)
|
||||||
{
|
{
|
||||||
curlpp::initialize();
|
curlpp::initialize();
|
||||||
|
|
||||||
|
Poco::Net::initializeSSL();
|
||||||
|
|
||||||
|
// FIXME: rewrite set_proxy() and set proxy here.
|
||||||
|
// string proxy_host, proxy_userpw;
|
||||||
|
// parent.get_proxy(proxy_host, proxy_userpw);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HTTPSClientSession::ProxyConfig proxy;
|
||||||
|
string proxy_env = Environment::get("http_proxy");
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
// Only keep text between // and /.
|
||||||
|
if ((pos = proxy_env.find("//")) != string::npos)
|
||||||
|
{
|
||||||
|
proxy_env = proxy_env.substr(pos + 2);
|
||||||
|
}
|
||||||
|
if ((pos = proxy_env.find('/')) != string::npos)
|
||||||
|
{
|
||||||
|
proxy_env = proxy_env.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pos = proxy_env.find(':')) != string::npos)
|
||||||
|
{
|
||||||
|
proxy.host = proxy_env.substr(0, pos);
|
||||||
|
proxy.port = std::stoi(proxy_env.substr(pos + 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
proxy.host = proxy_env;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPSClientSession::setGlobalProxyConfig(proxy);
|
||||||
|
}
|
||||||
|
catch (const std::exception &)
|
||||||
|
{
|
||||||
|
// No proxy found, no problem.
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
API::http::~http()
|
API::http::~http()
|
||||||
{
|
{
|
||||||
curlpp::terminate();
|
curlpp::terminate();
|
||||||
|
|
||||||
|
Poco::Net::uninitializeSSL();
|
||||||
}
|
}
|
||||||
|
|
||||||
return_call API::http::request(const http_method &meth, const string &path)
|
return_call API::http::request(const http_method &meth, const string &path)
|
||||||
{
|
{
|
||||||
return request(meth, path, curlpp::Forms());
|
return request(meth, path, make_unique<HTMLForm>());
|
||||||
}
|
}
|
||||||
|
|
||||||
return_call API::http::request(const http_method &meth, const string &path,
|
return_call API::http::request(const http_method &meth, const string &path,
|
||||||
const curlpp::Forms &formdata)
|
unique_ptr<HTMLForm> formdata)
|
||||||
{
|
{
|
||||||
string answer;
|
string answer;
|
||||||
return request_common(meth, path, formdata, answer);
|
return request_common(meth, path, move(formdata), answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void API::http::request_stream(const string &path, string &stream)
|
void API::http::request_stream(const string &path, string &stream)
|
||||||
|
@ -64,7 +123,7 @@ void API::http::request_stream(const string &path, string &stream)
|
||||||
[&, path] // path is captured by value because it may be
|
[&, path] // path is captured by value because it may be
|
||||||
{ // deleted before we access it.
|
{ // deleted before we access it.
|
||||||
ret = request_common(http_method::GET_STREAM, path,
|
ret = request_common(http_method::GET_STREAM, path,
|
||||||
curlpp::Forms(), stream);
|
make_unique<HTMLForm>(), stream);
|
||||||
ttdebug << "Remaining content of the stream: " << stream << '\n';
|
ttdebug << "Remaining content of the stream: " << stream << '\n';
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
|
@ -78,149 +137,171 @@ void API::http::request_stream(const string &path, string &stream)
|
||||||
|
|
||||||
return_call API::http::request_common(const http_method &meth,
|
return_call API::http::request_common(const http_method &meth,
|
||||||
const string &path,
|
const string &path,
|
||||||
const curlpp::Forms &formdata,
|
unique_ptr<HTMLForm> formdata,
|
||||||
string &answer)
|
string &answer)
|
||||||
{
|
{
|
||||||
using namespace std::placeholders; // _1, _2, _3
|
|
||||||
|
|
||||||
ttdebug << "Path is: " << path << '\n';
|
ttdebug << "Path is: " << path << '\n';
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
curlpp::Easy request;
|
string method;
|
||||||
std::list<string> headers;
|
|
||||||
|
|
||||||
request.setOpt<curlopts::Url>("https://" + _instance + path);
|
|
||||||
ttdebug << "User-Agent: " << parent.get_useragent() << "\n";
|
|
||||||
request.setOpt<curlopts::UserAgent>(parent.get_useragent());
|
|
||||||
|
|
||||||
{
|
|
||||||
string proxy;
|
|
||||||
string userpw;
|
|
||||||
parent.get_proxy(proxy, userpw);
|
|
||||||
if (!proxy.empty())
|
|
||||||
{
|
|
||||||
request.setOpt<curlopts::Proxy>(proxy);
|
|
||||||
if (!userpw.empty())
|
|
||||||
{
|
|
||||||
request.setOpt<curlopts::ProxyUserPwd>(userpw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_access_token.empty())
|
|
||||||
{
|
|
||||||
headers.push_back("Authorization: Bearer " + _access_token);
|
|
||||||
}
|
|
||||||
if (meth != http_method::GET_STREAM)
|
|
||||||
{
|
|
||||||
headers.push_back("Connection: close");
|
|
||||||
// Get headers from server
|
|
||||||
request.setOpt<curlpp::options::Header>(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setOpt<curlopts::HttpHeader>(headers);
|
|
||||||
request.setOpt<curlopts::FollowLocation>(true);
|
|
||||||
request.setOpt<curlopts::WriteFunction>
|
|
||||||
(std::bind(&http::callback_write, this, _1, _2, _3, &answer));
|
|
||||||
request.setOpt<curlopts::ProgressFunction>
|
|
||||||
(std::bind(&http::callback_progress, this, _1, _2, _3, _4));
|
|
||||||
request.setOpt<curlopts::NoProgress>(0);
|
|
||||||
if (!formdata.empty())
|
|
||||||
{
|
|
||||||
request.setOpt<curlopts::HttpPost>(formdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: operator string on http_method?
|
||||||
switch (meth)
|
switch (meth)
|
||||||
{
|
{
|
||||||
case http_method::GET:
|
case http_method::GET:
|
||||||
case http_method::GET_STREAM:
|
case http_method::GET_STREAM:
|
||||||
break;
|
{
|
||||||
case http_method::PATCH:
|
method = HTTPRequest::HTTP_GET;
|
||||||
request.setOpt<curlopts::CustomRequest>("PATCH");
|
|
||||||
break;
|
|
||||||
case http_method::POST:
|
|
||||||
request.setOpt<curlopts::CustomRequest>("POST");
|
|
||||||
break;
|
|
||||||
case http_method::PUT:
|
|
||||||
request.setOpt<curlopts::CustomRequest>("PUT");
|
|
||||||
break;
|
|
||||||
case http_method::DELETE:
|
|
||||||
request.setOpt<curlopts::CustomRequest>("DELETE");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case http_method::PUT:
|
||||||
|
{
|
||||||
|
method = HTTPRequest::HTTP_PUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case http_method::POST:
|
||||||
|
{
|
||||||
|
method = HTTPRequest::HTTP_POST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case http_method::PATCH:
|
||||||
|
{
|
||||||
|
method = HTTPRequest::HTTP_PATCH;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case http_method::DELETE:
|
||||||
|
{
|
||||||
|
method = HTTPRequest::HTTP_DELETE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//request.setOpt<curlopts::Verbose>(true);
|
HTTPSClientSession session(_instance);
|
||||||
|
HTTPRequest request(method, path, HTTPMessage::HTTP_1_1);
|
||||||
|
request.set("User-Agent", parent.get_useragent());
|
||||||
|
|
||||||
|
if (!_access_token.empty())
|
||||||
|
{
|
||||||
|
request.set("Authorization", " Bearer " + _access_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formdata->empty())
|
||||||
|
{
|
||||||
|
// TODO: Test form submit.
|
||||||
|
// TODO: Change maptoformdata() and so on.
|
||||||
|
formdata->prepareSubmit(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPResponse response;
|
||||||
|
|
||||||
|
session.sendRequest(request);
|
||||||
|
istream &rs = session.receiveResponse(response);
|
||||||
|
|
||||||
|
const uint16_t http_code = response.getStatus();
|
||||||
|
ttdebug << "Response code: " << http_code << '\n';
|
||||||
|
|
||||||
answer.clear();
|
answer.clear();
|
||||||
request.perform();
|
StreamCopier::copyToString(rs, answer);
|
||||||
uint16_t http_code = curlpp::infos::ResponseCode::get(request);
|
|
||||||
ttdebug << "Response code: " << http_code << '\n';
|
|
||||||
// Work around "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK"
|
|
||||||
size_t pos = answer.find("\r\n\r\n", 25);
|
|
||||||
_headers = answer.substr(0, pos);
|
|
||||||
// Only return body
|
|
||||||
answer = answer.substr(pos + 4);
|
|
||||||
|
|
||||||
if (http_code == 200 || http_code == 302 || http_code == 307)
|
switch (http_code)
|
||||||
{ // OK or Found or Temporary Redirect
|
{
|
||||||
|
case HTTPResponse::HTTP_OK:
|
||||||
|
{
|
||||||
return { 0, "", http_code, answer };
|
return { 0, "", http_code, answer };
|
||||||
}
|
}
|
||||||
else if (http_code == 301 || http_code == 308)
|
// Not using the constants because some are too new for Debian stretch.
|
||||||
{ // Moved Permanently or Permanent Redirect
|
case 301: // HTTPResponse::HTTP_MOVED_PERMANENTLY
|
||||||
// return new URL
|
case 308: // HTTPResponse::HTTP_PERMANENT_REDIRECT
|
||||||
answer = curlpp::infos::EffectiveUrl::get(request);
|
case 302: // HTTPResponse::HTTP_FOUND
|
||||||
return { 78, "Remote address changed", http_code, answer };
|
case 303: // HTTPResponse::HTTP_SEE_OTHER
|
||||||
}
|
case 307: // HTTPResponse::HTTP_TEMPORARY_REDIRECT
|
||||||
else if (http_code == 0)
|
|
||||||
{
|
{
|
||||||
return { 255, "Unknown error", http_code, answer };
|
string location = response.get("Location");
|
||||||
|
|
||||||
|
// TODO: Test this.
|
||||||
|
if (location.substr(0, 4) == "http")
|
||||||
|
{ // Remove protocol and instance from path.
|
||||||
|
size_t pos1 = location.find("//") + 2;
|
||||||
|
size_t pos2 = location.find('/', pos1);
|
||||||
|
|
||||||
|
if (location.substr(pos1, pos2) != _instance)
|
||||||
|
{ // Return new location if the domain changed.
|
||||||
|
return { 78, "Remote address changed", http_code,
|
||||||
|
location };
|
||||||
|
}
|
||||||
|
|
||||||
|
location = location.substr(pos2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http_code == 301 || http_code == 308)
|
||||||
|
{ // Return new location for permanent redirects.
|
||||||
|
return { 78, "Remote address changed", http_code, location };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return request_common(meth, location, move(formdata), answer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
default:
|
||||||
{
|
{
|
||||||
return { 111, "Connection refused", http_code, answer };
|
return { 111, "Connection refused", http_code, answer };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (curlpp::RuntimeError &e)
|
catch (const Poco::Net::DNSException &e)
|
||||||
{
|
{
|
||||||
const string what = e.what();
|
|
||||||
// This error is thrown if http.cancel_stream() is used.
|
|
||||||
if ((what.compare(0, 16, "Callback aborted") == 0) ||
|
|
||||||
(what.compare(0, 19, "Failed writing body") == 0))
|
|
||||||
{
|
|
||||||
ttdebug << "Request was cancelled by user\n";
|
|
||||||
return { 0, "Request was cancelled by user", 0, "" };
|
|
||||||
}
|
|
||||||
else if (what.compare(what.size() - 20, 20, "Connection timed out") == 0)
|
|
||||||
{
|
|
||||||
ttdebug << what << "\n";
|
|
||||||
return { 110, "Connection timed out", 0, "" };
|
|
||||||
}
|
|
||||||
else if (what.compare(0, 23, "Could not resolve host:") == 0)
|
|
||||||
{
|
|
||||||
ttdebug << what << "\n";
|
|
||||||
return { 113, "Could not resolve host", 0, "" };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent.exceptions())
|
if (parent.exceptions())
|
||||||
{
|
{
|
||||||
std::rethrow_exception(std::current_exception());
|
e.rethrow();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ttdebug << "curlpp::RuntimeError: " << e.what() << std::endl;
|
|
||||||
return { 192, e.what(), 0, "" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ttdebug << e.displayText() << "\n";
|
||||||
|
return { 113, e.displayText(), 0, "" };
|
||||||
}
|
}
|
||||||
catch (curlpp::LogicError &e)
|
catch (const Poco::Net::ConnectionRefusedException &e)
|
||||||
|
{
|
||||||
|
if (parent.exceptions())
|
||||||
|
{
|
||||||
|
e.rethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
ttdebug << e.displayText() << "\n";
|
||||||
|
return { 111, e.displayText(), 0, "" };
|
||||||
|
}
|
||||||
|
catch (const Poco::Net::SSLException &e)
|
||||||
|
{
|
||||||
|
if (parent.exceptions())
|
||||||
|
{
|
||||||
|
e.rethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
ttdebug << e.displayText() << "\n";
|
||||||
|
return { 150, e.displayText(), 0, "" };
|
||||||
|
}
|
||||||
|
catch (const Poco::Net::NetException &e)
|
||||||
|
{
|
||||||
|
if (parent.exceptions())
|
||||||
|
{
|
||||||
|
e.rethrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
ttdebug << "Unknown network error: " << e.displayText() << std::endl;
|
||||||
|
return { 255, e.displayText(), 0, "" };
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
if (parent.exceptions())
|
if (parent.exceptions())
|
||||||
{
|
{
|
||||||
std::rethrow_exception(std::current_exception());
|
std::rethrow_exception(std::current_exception());
|
||||||
}
|
}
|
||||||
|
|
||||||
ttdebug << "curlpp::LogicError: " << e.what() << std::endl;
|
ttdebug << "Unknown error: " << e.what() << std::endl;
|
||||||
return { 193, e.what(), 0, "" };
|
return { 255, e.what(), 0, "" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,20 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <Poco/Net/FilePartSource.h>
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
#include "debug.hpp"
|
#include "debug.hpp"
|
||||||
#include "mastodon-cpp.hpp"
|
#include "mastodon-cpp.hpp"
|
||||||
|
|
||||||
using namespace Mastodon;
|
using namespace Mastodon;
|
||||||
|
using std::make_unique;
|
||||||
|
using Poco::Net::FilePartSource;
|
||||||
|
|
||||||
API::API(const string &instance, const string &access_token)
|
API::API(const string &instance, const string &access_token)
|
||||||
: _instance(instance)
|
: _instance(instance)
|
||||||
|
@ -111,9 +111,10 @@ const string API::maptostr(const parameters &map, const bool &firstparam)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const curlpp::Forms API::maptoformdata(const parameters &map)
|
unique_ptr<HTMLForm> API::maptoformdata(const parameters &map)
|
||||||
{
|
{
|
||||||
curlpp::Forms formdata;
|
unique_ptr<HTMLForm> formdata =
|
||||||
|
make_unique<HTMLForm>(HTMLForm::ENCODING_MULTIPART);
|
||||||
|
|
||||||
if (map.size() == 0)
|
if (map.size() == 0)
|
||||||
{
|
{
|
||||||
|
@ -122,51 +123,57 @@ const curlpp::Forms API::maptoformdata(const parameters &map)
|
||||||
|
|
||||||
for (const auto &it : map)
|
for (const auto &it : map)
|
||||||
{
|
{
|
||||||
|
string key = it.key;
|
||||||
|
|
||||||
|
// TODO: Test nested parameters.
|
||||||
|
if (const size_t pos = key.find('.') != string::npos)
|
||||||
|
{ // Nested parameters.
|
||||||
|
key.replace(pos, 1, "[");
|
||||||
|
key += ']';
|
||||||
|
}
|
||||||
|
|
||||||
if (it.values.size() == 1)
|
if (it.values.size() == 1)
|
||||||
{ // If the file is not base64-encoded, treat as filename.
|
{ // If the file is not base64-encoded, treat as filename.
|
||||||
if ((it.key == "avatar" ||
|
if ((key == "avatar" ||
|
||||||
it.key == "header" ||
|
key == "header" ||
|
||||||
it.key == "file") &&
|
key == "file") &&
|
||||||
it.values.front().substr(0, 5) != "data:")
|
it.values.front().substr(0, 5) != "data:")
|
||||||
{
|
{
|
||||||
ttdebug << it.key << ": Filename detected.\n";
|
ttdebug << key << ": Filename detected.\n";
|
||||||
std::ifstream testfile(it.values.front());
|
|
||||||
if (testfile.good())
|
try
|
||||||
{
|
{
|
||||||
testfile.close();
|
formdata->addPart(key,
|
||||||
formdata.push_back(
|
new FilePartSource(it.values.front()));
|
||||||
new curlpp::FormParts::File(it.key, it.values.front()));
|
|
||||||
}
|
}
|
||||||
else
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: File not found: " << it.values.front()
|
if (exceptions())
|
||||||
<< std::endl;
|
{
|
||||||
|
std::rethrow_exception(std::current_exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Proper error handling without exceptions.
|
||||||
|
std::cerr << "Error: Could not open file: "
|
||||||
|
<< it.values.front() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (key == "account_ids"
|
||||||
{
|
|
||||||
string key = it.key;
|
|
||||||
// Append [] to array keys.
|
|
||||||
if (key == "account_ids"
|
|
||||||
|| key == "exclude_types"
|
|| key == "exclude_types"
|
||||||
|| key == "media_ids"
|
|| key == "media_ids"
|
||||||
|| key == "context")
|
|| key == "context")
|
||||||
{
|
{
|
||||||
key += "[]";
|
key += "[]";
|
||||||
}
|
|
||||||
formdata.push_back(
|
|
||||||
new curlpp::FormParts::Content(key, it.values.front()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formdata->add(key, it.values.front());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::transform(it.values.begin(), it.values.end(),
|
for (const string &value : it.values)
|
||||||
std::back_inserter(formdata),
|
{
|
||||||
[&it](const string &s)
|
formdata->add(key + "[]", value);
|
||||||
{
|
}
|
||||||
return new curlpp::FormParts::Content
|
|
||||||
(it.key + "[]", s);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,15 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <curlpp/cURLpp.hpp>
|
#include <curlpp/cURLpp.hpp>
|
||||||
#include <curlpp/Easy.hpp>
|
#include <curlpp/Easy.hpp>
|
||||||
|
#include <Poco/Net/HTMLForm.h>
|
||||||
|
|
||||||
#include "return_types.hpp"
|
#include "return_types.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::uint8_t;
|
using std::uint8_t;
|
||||||
|
using std::unique_ptr;
|
||||||
|
using Poco::Net::HTMLForm;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @example example01_get_public_timeline.cpp
|
* @example example01_get_public_timeline.cpp
|
||||||
|
@ -45,6 +48,7 @@ using std::uint8_t;
|
||||||
*/
|
*/
|
||||||
namespace Mastodon
|
namespace Mastodon
|
||||||
{
|
{
|
||||||
|
// TODO: error enum, different error codes.
|
||||||
/*!
|
/*!
|
||||||
* @brief Interface to the Mastodon API.
|
* @brief Interface to the Mastodon API.
|
||||||
*
|
*
|
||||||
|
@ -60,8 +64,7 @@ namespace Mastodon
|
||||||
* | 110 | Connection timed out |
|
* | 110 | Connection timed out |
|
||||||
* | 111 | Connection refused (check http_error_code) |
|
* | 111 | Connection refused (check http_error_code) |
|
||||||
* | 113 | No route to host / Could not resolve host |
|
* | 113 | No route to host / Could not resolve host |
|
||||||
* | 192 | curlpp runtime error |
|
* | 150 | Encryption error |
|
||||||
* | 193 | curlpp logic error |
|
|
||||||
* | 255 | Unknown error |
|
* | 255 | Unknown error |
|
||||||
*
|
*
|
||||||
* @since before 0.11.0
|
* @since before 0.11.0
|
||||||
|
@ -102,7 +105,7 @@ namespace Mastodon
|
||||||
*/
|
*/
|
||||||
return_call request(const http_method &meth,
|
return_call request(const http_method &meth,
|
||||||
const string &path,
|
const string &path,
|
||||||
const curlpp::Forms &formdata);
|
unique_ptr<HTMLForm> formdata);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief HTTP Request for streams.
|
* @brief HTTP Request for streams.
|
||||||
|
@ -153,7 +156,7 @@ namespace Mastodon
|
||||||
|
|
||||||
return_call request_common(const http_method &meth,
|
return_call request_common(const http_method &meth,
|
||||||
const string &path,
|
const string &path,
|
||||||
const curlpp::Forms &formdata,
|
unique_ptr<HTMLForm> formdata,
|
||||||
string &answer);
|
string &answer);
|
||||||
size_t callback_write(char* data, size_t size, size_t nmemb,
|
size_t callback_write(char* data, size_t size, size_t nmemb,
|
||||||
string *oss);
|
string *oss);
|
||||||
|
@ -412,8 +415,7 @@ namespace Mastodon
|
||||||
/*!
|
/*!
|
||||||
* @brief Turn exceptions on or off. Defaults to off.
|
* @brief Turn exceptions on or off. Defaults to off.
|
||||||
*
|
*
|
||||||
* This applies to exceptions from curlpp. curlpp::RuntimeError
|
* Most exceptions will be thrown at you to handle if on.
|
||||||
* and curlpp::LogicError.
|
|
||||||
*
|
*
|
||||||
* @param value true for on, false for off
|
* @param value true for on, false for off
|
||||||
*
|
*
|
||||||
|
@ -514,7 +516,7 @@ namespace Mastodon
|
||||||
*/
|
*/
|
||||||
void get_stream(const Mastodon::API::v1 &call,
|
void get_stream(const Mastodon::API::v1 &call,
|
||||||
const parameters ¶meters,
|
const parameters ¶meters,
|
||||||
std::unique_ptr<Mastodon::API::http> &ptr,
|
unique_ptr<Mastodon::API::http> &ptr,
|
||||||
string &stream);
|
string &stream);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -527,7 +529,7 @@ namespace Mastodon
|
||||||
* @since 0.100.0
|
* @since 0.100.0
|
||||||
*/
|
*/
|
||||||
void get_stream(const Mastodon::API::v1 &call,
|
void get_stream(const Mastodon::API::v1 &call,
|
||||||
std::unique_ptr<Mastodon::API::http> &ptr,
|
unique_ptr<Mastodon::API::http> &ptr,
|
||||||
string &stream);
|
string &stream);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -540,7 +542,7 @@ namespace Mastodon
|
||||||
* @since 0.100.0
|
* @since 0.100.0
|
||||||
*/
|
*/
|
||||||
void get_stream(const string &call,
|
void get_stream(const string &call,
|
||||||
std::unique_ptr<Mastodon::API::http> &ptr,
|
unique_ptr<Mastodon::API::http> &ptr,
|
||||||
string &stream);
|
string &stream);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -665,9 +667,9 @@ namespace Mastodon
|
||||||
*
|
*
|
||||||
* @param map Map of parameters
|
* @param map Map of parameters
|
||||||
*
|
*
|
||||||
* @return Form data as curlpp::Forms
|
* @return Form data as Poco::Net::HTMLForm.
|
||||||
*/
|
*/
|
||||||
const curlpp::Forms maptoformdata(const parameters &map);
|
unique_ptr<HTMLForm> maptoformdata(const parameters &map);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Delete Mastodon::param from Mastodon::parameters.
|
* @brief Delete Mastodon::param from Mastodon::parameters.
|
||||||
|
|
Reference in New Issue