1 /*
2  * Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12  * for more details.
13  */
14 
15 
16 #ifndef ACCOUNTINFO_H
17 #define ACCOUNTINFO_H
18 
19 #include <QByteArray>
20 #include <QElapsedTimer>
21 #include <QPointer>
22 #include "connectionvalidator.h"
23 #include "creds/abstractcredentials.h"
24 
25 #include <memory>
26 
27 class QSettings;
28 
29 namespace OCC {
30 
31 class AccountState;
32 class Account;
33 class AccountApp;
34 class RemoteWipe;
35 
36 using AccountStatePtr = QExplicitlySharedDataPointer<AccountState>;
37 using AccountAppList = QList<AccountApp *>;
38 
39 /**
40  * @brief Extra info about an ownCloud server account.
41  * @ingroup gui
42  */
43 class AccountState : public QObject, public QSharedData
44 {
45     Q_OBJECT
46     Q_PROPERTY(AccountPtr account MEMBER _account)
47 
48 public:
49     enum State {
50         /// Not even attempting to connect, most likely because the
51         /// user explicitly signed out or cancelled a credential dialog.
52         SignedOut,
53 
54         /// Account would like to be connected but hasn't heard back yet.
55         Disconnected,
56 
57         /// The account is successfully talking to the server.
58         Connected,
59 
60         /// There's a temporary problem with talking to the server,
61         /// don't bother the user too much and try again.
62         ServiceUnavailable,
63 
64         /// Similar to ServiceUnavailable, but we know the server is down
65         /// for maintenance
66         MaintenanceMode,
67 
68         /// Could not communicate with the server for some reason.
69         /// We assume this may resolve itself over time and will try
70         /// again automatically.
71         NetworkError,
72 
73         /// Server configuration error. (For example: unsupported version)
74         ConfigurationError,
75 
76         /// We are currently asking the user for credentials
77         AskingCredentials
78     };
79 
80     /// The actual current connectivity status.
81     using ConnectionStatus = ConnectionValidator::Status;
82 
83     /// Use the account as parent
84     explicit AccountState(AccountPtr account);
85     ~AccountState() override;
86 
87     /** Creates an account state from settings and an Account object.
88      *
89      * Use from AccountManager with a prepared QSettings object only.
90      */
91     static AccountState *loadFromSettings(AccountPtr account, QSettings &settings);
92 
93     /** Writes account state information to settings.
94      *
95      * It does not write the Account data.
96      */
97     void writeToSettings(QSettings &settings);
98 
99     AccountPtr account() const;
100 
101     ConnectionStatus connectionStatus() const;
102     QStringList connectionErrors() const;
103 
104     State state() const;
105     static QString stateString(State state);
106 
107     bool isSignedOut() const;
108 
109     AccountAppList appList() const;
110     AccountApp* findApp(const QString &appId) const;
111 
112     /** A user-triggered sign out which disconnects, stops syncs
113      * for the account and forgets the password. */
114     void signOutByUi();
115 
116     /** Tries to connect from scratch.
117      *
118      * Does nothing for signed out accounts.
119      * Connected accounts will be disconnected and try anew.
120      * Disconnected accounts will go to checkConnectivity().
121      *
122      * Useful for when network settings (proxy) change.
123      */
124     void freshConnectionAttempt();
125 
126     /// Move from SignedOut state to Disconnected (attempting to connect)
127     void signIn();
128 
129     bool isConnected() const;
130 
131     /** Returns a new settings object for this account, already in the right groups. */
132     std::unique_ptr<QSettings> settings();
133 
134     /** Mark the timestamp when the last successful ETag check happened for
135      *  this account.
136      *  The checkConnectivity() method uses the timestamp to save a call to
137      *  the server to validate the connection if the last successful etag job
138      *  was not so long ago.
139      */
140     void tagLastSuccessfullETagRequest(const QDateTime &tp);
141 
142     /** Saves the ETag Response header from the last Notifications api
143      * request with statusCode 200.
144     */
145     QByteArray notificationsEtagResponseHeader() const;
146 
147     /** Returns the ETag Response header from the last Notifications api
148      * request with statusCode 200.
149     */
150     void setNotificationsEtagResponseHeader(const QByteArray &value);
151 
152     /** Saves the ETag Response header from the last Navigation Apps api
153      * request with statusCode 200.
154     */
155     QByteArray navigationAppsEtagResponseHeader() const;
156 
157     /** Returns the ETag Response header from the last Navigation Apps api
158      * request with statusCode 200.
159     */
160     void setNavigationAppsEtagResponseHeader(const QByteArray &value);
161 
162     ///Asks for user credentials
163     void handleInvalidCredentials();
164 
165     /** Returns the notifications status retrieved by the notificatons endpoint
166      *  https://github.com/nextcloud/desktop/issues/2318#issuecomment-680698429
167     */
168     bool isDesktopNotificationsAllowed() const;
169 
170     /** Set desktop notifications status retrieved by the notificatons endpoint
171     */
172     void setDesktopNotificationsAllowed(bool isAllowed);
173 
174 public slots:
175     /// Triggers a ping to the server to update state and
176     /// connection status and errors.
177     void checkConnectivity();
178 
179 private:
180     void setState(State state);
181     void fetchNavigationApps();
182 
183 signals:
184     void stateChanged(State state);
185     void isConnectedChanged();
186     void hasFetchedNavigationApps();
187     void statusChanged();
188     void desktopNotificationsAllowedChanged();
189 
190 protected Q_SLOTS:
191     void slotConnectionValidatorResult(ConnectionValidator::Status status, const QStringList &errors);
192 
193     /// When client gets a 401 or 403 checks if server requested remote wipe
194     /// before asking for user credentials again
195     void slotHandleRemoteWipeCheck();
196 
197     void slotCredentialsFetched(AbstractCredentials *creds);
198     void slotCredentialsAsked(AbstractCredentials *creds);
199 
200     void slotNavigationAppsFetched(const QJsonDocument &reply, int statusCode);
201     void slotEtagResponseHeaderReceived(const QByteArray &value, int statusCode);
202     void slotOcsError(int statusCode, const QString &message);
203 
204 private:
205     AccountPtr _account;
206     State _state;
207     ConnectionStatus _connectionStatus;
208     QStringList _connectionErrors;
209     bool _waitingForNewCredentials;
210     QDateTime _timeOfLastETagCheck;
211     QPointer<ConnectionValidator> _connectionValidator;
212     QByteArray _notificationsEtagResponseHeader;
213     QByteArray _navigationAppsEtagResponseHeader;
214 
215     /**
216      * Starts counting when the server starts being back up after 503 or
217      * maintenance mode. The account will only become connected once this
218      * timer exceeds the _maintenanceToConnectedDelay value.
219      */
220     QElapsedTimer _timeSinceMaintenanceOver;
221 
222     /**
223      * Milliseconds for which to delay reconnection after 503/maintenance.
224      */
225     int _maintenanceToConnectedDelay;
226 
227     /**
228      * Connects remote wipe check with the account
229      * the log out triggers the check (loads app password -> create request)
230      */
231     RemoteWipe *_remoteWipe;
232 
233     /**
234      * Holds the App names and URLs available on the server
235      */
236     AccountAppList _apps;
237 
238     bool _isDesktopNotificationsAllowed;
239 };
240 
241 class AccountApp : public QObject
242 {
243     Q_OBJECT
244 public:
245     AccountApp(const QString &name, const QUrl &url,
246         const QString &id, const QUrl &iconUrl,
247         QObject* parent = nullptr);
248 
249     QString name() const;
250     QUrl url() const;
251     QString id() const;
252     QUrl iconUrl() const;
253 
254 private:
255     QString _name;
256     QUrl _url;
257 
258     QString _id;
259     QUrl _iconUrl;
260 };
261 
262 }
263 
264 Q_DECLARE_METATYPE(OCC::AccountState *)
265 Q_DECLARE_METATYPE(OCC::AccountStatePtr)
266 
267 #endif //ACCOUNTINFO_H
268