This repository has been archived on 2020-05-10. You can view files and clone it, but cannot push or open issues or pull requests.
mastodon-cpp/src/mastodon-cpp.hpp

763 lines
27 KiB
C++
Raw Normal View History

2018-01-06 21:33:52 +01:00
/* 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/>.
*/
#ifndef MASTODON_CPP_HPP
#define MASTODON_CPP_HPP
2018-01-09 22:12:11 +01:00
#include <string>
#include <vector>
#include <cstdint>
#include <map>
2018-02-26 07:57:30 +01:00
#include <memory>
2018-04-03 00:04:47 +02:00
#include <array>
2018-02-09 16:01:24 +01:00
#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
2018-01-09 22:12:11 +01:00
using std::uint_fast16_t;
using std::string;
2018-01-15 15:34:18 +01:00
/*!
* @example example01_dump_json.cpp
* @example example02_parse_account.cpp
* @example example03_mastocron.cpp
* @example example04_update_credentials.cpp
* @example example05_follow_unfollow.cpp
* @example example06_toot_delete-toot.cpp
* @example example07_register_app.cpp
* @example example08_rate_limiting.cpp
* @example example09_streaming_api.cpp
* @example example10_simplify.cpp
2018-03-13 13:25:01 +01:00
* @example example11_post_media.cpp
2018-04-01 02:20:10 +02:00
* @example example12_easy_laststatus.cpp
2018-04-01 04:27:29 +02:00
* @example example13_easy_stream.cpp
2018-04-04 04:17:22 +02:00
* @example example14_easy_treeview.cpp
2018-01-15 15:34:18 +01:00
*/
2018-04-01 04:27:29 +02:00
2018-01-09 22:12:11 +01:00
namespace Mastodon
2018-01-06 21:33:52 +01:00
{
2018-01-15 15:34:18 +01:00
/*!
2018-03-26 01:14:59 +02:00
* @brief Class for the Mastodon API.
*
* All input is expected to be UTF-8. Binary data must be
* base64-encoded or a filename.
* It appears that media attachements can not be base64 encoded.
2018-03-26 01:14:59 +02:00
*
2018-01-15 15:34:18 +01:00
* @section error Error codes
2018-02-28 22:37:30 +01:00
* mastodon-cpp will never use error codes below 11, except 0.
2018-04-10 10:44:46 +02:00
* | Code | Explanation |
* | --------: |:---------------------------------|
* | 0 | No error |
* | 11 | Invalid call |
* | 12 | Not implemented |
* | 13 | URL changed (HTTP 301 or 308) |
* | 14 | Aborted by user |
* | 15 | Network error (curlpp exception) |
* | 100 - 999 | HTTP status codes |
* | 65535 | Unknown error |
*
* @deprecated Errors 20-25 are no longer in use.
2018-01-15 15:34:18 +01:00
*/
class API
{
public:
2018-02-28 06:21:47 +01:00
/*!
* @brief http class. Do not use this directly.
*/
class http
{
public:
2018-04-04 04:38:04 +02:00
/*!
* @brief HTTP methods
*/
2018-02-28 06:21:47 +01:00
enum class method
{
GET,
PATCH,
POST,
PUT,
DELETE,
GET_STREAM
};
explicit http(const API &api, const string &instance,
const string &access_token);
2018-02-28 06:21:47 +01:00
~http();
const uint_fast16_t request(const method &meth,
const string &path,
string &answer);
2018-02-28 06:21:47 +01:00
/*!
* @brief HTTP Request.
2018-02-28 06:21:47 +01:00
*
* @param meth The method defined in http::method
* @param path The api call as string
* @param formdata The form data for PATCH and POST requests.
2018-02-28 06:21:47 +01:00
* @param answer The answer from the server
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed,
* 13 is returned and answer is set to the new URL.
2018-02-28 06:21:47 +01:00
*/
const uint_fast16_t request(const method &meth,
const string &path,
2018-03-13 14:37:44 +01:00
const curlpp::Forms &formdata,
string &answer);
2018-02-28 06:21:47 +01:00
2018-04-04 04:38:04 +02:00
/*!
* @brief Get all headers in a string
*/
const void get_headers(string &headers) const;
2018-02-28 06:21:47 +01:00
/*!
* @brief Aborts the stream. Use only with streams.
*
* Aborts the stream next time data comes in. Can take a few
* seconds.
* This works only with streams, because only streams have an
* own http object.
*/
const void abort_stream();
private:
const API &parent;
const string _instance;
const string _access_token;
string _headers;
2018-02-28 06:21:47 +01:00
bool _abort_stream;
2018-04-04 04:38:04 +02:00
const size_t callback(char* data, size_t size, size_t nmemb,
string *oss);
2018-02-28 06:21:47 +01:00
};
2018-02-26 07:57:30 +01:00
/*!
* @brief Used for passing parameters.
*
* Example:
* @code
* parametermap p =
* {
* {"field1", { "value1", "value2" } },
* {"field2", { "value" } }
* }
* @endcode
*/
typedef std::map<string, std::vector<string>> parametermap;
/*!
* @brief A list of all API calls.
*
2018-02-11 17:16:20 +01:00
* The original `/` are substituted by `_`.
*/
enum class v1
{
accounts_id,
accounts_verify_credentials,
accounts_id_followers,
accounts_id_following,
accounts_id_statuses,
accounts_relationships,
accounts_search,
blocks,
domain_blocks,
favourites,
follow_requests,
instance,
custom_emojis,
lists,
accounts_id_lists,
lists_id_accounts,
lists_id,
mutes,
notifications,
notifications_id,
reports,
search,
statuses_id,
statuses_id_context,
statuses_id_card,
statuses_id_reblogged_by,
statuses_id_favourited_by,
timelines_home,
timelines_public,
timelines_tag_hashtag,
timelines_list_list_id,
// PATCH
accounts_update_credentials,
// POST
accounts_id_follow,
accounts_id_unfollow,
accounts_id_block,
accounts_id_unblock,
accounts_id_mute,
accounts_id_unmute,
apps,
follow_requests_id_authorize,
follow_requests_id_reject,
follows,
media,
notifications_clear,
notifications_dismiss,
statuses,
statuses_id_reblog,
statuses_id_unreblog,
statuses_id_favourite,
statuses_id_unfavourite,
statuses_id_pin,
statuses_id_unpin,
statuses_id_mute,
2018-02-26 07:57:30 +01:00
statuses_id_unmute,
// PUT
media_id,
2018-02-26 07:57:30 +01:00
// Streaming
streaming_user,
streaming_public,
streaming_public_local,
streaming_hashtag,
streaming_list
};
/*!
* @brief Constructs a new API object.
*
* To register your application, leave access_token blank and call
* register_app1() and register_app2().
*
* @param instance The hostname of your instance
* @param access_token Your access token.
*/
explicit API(const string &instance, const string &access_token);
/*!
* @brief Sets the useragent. Default is mastodon-cpp/version.
*
* @param useragent The useragent
*/
const void set_useragent(const string &useragent);
/*!
* @brief Gets the useragent.
*
* @return The useragent.
*/
const string get_useragent() const;
2018-03-07 10:27:27 +01:00
/*!
* @brief Returns the instance.
*
* @return The instance.
*/
const string get_instance() const;
2018-03-07 10:27:27 +01:00
/*!
* @brief Percent-encodes a string. This is done automatically, unless you
* make a custom request.
2018-03-11 15:50:46 +01:00
*
* Calls curlpp::escape(str)
*
* The only time you should use this, is if you use
* get(const string &call, string &answer).
*
* See RFC 3986 section 2.1 for more info.
*
* @param str The string
*
* @return The percent-encoded string
*/
const string urlencode(const string &str) const;
2018-02-09 22:37:57 +01:00
/*!
* @brief Register application, step 1/2
*
* @param client_name The name of the application
* @param redirect_uri urn:ietf:wg:oauth:2.0:oob for none
* @param scopes Scopes (read, write, follow, space separated)
* @param website The website of the application
* @param client_id Returned
* @param client_secret Returned
* @param url Returned, used to generate code for register_app2
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and url is set to the new URL.
2018-02-09 22:37:57 +01:00
*/
const uint_fast16_t register_app1(const string &client_name,
const string &redirect_uri,
const string &scopes,
const string &website,
string &client_id,
string &client_secret,
string &url);
/*!
2018-03-01 03:10:05 +01:00
* @deprecated Will vanish in 1.0.0
*/
2018-03-26 19:17:28 +02:00
[[deprecated("Will vanish in 1.0.0")]]
const uint_fast16_t register_app1(const string &instance,
const string &client_name,
const string &redirect_uri,
const string &scopes,
const string &website,
string &client_id,
string &client_secret,
string &url);
2018-02-09 22:37:57 +01:00
/*!
* @brief Register application, step 2/2
*
* The access token will be used in all subsequent calls.
2018-02-09 22:37:57 +01:00
*
* @param client_id
* @param client_secret
* @param redirect_uri urn:ietf:wg:oauth:2.0:oob for none
* @param code The code generated by the website
* @param access_token Returned
*
* @return @ref error "Error code".
*/
const uint_fast16_t register_app2(const string &client_id,
const string &client_secret,
const string &redirect_uri,
const string &code,
string &access_token);
/*!
2018-03-01 03:10:05 +01:00
* @deprecated Will vanish in 1.0.0
*/
2018-03-26 19:17:28 +02:00
[[deprecated("Will vanish in 1.0.0")]]
const uint_fast16_t register_app2(const string &instance,
const string &client_id,
const string &client_secret,
const string &redirect_uri,
const string &code,
string &access_token);
2018-02-05 14:56:16 +01:00
2018-04-03 00:04:47 +02:00
/*!
* @brief Gets the header from the last answer.
*
* @param header The header to get
*
* @return The header, or "" on error.
*/
const string get_header(const string &header) const;
2018-04-10 10:17:30 +02:00
/*!
* @brief Turn exceptions on or off. Defaults to off.
*
* This applies to exceptions from curlpp. curlpp::RuntimeError and
* curlpp::LogicError.
*
* @param value true for on, false for off
*
* @return true if exceptions are turned on, false otherwise
*/
bool exceptions(const bool &value);
/*!
* @brief Returns true if exceptions are turned on, false otherwise
*/
const bool exceptions() const;
/*!
* @brief Make a GET request which doesn't require parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
*
* @return @ref error "Error code".
*/
const uint_fast16_t get(const Mastodon::API::v1 &call, string &answer);
/*!
* @brief Make a GET request which requires parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t get(const Mastodon::API::v1 &call,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a custom GET request.
*
* @param call String in the form `/api/v1/example`
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
*
* @return @ref error "Error code". If the URL has permanently changed, 13
* is returned and answer is set to the new URL.
*/
const uint_fast16_t get(const string &call, string &answer);
/*!
* @brief Make a GET request which requires a parameter as part of the
* call.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use get() without argument instead.")]]
const uint_fast16_t get(const Mastodon::API::v1 &call,
const string &argument,
string &answer);
/*!
* @brief Make a GET request which requires a parameter as part of the
* call and parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use get() without argument instead.")]]
const uint_fast16_t get(const Mastodon::API::v1 &call,
const string &argument,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a streaming GET request.
*
* @param call A call defined in Mastodon::API::v1
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Events with JSON-payload.
* @param ptr Pointer to the http object. Can be used to call
* ptr->abort_stream()
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t get_stream(const Mastodon::API::v1 &call,
const parametermap &parameters,
string &answer,
std::unique_ptr<Mastodon::API::http> &ptr);
2018-02-26 07:57:30 +01:00
/*!
* @brief Make a streaming GET request.
*
* @param call A call defined in Mastodon::API::v1
2018-03-05 09:10:32 +01:00
* @param answer The answer from the server. Events with JSON-payload.
2018-02-26 07:57:30 +01:00
* @param ptr Pointer to the http object. Can be used to call
* ptr->abort_stream()
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-26 07:57:30 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t get_stream(const Mastodon::API::v1 &call,
string &answer,
2018-02-26 07:57:30 +01:00
std::unique_ptr<Mastodon::API::http> &ptr);
/*!
* @brief Make a streaming GET request.
*
* @param call String in the form `/api/v1/example`
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
2018-02-26 07:57:30 +01:00
* @param ptr Pointer to the http object. Can be used to call
* ptr->abort_stream()
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-26 07:57:30 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t get_stream(const string &call,
string &answer,
2018-02-26 07:57:30 +01:00
std::unique_ptr<Mastodon::API::http> &ptr);
/*!
* @brief Make a streaming GET request.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param answer The answer from the server. Events with JSON-payload.
2018-02-26 07:57:30 +01:00
* @param ptr Pointer to the http object. Can be used to call
* ptr->abort_stream()
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-26 07:57:30 +01:00
* is returned and answer is set to the new URL.
*
* @deprecated Vill vanish in 1.0.0
2018-02-26 07:57:30 +01:00
*/
[[deprecated("Will vanish in 1.0.0, use get_stream() without argument instead.")]]
const uint_fast16_t get_stream(const Mastodon::API::v1 &call,
const string &argument,
string &answer,
2018-02-26 07:57:30 +01:00
std::unique_ptr<Mastodon::API::http> &ptr);
/*!
* @brief Make a PATCH request.
*
2018-02-09 20:14:50 +01:00
* Binary data must be base64-encoded or a filename.
*
* @param call A call defined in Mastodon::API::v1
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t patch(const Mastodon::API::v1 &call,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a POST request which doesn't require parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t post(const Mastodon::API::v1 &call, string &answer);
2018-01-17 23:51:59 +01:00
/*!
* @brief Make a POST request which requires parameters.
2018-01-17 23:51:59 +01:00
*
* Binary data must be base64-encoded or a filename.
*
* @param call A call defined in Mastodon::API::v1
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
2018-01-17 23:51:59 +01:00
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t post(const Mastodon::API::v1 &call,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a custom POST request.
2018-01-17 23:51:59 +01:00
*
2018-02-09 20:14:50 +01:00
* Binary data must be base64-encoded or a filename.
*
* @param call String in the form `/api/v1/example`
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
2018-01-17 23:51:59 +01:00
*/
const uint_fast16_t post(const string &call,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a POST request which requires a parameter as part of the
* call.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param answer The answer from the server. Usually JSON. On error an
* empty string.
*
* @return @ref error "Error code". If the URL has permanently changed, 13
* is returned and answer is set to the new URL.
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use post() without argument instead.")]]
const uint_fast16_t post(const Mastodon::API::v1 &call,
const string &argument,
string &answer);
/*!
* @brief Make a POST request which requires a parameter as part of the
* call and parameters.
*
2018-02-09 20:14:50 +01:00
* Binary data must be base64-encoded or a filename.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use post() without argument instead.")]]
const uint_fast16_t post(const Mastodon::API::v1 &call,
const string &argument,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a PUT request which requires a parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param parameters A Mastodon::API::parametermap containing
* parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
* @return @ref error "Error code". If the URL has permanently changed, 13
* is returned and answer is set to the new URL.
*/
const uint_fast16_t put(const Mastodon::API::v1 &call,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a custom PUT request.
*
* @param call String in the form `/api/v1/example`
* @param parameters A Mastodon::API::parametermap containing
* parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t put(const string &call,
const parametermap &parameters,
string &answer);
2018-01-17 23:51:59 +01:00
/*!
* @brief Make a PUT request which requires a parameter as part of the
* call and parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param parameters A Mastodon::API::parametermap containing
* parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use put() without argument instead.")]]
const uint_fast16_t put(const Mastodon::API::v1 &call,
const string &argument,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a DELETE request which requires parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param parameters A Mastodon::API::parametermap containing parameters
*
* @return @ref error "Error code".
*/
const uint_fast16_t del(const Mastodon::API::v1 &call,
const parametermap &parameters);
/*!
* @brief Make a custom DELETE request.
*
* @param call String in the form `/api/v1/example`
* @param parameters A Mastodon::API::parametermap containing parameters
* @param answer The answer from the server. Usually JSON. On error
* an empty string.
*
2018-02-28 22:37:30 +01:00
* @return @ref error "Error code". If the URL has permanently changed, 13
2018-02-17 20:01:51 +01:00
* is returned and answer is set to the new URL.
*/
const uint_fast16_t del(const string &call,
const parametermap &parameters,
string &answer);
/*!
* @brief Make a DELETE request which requires a parameter as part of the
* call.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
*
* @return @ref error "Error code".
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use del() without argument instead.")]]
const uint_fast16_t del(const Mastodon::API::v1 &call,
const string &argument);
/*!
* @brief Make a DELETE request which requires a parameter as part of the
* call and parameters.
*
* @param call A call defined in Mastodon::API::v1
* @param argument The parameter that is part of the call
* @param parameters A Mastodon::API::parametermap containing parameters
*
* @return @ref error "Error code".
*
* @deprecated Will vanish in 1.0.0
*/
[[deprecated("Will vanish in 1.0.0, use del() without argument instead.")]]
const uint_fast16_t del(const Mastodon::API::v1 &call,
const string &argument,
const parametermap &parameters);
private:
const string _instance;
string _access_token;
string _useragent;
2018-02-28 06:21:47 +01:00
http _http;
2018-04-10 10:17:30 +02:00
bool _exceptions;
/*!
* @brief Converts map of parameters into a string.
*
* @param map Map of parameters
* @param firstparam Contains this map the first parameter?
*
* @return String of parameters
*/
const string maptostr(const parametermap &map,
const bool &firstparam = true);
2018-02-09 16:17:32 +01:00
/*!
* @brief Converts map of parameters into form data
*
* @param map Map of parameters
*
* @return Form data as curlpp::Forms
*/
2018-02-09 16:01:24 +01:00
const curlpp::Forms maptoformdata(const parametermap &map);
};
2018-01-09 22:12:11 +01:00
}
2018-01-06 21:33:52 +01:00
#endif