The first GET requests are working.

This commit is contained in:
tastytea 2018-01-09 22:12:11 +01:00
parent cd9b3f28d5
commit be6fca69fe
Signed by: tastytea
GPG Key ID: 59346E0EA35C67E5
9 changed files with 397 additions and 74 deletions

View File

@ -1,9 +1,9 @@
cmake_minimum_required (VERSION 3.7)
project (mastodon-cpp
VERSION 0.0.0
VERSION 0.0.1
LANGUAGES CXX
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -rdynamic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
# Write SHA1 hash and date of current git commit to a header file. Also version.
@ -38,7 +38,7 @@ set_target_properties(mastodon-cpp PROPERTIES
SOVERSION ${mastodon-cpp_VERSION_MAJOR}
)
install(TARGETS mastodon-cpp DESTINATION lib)
#target_link_libraries(mastodon-cpp pthread boost_system boost_filesystem dl)
target_link_libraries(mastodon-cpp pthread boost_system ssl crypto)
install(FILES ${PROJECT_SOURCE_DIR}/src/lib/mastodon-cpp.hpp DESTINATION include)
# Example client
@ -59,19 +59,3 @@ if(NOT NOTESTS)
add_test(${bin} ${bin})
endforeach()
endif()
# Binary and source packages
# Some variables are not respected, it seems
# include(InstallRequiredSystemLibraries)
# set(CPACK_GENERATOR "TGZ")
# set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
# set(CPACK_PACKAGE_VERSION_MAJOR "${cdcl_VERSION_MAJOR}")
# set(CPACK_PACKAGE_VERSION_MINOR "${cdcl_VERSION_MINOR}")
# set(CPACK_PACKAGE_VERSION_PATCH "${cdcl_VERSION_PATCH}")
# set(CPACK_PACKAGE_DESCRIPTION_SUMMARY, "Edit checklists on CalDAV servers.")
# set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.md")
# set(CPACK_PACKAGE_CHECKSUM, "SHA256")
# set(CPACK_SOURCE_PACKAGE_FILE_NAME, "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
# set(CPACK_SOURCE_IGNORE_FILES, "/\\\\.git/;/build/;.\\\\.sublime-")
# include (CPack)

View File

@ -1,4 +1,6 @@
**mastodon-cpp** is an interface for the Mastodon API.
**mastodon-cpp** is a CPP wrapper for the Mastodon API. The aim is to be as simple as possible.
The library takes care of the network stuff. You submit a query and get the raw JSON.
All versions below 1.0.0 (SOVERSION 0) are considered insecure, unstable and can change drastically any time.
# Install
## Dependencies
@ -18,24 +20,39 @@
make
# Usage
Mastodon::API masto("social.example.com", "access token");
std::cout << masto.get(Mastodon::API::v1::accounts_id, "12345") << '\n';
# Status of implementation
There is an example in `src/example`.
* [ ] GET /api/v1/accounts/:id
* [ ] GET /api/v1/accounts/verify_credentials
# TODO
* Version 0.1.0
* [ ] Implement all GET methods
* [ ] Proper error handling
* [x] Network stuff
* Version 0.2.0
* [ ] Implement all PATCH methods
* [ ] Implement all POST methods
* [ ] Implement all DELETE methods
* Later
[ ] Escape user input
[ ] Asynchronous I/O
## Status of implementation
* [x] GET /api/v1/accounts/:id
* [x] GET /api/v1/accounts/verify_credentials
* [ ] PATCH /api/v1/accounts/update_credentials
* [ ] GET /api/v1/accounts/:id/followers
* [ ] GET /api/v1/accounts/:id/following
* [ ] GET /api/v1/accounts/:id/statuses
* [x] GET /api/v1/accounts/:id/followers
* [x] GET /api/v1/accounts/:id/following
* [x] GET /api/v1/accounts/:id/statuses
* [ ] POST /api/v1/accounts/:id/follow
* [ ] POST /api/v1/accounts/:id/unfollow
* [ ] GET /api/v1/accounts/:id/block
* [ ] GET /api/v1/accounts/:id/unblock
* [ ] GET /api/v1/accounts/:id/mute
* [ ] GET /api/v1/accounts/:id/unmute
* [ ] GET /api/v1/accounts/relationships
* [ ] GET /api/v1/accounts/search
* [ ] POST /api/v1/accounts/:id/block
* [ ] POST /api/v1/accounts/:id/unblock
* [ ] POST /api/v1/accounts/:id/mute
* [ ] POST /api/v1/accounts/:id/unmute
* [x] GET /api/v1/accounts/relationships
* [x] GET /api/v1/accounts/search
* [ ] POST /api/v1/apps
* [ ] GET /api/v1/blocks
* [ ] GET /api/v1/favourites
@ -69,7 +86,7 @@
* [ ] GET /api/v1/timelines/list/:list_id
# Copyright
Copyright © 2017 tastytea <tastytea@tastytea.de>.
Copyright © 2018 tastytea <tastytea@tastytea.de>.
License GPLv3: GNU GPL version 3 <https://www.gnu.org/licenses/gpl-3.0.html>.
This program comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.

95
src/api_get.cpp Normal file
View File

@ -0,0 +1,95 @@
/* This file is part of mastodon-cpp.
* Copyright © 2018 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <string>
#include <vector>
#include "mastodon-cpp.hpp"
using namespace Mastodon;
using std::string;
using std::cerr;
const string API::get(const Mastodon::API::v1 &method)
{
const std::vector<string> v{};
return get(method, v);
}
const string API::get(const Mastodon::API::v1 &method,
const std::vector<string> &parameters)
{
string strmethod = "";
switch (method)
{
case v1::accounts_verify_credentials:
strmethod = "/api/v1/accounts/verify_credentials";
break;
default:
cerr << "ERROR: Invalid method.\n";
break;
}
string answer;
_http.request_sync(http::method::GET, strmethod, answer);
return answer;
}
const string API::get(const Mastodon::API::v1 &method,
const string &argument)
{
const std::vector<string> v;
return get(method, argument, v);
}
const string API::get(const Mastodon::API::v1 &method,
const string &argument,
const std::vector<string> &parameters)
{
string strmethod = "";
switch (method)
{
case v1::accounts_id:
strmethod = "/api/v1/accounts/" + argument;
break;
case v1::accounts_id_followers:
strmethod = "/api/v1/accounts/" + argument + "/followers";
break;
case v1::accounts_id_following:
strmethod = "/api/v1/accounts/" + argument + "/following";
break;
case v1::accounts_id_statuses:
strmethod = "/api/v1/accounts/" + argument + "/statuses";
break;
case v1::accounts_relationships:
strmethod = "/api/v1/accounts/relationships?id=" + argument;
break;
case v1::accounts_search:
strmethod = "/api/v1/accounts/search?q=" + argument;
break;
default:
cerr << "ERROR: Invalid method.\n";
return "";
break;
}
string answer;
_http.request_sync(http::method::GET, strmethod, answer);
return answer;
}
const string API::get(const std::string &method)
{
return method;
}

View File

@ -1,22 +1,19 @@
/* This file is part of mastodon-cpp.
* Copyright © 2018 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include "../mastodon-cpp.hpp"
int main(int argc, char *argv[])
{
//
if (argc < 3)
{
std::cerr << "usage: " << argv[0] << " <instance> <access token>\n";
return 1;
}
Mastodon::API masto(argv[1], argv[2]);
std::cout << masto.get(Mastodon::API::v1::accounts_id,
"44897") << '\n';
}

157
src/http.cpp Normal file
View File

@ -0,0 +1,157 @@
/* This file is part of mastodon-cpp.
* Copyright © 2018 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include <cstdint>
#include <iostream>
#include <istream>
#include <ostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include "mastodon-cpp.hpp"
using namespace Mastodon;
using std::string;
using std::cerr;
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;
API::http::http(const string &instance, const string &access_token,
const string &useragent)
: _instance(instance)
, _access_token(access_token)
, _useragent(useragent)
, _ctx(ssl::context::tlsv12)
, _resolver(_io_service)
, _socket(_io_service, _ctx)
{
_ctx.set_options(ssl::context::tlsv12 | ssl::context::tlsv11 |
ssl::context::no_sslv3 | ssl::context::no_sslv2 |
ssl::context::no_tlsv1);
_ctx.set_default_verify_paths();
}
const std::uint16_t API::http::request_sync(const method &meth,
const string &path,
string &answer)
{
return request_sync(meth, path, "", answer);
}
const std::uint16_t API::http::request_sync(const method &meth,
const string &path,
const string &data,
string &answer)
{
try
{
tcp::resolver::query query(_instance, "https");
tcp::resolver::iterator endpoint_iterator = _resolver.resolve(query);
boost::asio::connect(_socket.lowest_layer(), endpoint_iterator);
_socket.lowest_layer().set_option(tcp::no_delay(true));
// Server Name Indication (SNI)
SSL_set_tlsext_host_name(_socket.native_handle(), _instance.c_str());
_socket.set_verify_mode(ssl::verify_peer);
_socket.set_verify_callback(ssl::rfc2818_verification(_instance));
_socket.handshake(ssl_socket::client);
boost::asio::streambuf request;
std::ostream request_stream(&request);
switch (meth)
{
case http::method::GET:
request_stream << "GET";
request_stream << " " << path;
break;
// case http::method::PATCH:
// request_stream << "PATCH";
// break;
// case http::method::POST:
// request_stream << "POST";
// break;
// case http::method::DELETE:
// request_stream << "DELETE";
// break;
default:
cerr << "NOT IMPLEMENTED\n";
return 0xffff;
}
request_stream << " HTTP/1.0\r\n";
request_stream << "Host: " << _instance << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n";
request_stream << "User-Agent: " << _useragent << "\r\n";
request_stream << "Authorization: Bearer "
<< _access_token << "\r\n\r\n";
boost::asio::write(_socket, request);
boost::asio::streambuf response;
boost::asio::read_until(_socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
std::uint16_t status_code;
std::string status_message;
response_stream >> http_version;
response_stream >> status_code;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
cerr << "Invalid response\n";
return 0xffff;
}
if (status_code != 200)
{
cerr << "Response returned with status code " << status_code
<< ": " << status_message << "\n";
return 0xffff;
}
// Read headers
boost::asio::read_until(_socket, response, "\r\n\r\n");
std::string header;
while (std::getline(response_stream, header) && header != "\r")
{}
//response.consume(response.size());
// Read body
boost::system::error_code error;
answer = "";
std::ostringstream ss;
while (boost::asio::read(_socket, response,
boost::asio::transfer_at_least(1), error))
{
ss << &response;
}
if (error != boost::asio::error::eof)
{
throw boost::system::system_error(error);
}
answer = ss.str();
}
catch (const std::exception &e)
{
cerr << "Exception: " << e.what() << "\n";
return 0xffff;
}
return 0;
}

View File

@ -14,12 +14,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <string>
#include "version.hpp"
#include "mastodon-cpp.hpp"
using std::cout;
using namespace Mastodon;
using std::string;
Mastodon::Mastodon()
API::API(const string &instance, const string &access_token)
: _instance(instance)
, _access_token(access_token)
, _useragent(string("mastodon-cpp/") + global::version)
, _http(instance, access_token, _useragent)
{
cout << "Test.\n";
//
}
const void API::set_useragent(const std::string &useragent)
{
_useragent = useragent;
}

View File

@ -17,10 +17,81 @@
#ifndef MASTODON_CPP_HPP
#define MASTODON_CPP_HPP
class Mastodon
#include <string>
#include <vector>
#include <cstdint>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
namespace Mastodon
{
public:
Mastodon();
};
class API
{
public:
enum class v1
{
accounts_id,
accounts_verify_credentials,
accounts_id_followers,
accounts_id_following,
accounts_id_statuses,
accounts_relationships,
accounts_search
};
explicit API(const std::string &instance,
const std::string &access_token);
// Select one of the predefined methods.
const std::string get(const Mastodon::API::v1 &method);
const std::string get(const Mastodon::API::v1 &method,
const std::vector<std::string> &parameters);
const std::string get(const Mastodon::API::v1 &method,
const std::string &argument,
const std::vector<std::string> &parameters);
const std::string get(const Mastodon::API::v1 &method,
const std::string &argument);
// Supply a custom method as string.
const std::string get(const std::string &method);
const void set_useragent(const std::string &useragent);
private:
const std::string _instance;
const std::string _access_token;
std::string _useragent;
class http
{
public:
enum class method
{
GET,
PATCH,
POST,
DELETE
};
explicit http(const std::string &instance,
const std::string &access_token,
const std::string &useragent);
const std::uint16_t request_sync(const method &meth,
const std::string &path,
std::string &answer);
const std::uint16_t request_sync(const method &meth,
const std::string &path,
const std::string &data,
std::string &answer);
private:
const std::string _instance;
const std::string _access_token;
const std::string _useragent;
boost::asio::ssl::context _ctx;
boost::asio::io_service _io_service;
boost::asio::ip::tcp::resolver _resolver;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> _socket;
} _http;
};
}
#endif

View File

@ -1,22 +1,9 @@
/* This file is part of mastodon-cpp.
* Copyright © 2018 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../mastodon-cpp.hpp"
int main(int argc, char *argv[])
{
Mastodon test;
Mastodon::API test("", "");
}

View File

@ -1,10 +1,14 @@
#ifndef VERSION_HPP
#define VERSION_HPP
namespace global {
static constexpr char version[] = "@PROJECT_VERSION@";
static constexpr char git_sha1[] ="@GIT_SHA1@";
static constexpr char git_date[] ="@GIT_DATE@";
namespace Mastodon
{
namespace global
{
static constexpr char version[] = "@PROJECT_VERSION@";
static constexpr char git_sha1[] ="@GIT_SHA1@";
static constexpr char git_date[] ="@GIT_DATE@";
}
}
#endif // VERSION_HPP