File indexing completed on 2024-05-12 05:04:08
0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu> 0002 // SPDX-License-Identifier: GPL-3.0-or-later 0003 0004 #pragma once 0005 0006 #include "accountconfig.h" 0007 #include "adminaccountinfo.h" 0008 #include "customemoji.h" 0009 #include "identity.h" 0010 #include "preferences.h" 0011 #include "reportinfo.h" 0012 0013 #include <QJsonDocument> 0014 #include <QJsonObject> 0015 0016 class Attachment; 0017 class Notification; 0018 class QNetworkReply; 0019 class QHttpMultiPart; 0020 class QFile; 0021 class Preferences; 0022 0023 /// Represents an account, which could possibly be real or a mock for testing. 0024 /// Also handles most of the API work, and account actions. 0025 class AbstractAccount : public QObject 0026 { 0027 Q_OBJECT 0028 0029 Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged) 0030 0031 Q_PROPERTY(QString instanceUri READ instanceUri CONSTANT) 0032 Q_PROPERTY(int maxPostLength READ maxPostLength NOTIFY fetchedInstanceMetadata) 0033 Q_PROPERTY(int maxPollOptions READ maxPollOptions NOTIFY fetchedInstanceMetadata) 0034 Q_PROPERTY(bool supportsLocalVisibility READ supportsLocalVisibility NOTIFY fetchedInstanceMetadata) 0035 Q_PROPERTY(QString instanceName READ instanceName NOTIFY fetchedInstanceMetadata) 0036 Q_PROPERTY(QUrl authorizeUrl READ getAuthorizeUrl NOTIFY registered) 0037 Q_PROPERTY(Identity *identity READ identity NOTIFY identityChanged) 0038 Q_PROPERTY(Preferences *preferences READ preferences CONSTANT) 0039 Q_PROPERTY(bool hasFollowRequests READ hasFollowRequests NOTIFY hasFollowRequestsChanged) 0040 Q_PROPERTY(AccountConfig *config READ config NOTIFY fetchedInstanceMetadata) 0041 Q_PROPERTY(bool registrationsOpen READ registrationsOpen NOTIFY fetchedInstanceMetadata) 0042 Q_PROPERTY(QString registrationMessage READ registrationMessage NOTIFY fetchedInstanceMetadata) 0043 0044 public: 0045 AbstractAccount(QObject *parent, const QString &instanceUri); 0046 AbstractAccount(QObject *parent); 0047 0048 /// Register the application on the server 0049 /// \param appName The name of the application displayed to other clients 0050 /// \param website The application's website 0051 /// \param additionalScopes Any additional scopes to request 0052 void registerApplication(const QString &appName, const QString &website, const QString &additionalScopes = {}); 0053 0054 /// Register a new account on the server 0055 /// \param username The account's username 0056 /// \param email The account's email address 0057 /// \param password The account's password 0058 /// \param agreement Whether the user agrees the server's rules and terms 0059 /// \param locale The user's locale 0060 /// \param reason If the server requires approval, a reason given to register 0061 Q_INVOKABLE void 0062 registerAccount(const QString &username, const QString &email, const QString &password, bool agreement, const QString &locale, const QString &reason); 0063 0064 /// Check if the application is registered 0065 /// \see registerApplication 0066 bool isRegistered() const; 0067 0068 /// Check if this account's instance has registrations open. 0069 bool registrationsOpen() const; 0070 0071 /// Check the reason why registrations are disabled, 0072 QString registrationMessage() const; 0073 0074 /// Get the oauth2 authorization url 0075 Q_INVOKABLE QUrl getAuthorizeUrl() const; 0076 0077 /// Sets the access token 0078 /// \param token The access token 0079 void setAccessToken(const QString &token); 0080 0081 /// Get the oauth2 token url 0082 QUrl getTokenUrl() const; 0083 0084 /// Set the oauth2 token 0085 /// \param authcode The oauth2 authentication code 0086 Q_INVOKABLE void setToken(const QString &authcode); 0087 0088 /// Check if the account has a token set 0089 /// \see setToken 0090 bool haveToken() const; 0091 0092 /// Check if the account has a username yet 0093 bool hasName() const; 0094 0095 /// Check if the account has an instance uri set 0096 bool hasInstanceUrl() const; 0097 0098 /// Verifies the token with the instance and if successful, loads identity information for the account 0099 Q_INVOKABLE virtual void validateToken(bool newAccount = false) = 0; 0100 0101 /// Return local account preferences 0102 AccountConfig *config(); 0103 0104 /// Returns the server-side preferences 0105 Preferences *preferences() const; 0106 0107 /// Return the username of the account 0108 /// \see setUsername 0109 QString username() const; 0110 0111 /// Sets the username for the account 0112 /// \param name The new username 0113 void setUsername(const QString &name); 0114 0115 /// Fetches instance-specific metadata like max post length, allowed content types, etc 0116 void fetchInstanceMetadata(); 0117 0118 /// Fetches instance-specific custom emojis 0119 void fetchCustomEmojis(); 0120 0121 /// Returns the custom emojis that's accessible for this account 0122 QList<CustomEmoji> customEmojis() const; 0123 0124 /// Returns the instance URI 0125 /// \see setInstanceUri 0126 QString instanceUri() const; 0127 0128 /// Sets the instance URI for the account 0129 /// \param instance_uri The new instance URI 0130 void setInstanceUri(const QString &instance_uri); 0131 0132 /// Returns the max allowable length of posts in characters 0133 size_t maxPostLength() const; 0134 0135 /// Returns the maximum number of poll options 0136 size_t maxPollOptions() const; 0137 0138 /// Certain servers (like Pleroma/Akkoma) support an additional visibility type, called Local 0139 bool supportsLocalVisibility() const; 0140 0141 /// Returns the amount of characters that URLs take 0142 /// Any URL that appears in a post will only be counted by this limit 0143 size_t charactersReservedPerUrl() const; 0144 0145 /// Returns the title set by the instance 0146 QString instanceName() const; 0147 0148 /// Returns the identity of the account 0149 Identity *identity(); 0150 0151 /// Looks up an identity specific to this account (like relationships) using an accountId 0152 /// and optionally a JSON document containing identity information. 0153 /// \param accountId The account ID 0154 /// \param doc doc Optionally provide an existing account JSON, if you were already given some in another request 0155 std::shared_ptr<Identity> identityLookup(const QString &accountId, const QJsonObject &doc); 0156 0157 /// Checks if the accountId exists in the account's identity cache 0158 /// \param accountId The account ID to look up 0159 bool identityCached(const QString &accountId) const; 0160 0161 /// Get identity of the admin::account 0162 /// \param accountId The account ID to look up 0163 /// \param doc doc Optionally provide an existing account JSON, if you were already given some in another request 0164 std::shared_ptr<AdminAccountInfo> adminIdentityLookup(const QString &accountId, const QJsonObject &doc); 0165 0166 /// Vanilla pointer identity 0167 AdminAccountInfo *adminIdentityLookupWithVanillaPointer(const QString &accountId, const QJsonObject &doc); 0168 0169 /// Populating with Admin::Report 0170 std::shared_ptr<ReportInfo> reportInfoLookup(const QString &reportId, const QJsonObject &doc); 0171 0172 /// Invalidates the account 0173 void invalidate(); 0174 0175 /// Favorite a post 0176 /// \param p The post object to mutate 0177 /// \see unfavorite 0178 void favorite(Post *p); 0179 0180 /// Unfavorite a post 0181 /// \param p The post object to mutate 0182 /// \see favorite 0183 void unfavorite(Post *p); 0184 0185 /// Boost (also known as reblog, or repeat) a post 0186 /// \param p The post object to mutate 0187 /// \see unrepeat 0188 void repeat(Post *p); 0189 0190 /// Unboost a post 0191 /// \param p The post object to mutate 0192 /// \see repeat 0193 void unrepeat(Post *p); 0194 0195 /// Bookmark a post 0196 /// \param p The post object to mutate 0197 /// \see unbookmark 0198 void bookmark(Post *p); 0199 0200 /// Unbookmark a post 0201 /// \param p The post object to mutate 0202 /// \see bookmark 0203 void unbookmark(Post *p); 0204 0205 /// Pin a post 0206 /// \param p The post object to mutate 0207 /// \see unpin 0208 void pin(Post *p); 0209 0210 /// Unpin a post 0211 /// \param p The post object to mutate 0212 /// \see pin 0213 void unpin(Post *p); 0214 0215 /// Returns a streaming url for \p stream 0216 /// \param stream The requested stream (e.g. user) 0217 QUrl streamingUrl(const QString &stream); 0218 0219 /// Invalidates a post 0220 /// \param p The post object to mutate 0221 void invalidatePost(Post *p); 0222 0223 /// Types of formatting that we may use is determined primarily by the server metadata, this is a simple enough 0224 /// way to determine what formats are accepted. 0225 enum AllowedContentType { PlainText = 1 << 0, Markdown = 1 << 1, Html = 1 << 2, BBCode = 1 << 3 }; 0226 0227 /// Return the allowed content types of the account's instance 0228 AllowedContentType allowedContentTypes() const 0229 { 0230 return m_allowedContentTypes; 0231 } 0232 0233 /// Return a well-formed URL of an API path 0234 /// \param path The base API path 0235 QUrl apiUrl(const QString &path) const; 0236 0237 /// Make an HTTP GET request to the server 0238 /// \param url The url of the request 0239 /// \param authenticated Whether the request should be authentificated 0240 /// \param parent The parent object that calls get() or the callback belongs to 0241 /// \param callback The callback that should be executed if the request is successful 0242 /// \param errorCallback The callback that should be executed if the request is not successful 0243 virtual void get(const QUrl &url, 0244 bool authenticated, 0245 QObject *parent, 0246 std::function<void(QNetworkReply *)> callback, 0247 std::function<void(QNetworkReply *)> errorCallback = nullptr) = 0; 0248 0249 /// Make an HTTP POST request to the server 0250 /// \param url The url of the request 0251 /// \param doc The request body as JSON 0252 /// \param parent The parent object that calls get() or the callback belongs to 0253 /// \param callback The callback that should be executed if the request is successful 0254 /// \param errorCallback The callback that should be executed if the request is not successful 0255 virtual void post(const QUrl &url, 0256 const QJsonDocument &doc, 0257 bool authenticated, 0258 QObject *parent, 0259 std::function<void(QNetworkReply *)> callback, 0260 std::function<void(QNetworkReply *)> errorCallback = nullptr, 0261 QHash<QByteArray, QByteArray> headers = {}) = 0; 0262 0263 /// Make an HTTP POST request to the server 0264 /// \param url The url of the request 0265 /// \param doc The request body as form-data 0266 /// \param authenticated Whether the request should be authenticated 0267 /// \param parent The parent object that calls get() or the callback belongs to 0268 /// \param callback The callback that should be executed if the request is successful 0269 /// \param errorCallback The callback that should be executed if the request is not successful 0270 virtual void post(const QUrl &url, 0271 const QUrlQuery &formdata, 0272 bool authenticated, 0273 QObject *parent, 0274 std::function<void(QNetworkReply *)> callback, 0275 std::function<void(QNetworkReply *)> errorCallback = nullptr) = 0; 0276 0277 /// Make an HTTP POST request to the server 0278 /// \param url The url of the request 0279 /// \param message The request body as multi-part data 0280 /// \param authenticated Whether the request should be authenticated 0281 /// \param parent The parent object that calls get() or the callback belongs to 0282 /// \param callback The callback that should be executed if the request is successful 0283 virtual QNetworkReply *post(const QUrl &url, QHttpMultiPart *message, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0284 0285 /// Make an HTTP PUT request to the server 0286 /// \param url The url of the request 0287 /// \param doc The request body as JSON 0288 /// \param authenticated Whether the request should be authenticated 0289 /// \param parent The parent object that calls get() or the callback belongs to 0290 /// \param callback The callback that should be executed if the request is successful 0291 virtual void put(const QUrl &url, const QJsonDocument &doc, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0292 0293 /// Make an HTTP PUT request to the server 0294 /// \param url The url of the request 0295 /// \param doc The request body as form-data 0296 /// \param authenticated Whether the request should be authenticated 0297 /// \param parent The parent object that calls get() or the callback belongs to 0298 /// \param callback The callback that should be executed if the request is successful 0299 virtual void put(const QUrl &url, const QUrlQuery &doc, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0300 0301 /// Make an HTTP PATCH request to the server 0302 /// \param url The url of the request 0303 /// \param message The request body as multi-part data 0304 /// \param authenticated Whether the request should be authenticated 0305 /// \param parent The parent object that calls get() or the callback belongs to 0306 /// \param callback The callback that should be executed if the request is successful 0307 virtual void patch(const QUrl &url, QHttpMultiPart *multiPart, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)>) = 0; 0308 0309 /// Make an HTTP DELETE request to the server 0310 /// \param url The url of the request 0311 /// \param authenticated Whether the request should be authenticated 0312 /// \param parent The parent object that calls get() or the callback belongs to 0313 /// \param callback The callback that should be executed if the request is successful 0314 virtual void deleteResource(const QUrl &url, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0315 0316 /// Upload a file 0317 /// \param url The name of the file to upload 0318 /// \param callback The callback that should be executed if the request is successful 0319 virtual QNetworkReply *upload(const QUrl &filename, std::function<void(QNetworkReply *)> callback) = 0; 0320 0321 /// Write account to settings to disk 0322 virtual void writeToSettings() = 0; 0323 0324 /// Read account from settings to disk 0325 virtual void buildFromSettings() = 0; 0326 0327 /// Check if the account has any follow requests 0328 virtual bool hasFollowRequests() const = 0; 0329 0330 /// Check against the server for any new follow requests 0331 virtual void checkForFollowRequests() = 0; 0332 0333 /// Update the push notification rules 0334 virtual void updatePushNotifications() = 0; 0335 0336 /// Follow the given account. Can also be used to update whether to show reblogs or enable notifications. 0337 /// @param identity The account to follow 0338 /// @param reblogs Receive this account's reblogs in home timeline? Defaults to true. 0339 /// @param notify Receive notifications when this account posts a status? Defaults to false. 0340 Q_INVOKABLE void followAccount(Identity *identity, bool reblogs = true, bool notify = false); 0341 0342 /// Unfollow the given account. 0343 /// @param identity The account to unfollow 0344 Q_INVOKABLE void unfollowAccount(Identity *identity); 0345 0346 /// Block the given account. 0347 /// @param identity The account to block 0348 Q_INVOKABLE void blockAccount(Identity *identity); 0349 0350 /// Unblock the given account. 0351 /// @param identity The account to unblock 0352 Q_INVOKABLE void unblockAccount(Identity *identity); 0353 0354 /// Mute the given account. 0355 /// @param identity The account to mute 0356 /// @param notifications Whether notifications should also be muted, by default true 0357 /// @param duration How long the mute should last, in seconds. Defaults to 0 (indefinite). 0358 Q_INVOKABLE void muteAccount(Identity *identity, bool notifications = true, int duration = 0); 0359 0360 /// Unmute the given account. 0361 /// @param identity The account to unmute 0362 Q_INVOKABLE void unmuteAccount(Identity *identity); 0363 0364 /// Add the given account to the user's featured profiles. 0365 /// @param identity The account to feature 0366 Q_INVOKABLE void featureAccount(Identity *identity); 0367 0368 /// Remove the given account from the user's featured profiles. 0369 /// @param identity The account to unfeature 0370 Q_INVOKABLE void unfeatureAccount(Identity *identity); 0371 0372 /// Sets a private note on a user. 0373 /// @param identity The account to annotate 0374 /// @param note The note to add to the account. Leave empty to remove the existing note. 0375 Q_INVOKABLE void addNote(Identity *identity, const QString ¬e); 0376 0377 Q_INVOKABLE void mutateRemotePost(const QString &url, const QString &verb); 0378 0379 /// Fetches OEmbed data for a post. 0380 /// @param QString url the url of the post to fetch. 0381 Q_INVOKABLE void fetchOEmbed(const QString &id, Identity *identity); 0382 0383 /// Returns the preferred settings group name for this Account which includes the username and the instance uri. 0384 QString settingsGroupName() const; 0385 0386 /// Returns the preferred key name for the client secret 0387 QString clientSecretKey() const; 0388 0389 /// Returns the preferred key name for the access token 0390 QString accessTokenKey() const; 0391 0392 /// Type of account action 0393 enum AccountAction { 0394 Follow, ///< Follow the account 0395 Unfollow, ///< Unfollow the account 0396 Block, ///< Block the account 0397 Unblock, ///< Unlock the account 0398 Mute, ///< Mute the account 0399 Unmute, ///< Unmute the account 0400 Feature, ///< Feature the account 0401 Unfeature, ///< Unfeature the account 0402 Note, ///< Update the note for the account 0403 }; 0404 0405 /// Type of streaming event 0406 enum StreamingEventType { 0407 UpdateEvent, ///< A new Status has appeared. 0408 DeleteEvent, ///< A status has been deleted. 0409 NotificationEvent, ///< A new notification has appeared. 0410 FiltersChangedEvent, ///< Keyword filters have been changed. 0411 ConversationEvent, ///< A direct conversation has been updated. 0412 AnnouncementEvent, ///< An announcement has been published. 0413 AnnouncementRedactedEvent, ///< An announcement has received an emoji reaction. 0414 AnnouncementDeletedEvent, ///< An announcement has been deleted. 0415 StatusUpdatedEvent, ///< A Status has been edited. 0416 EncryptedMessageChangedEvent, ///< An encrypted message has been received. 0417 }; 0418 0419 Q_SIGNALS: 0420 /// Emitted when the account is authenticated 0421 /// \param Whether the authentication was successful 0422 /// \param errorMessage If not successful, a localized error message 0423 /// \see validateToken 0424 void authenticated(bool successful, const QString &errorMessage); 0425 0426 /// Emitted when the application is successfully registered to the server 0427 /// \see registerApplication 0428 void registered(); 0429 0430 /// Emitted when the account's own identity has been updated 0431 void identityChanged(); 0432 0433 /// Emitted when the requested timeline has been fetched 0434 /// \param The name of the timeline that was fetched 0435 /// \param posts The list of posts fetched 0436 void fetchedTimeline(const QString &timelineName, QList<Post *> posts); 0437 0438 /// Emitted when th=e account has been invalidated 0439 /// \see invalidate 0440 void invalidated(); 0441 0442 /// Emitted when the account's username has been changed 0443 /// \see setUsername 0444 void usernameChanged(); 0445 0446 /// Emitted when the instance metadata has been fetched 0447 /// \see fetchInstanceMetadata 0448 void fetchedInstanceMetadata(); 0449 0450 /// Emitted when the custom emojis has been fetched 0451 /// \see fetchCustomEmojis 0452 void fetchedCustomEmojis(); 0453 0454 /// Emitted when a post has been invalidated 0455 /// \param p The post that was invalidated 0456 /// \see invalidatePost 0457 void invalidatedPost(Post *p); 0458 0459 /// Emitted when a notification has been received 0460 /// \param n A shared handle to the new notification 0461 void notification(std::shared_ptr<Notification> n); 0462 0463 /// Emitted when an error occurred when performing an API request 0464 /// \param errorMessage A localized error message 0465 void errorOccured(const QString &errorMessage); 0466 0467 /// Emitted when a streaming event has been received 0468 /// \param eventType The type of streaming event 0469 /// \param payload The payload for the streaming event 0470 void streamingEvent(AbstractAccount::StreamingEventType eventType, const QByteArray &payload); 0471 0472 /// Emitted when the account has follow requests 0473 void hasFollowRequestsChanged(); 0474 0475 /// Emitted when a registration error has occurred. 0476 /// \param json The JSON body for further processing 0477 void registrationError(const QString &json); 0478 0479 /// Emitted when the oembed data is successfully returned. 0480 /// \see fetchOEmbed 0481 void fetchedOEmbed(const QString &html); 0482 0483 protected: 0484 QString m_name; 0485 QString m_instance_uri; 0486 QString m_token; 0487 QString m_client_id; 0488 QString m_client_secret; 0489 size_t m_maxPostLength; 0490 size_t m_maxPollOptions; 0491 bool m_supportsLocalVisibility; 0492 size_t m_charactersReservedPerUrl; 0493 QString m_instance_name; 0494 QJsonArray m_instance_rules; 0495 std::shared_ptr<Identity> m_identity; 0496 std::shared_ptr<AdminAccountInfo> m_adminIdentity; 0497 AdminAccountInfo *m_adminIdentityWithVanillaPointer; 0498 AdminAccountInfo *m_federationIdentity; 0499 std::shared_ptr<ReportInfo> m_reportInfo; 0500 AllowedContentType m_allowedContentTypes; 0501 Preferences *m_preferences = nullptr; 0502 QList<CustomEmoji> m_customEmojis; 0503 QString m_additionalScopes; 0504 AccountConfig *m_config = nullptr; 0505 bool m_registrationsOpen = false; 0506 QString m_registrationMessage; 0507 bool m_hasFollowRequests = false; 0508 0509 // OAuth authorization 0510 QUrlQuery buildOAuthQuery() const; 0511 0512 // updates and notifications 0513 void handleUpdate(const QJsonDocument &doc, const QString &target); 0514 void handleNotification(const QJsonDocument &doc); 0515 0516 void mutatePost(const QString &id, const QString &verb, bool deliver_home = false); 0517 QMap<QString, std::shared_ptr<Identity>> m_identityCache; 0518 QMap<QString, std::shared_ptr<AdminAccountInfo>> m_adminIdentityCache; 0519 QMap<QString, AdminAccountInfo *> m_adminIdentityCacheWithVanillaPointer; 0520 QMap<QString, std::shared_ptr<ReportInfo>> m_reportInfoCache; 0521 0522 void executeAction(Identity *i, AccountAction accountAction, const QJsonObject &extraArguments = {}); 0523 };