summaryrefslogtreecommitdiffstats
path: root/include/instance.hpp
blob: 225f10684e49501e439513f1e676d158ab758f31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*  This file is part of mastodonpp.
 *  Copyright © 2020 tastytea <tastytea@tastytea.de>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MASTODONPP_INSTANCE_HPP
#define MASTODONPP_INSTANCE_HPP

#include "curl_wrapper.hpp"
#include "types.hpp"

#include <cstdint>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

namespace mastodonpp
{

using std::uint64_t;
using std::string;
using std::string_view;
using std::move;
using std::vector;

/*!
 *  @brief  Holds the access data of an instance.
 *
 *  Instance%s are needed to initialize Connection%s. All properties you set
 *  here (with set_proxy(), set_useragent() and so on) are copied to every
 *  Connection you initialize afterwards.
 *
 *  @since  0.1.0
 *
 *  @headerfile instance.hpp mastodonpp/instance.hpp
 */
class Instance : public CURLWrapper
{
public:
    /*!
     *  @brief  Construct a new Instance object.
     *
     *  @param  hostname     The hostname of the instance.
     *  @param  access_token Your access token.
     *
     *  @since  0.1.0
     */
    explicit Instance(const string_view hostname,
                      const string_view access_token)
        : _hostname{hostname}
        , _baseuri{"https://" + _hostname}
        , _access_token{access_token}
        , _max_chars{0}
    {}

    /*!
     *  @brief  Set the properties of the connection of the calling class up.
     *
     *  Meant for internal use. This aligns the properties of the connection of
     *  the calling class with the properties of connection of this class.
     *
     *  @param  curlwrapper The CURLWrapper parent of the calling class.
     *
     *  @since  0.3.0
     */
    inline void copy_connection_properties(CURLWrapper &curlwrapper)
    {
        curlwrapper.setup_connection_properties(_proxy, _access_token, _cainfo,
                                                _useragent);
    }

    /*!
     *  @brief  Returns the hostname.
     *
     *  @since  0.1.0
     */
    [[nodiscard]]
    inline string_view get_hostname() const noexcept
    {
        return _hostname;
    }

    /*!
     *  @brief  Returns the base URI.
     *
     *  The base URI is “https://” + the hostname.
     *
     *  @since  0.1.0
     */
    [[nodiscard]]
    inline string_view get_baseuri() const noexcept
    {
        return _baseuri;
    }

    /*!
     *  @brief  Returns the access token.
     *
     *  @since  0.1.0
     */
    [[nodiscard]]
    inline string_view get_access_token() const noexcept
    {
        return _access_token;
    }

    /*!
     *  @brief  Set OAuth 2.0 Bearer Access Token.
     *
     *  Sets also the access token for all Connection%s that are initialized
     *  with this Instance afterwards.
     *
     *  @since  0.1.0
     */
    inline void set_access_token(string access_token)
    {
        _access_token = move(access_token);
    }

    /*!
     *  @brief  Returns the maximum number of characters per post.
     *
     *  Queries `/api/v1/instance` for `max_toot_chars'. If the instance doesn't
     *  support it, the limit is assumed to be 500.
     *
     *  After the first call, the value is saved internally. Subsequent calls
     *  return the saved value.
     *
     *  @since  0.1.0
     */
    [[nodiscard]]
    uint64_t get_max_chars() noexcept;

    /*! @copydoc CURLWrapper::set_proxy(string_view)
     *
     *  Sets also the proxy for all Connection%s that are initialized with this
     *  Instance afterwards.
     */
    void set_proxy(const string_view proxy) override
    {
        _proxy = proxy;
        CURLWrapper::set_proxy(proxy);
    }

    /*!
     *  @brief  Returns the NodeInfo of the instance.
     *
     *  Attempts to download the [NodeInfo]
     *  (https://nodeinfo.diaspora.software/protocol.html) of the instance and
     *  returns it. Not every instance has it.
     *
     *  @since  0.3.0
     */
    [[nodiscard]]
    answer_type get_nodeinfo();

    /*!
     *  @brief  Returns the allowed mime types for statuses.
     *
     *  Extracts `metadata.postFormats` from NodeInfo. If none can be found,
     *  returns `{"text/plain"}`.
     *
     *  After the first call, the value is saved internally. Subsequent calls
     *  return the saved value.
     *
     *  @since  0.3.0
     */
    vector<string> get_post_formats() noexcept;

    /*!
     *  @brief  Set path to Certificate Authority (CA) bundle.
     *
     *  Sets also the CA info for all Connection%s that are initialized with
     *  this Instance afterwards.
     *
     *  @since  0.3.0
     */
    void set_cainfo(string_view path) override
    {
        _cainfo = path;
        CURLWrapper::set_cainfo(path);
    }

    /*!
     *  @brief  Sets the User-Agent.
     *
     *  Sets also the User-Agent for all Connection%s that are initialized with
     *  this Instance afterwards.
     *
     *  @since  0.3.0
     */
    void set_useragent(const string_view useragent) override
    {
        _useragent = useragent;
        CURLWrapper::set_useragent(useragent);
    }

    /*!
     *  @brief  Simplifies obtaining an OAuth 2.0 Bearer Access Token.
     *
     *  * Create an Instance and initialize this class with it.
     *  * Call step_1() to get the URI your user has to visit.
     *  * Get the authorization code from your user.
     *  * Call step_2() with the code.
     *
     *  Example:
     *  @code
     *  mastodonpp::Instance instance{"example.com", {}};
     *  mastodonpp::Instance::ObtainToken token{instance};
     *  auto answer{token.step1("Good program", "read:blocks read:mutes", "")};
     *  if (answer)
     *  {
     *      std::cout << "Please visit " << answer << "\nand paste the code: ";
     *      std::string code;
     *      std::cin >> code;
     *      answer = access_token{token.step2(code)};
     *      if (answer)
     *      {
     *          std::cout << "Success!\n";
     *      }
     *  }
     *  @endcode
     *
     *  @since  0.3.0
     *
     *  @headerfile instance.hpp mastodonpp/instance.hpp
     */
    class ObtainToken : public CURLWrapper
    {
    public:
        /*!
         *  @brief  Constructor.
         *
         *  @since  0.3.0
         */
        explicit ObtainToken(Instance &instance)
            : _instance{instance}
            , _baseuri{instance.get_baseuri()}
        {
            _instance.copy_connection_properties(*this);
        }

        /*!
         *  @brief  Creates an application via `/api/v1/apps`.
         *
         *  The `body` of the returned @link answer_type answer @endlink
         *  contains only the URI, not the whole JSON response.
         *
         *  @param  client_name The name of your application.
         *  @param  scopes      Space separated list of scopes. Defaults to
         *                      “read” if empty.
         *  @param  website     The URI to the homepage of your application. Can
         *                      be an empty string.
         *
         *  @return The URI your user has to visit.
         *
         *  @since  0.3.0
         */
        [[nodiscard]]
        answer_type step_1(string_view client_name, string_view scopes,
                           string_view website);

        /*!
         *  @brief  Creates a token via `/oauth/token`.
         *
         *  The `body` of the returned @link answer_type answer @endlink
         *  contains only the access token, not the whole JSON response.
         *
         *  The access token will be set in the Instance you initialized
         *  this ObtainToken with.
         *
         *  @param  code The authorization code you got from the user.
         *
         *  @return The access token.
         *
         *  @since  0.3.0
         */
        [[nodiscard]]
        answer_type step_2(string_view code);

    private:
        Instance &_instance;
        const string _baseuri;
        string _scopes;
        string _client_id;
        string _client_secret;
    };

private:
    const string _hostname;
    const string _baseuri;
    string _access_token;
    uint64_t _max_chars;
    string _proxy;
    vector<string> _post_formats;
    string _cainfo;
    string _useragent;
};

} // namespace mastodonpp

#endif  // MASTODONPP_INSTANCE_HPP