LeechCraft 0.6.70-17609-g3dde4097dd
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
vkauthmanager.cpp
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
9#include "vkauthmanager.h"
10#include <QNetworkRequest>
11#include <QNetworkReply>
12#include <QNetworkCookie>
13#include <QtDebug>
14#include <QTimer>
15#include <QEvent>
16#include <QFuture>
17#include <QFutureInterface>
21#include <util/sll/either.h>
22#include <util/sll/qtutil.h>
23#include <util/xpc/util.h>
28#include <xmlsettingsdialog/basesettingsmanager.h>
29
31{
32 namespace
33 {
34 QUrl URLFromClientID (const QString& id, const QStringList& scope)
35 {
36 auto url = QUrl::fromEncoded ("https://oauth.vk.com/authorize?redirect_uri=http%3A%2F%2Foauth.vk.com%2Fblank.html&response_type=token&state=");
37 UrlOperator { url }
38 (QStringLiteral ("client_id"), id)
39 (QStringLiteral ("scope"), scope.join (','));
40 return url;
41 }
42 }
43
44 VkAuthManager::VkAuthManager (const QString& accName,
45 const QString& id, const QStringList& scope,
46 const QByteArray& cookies, const ICoreProxy_ptr& proxy,
47 QueueManager *queueMgr, QObject *parent)
48 : QObject (parent)
49 , Proxy_ (proxy)
50 , AccountHR_ (accName)
51 , AuthNAM_ (new QNetworkAccessManager (this))
52 , Cookies_ (new Util::CustomCookieJar (this))
53 , Queue_ (queueMgr)
54 , ID_ (id)
55 , URL_ (URLFromClientID (ID_, scope))
56 , ScheduleTimer_ (new QTimer (this))
57 {
58 AuthNAM_->setCookieJar (Cookies_);
59 Cookies_->Load (cookies);
60
61 ScheduleTimer_->setSingleShot (true);
62 connect (ScheduleTimer_,
63 &QTimer::timeout,
64 this,
65 [this]
66 {
67 IsRequestScheduled_ = false;
68 RequestAuthKey ();
69 });
70 }
71
73
75 {
76 return !Token_.isEmpty () &&
77 (!ValidFor_ || ReceivedAt_.secsTo (QDateTime::currentDateTime ()) < ValidFor_);
78 }
79
81 {
82 return !Token_.isEmpty () || !Cookies_->allCookies ().isEmpty ();
83 }
84
85 void VkAuthManager::UpdateScope (const QStringList& scope)
86 {
87 const auto& newUrl = URLFromClientID (ID_, scope);
88 if (URL_ == newUrl)
89 return;
90
91 URL_ = newUrl;
92 Token_.clear ();
93 ReceivedAt_ = QDateTime ();
94 ValidFor_ = 0;
95 }
96
98 {
99 if (!IsAuthenticated ())
100 {
101 if (!SilentMode_)
102 RequestAuthKey ();
103 else
104 {
105 for (const auto& queue : PrioManagedQueues_)
106 queue->clear ();
107 for (const auto& queue : ManagedQueues_)
108 queue->clear ();
109 }
110
111 return;
112 }
113
114 InvokeQueues (Token_);
115 emit gotAuthKey (Token_);
116 }
117
119 {
120 QFutureInterface<AuthKeyResult_t> iface;
121 iface.reportStarted ();
122
123 if (SilentMode_ && !IsAuthenticated ())
124 ReportFutureResult (iface, AuthKeyError_t { SilentMode {} });
125 else
126 {
127 connect (this,
129 [this, iface] () mutable { ReportFutureResult (iface, Token_); });
130 GetAuthKey ();
131 }
132
133 return iface.future ();
134 }
135
137 {
138 if (!Queue_)
139 {
140 qWarning () << Q_FUNC_INFO
141 << "cannot manage request queue if queue manager wasn't set";
142 return {};
143 }
144
145 ManagedQueues_ << queue;
146
147 return Util::MakeScopeGuard ([this, queue] { ManagedQueues_.removeAll (queue); });
148 }
149
151 {
152 if (!Queue_)
153 {
154 qWarning () << Q_FUNC_INFO
155 << "cannot manage request queue if queue manager wasn't set";
156 return {};
157 }
158
159 PrioManagedQueues_ << queue;
160
161 return Util::MakeScopeGuard ([this, queue] { PrioManagedQueues_.removeAll (queue); });
162 }
163
165 {
166 SilentMode_ = silent;
167 }
168
169 void VkAuthManager::InvokeQueues (const QString& token)
170 {
171 ScheduleTrack (token);
172
173 for (auto queue : PrioManagedQueues_)
174 while (!queue->isEmpty ())
175 {
176 const auto& pair = queue->takeFirst ();
177 const auto& f = pair.first;
178 Queue_->Schedule ([f, token] { f (token); }, nullptr, pair.second);
179 }
180
181 for (auto queue : ManagedQueues_)
182 while (!queue->isEmpty ())
183 {
184 const auto& f = queue->takeFirst ();
185 Queue_->Schedule ([f, token] { f (token); });
186 }
187 }
188
189 void VkAuthManager::RequestURL (const QUrl& url)
190 {
191 qDebug () << Q_FUNC_INFO << url;
192 auto reply = AuthNAM_->get (QNetworkRequest (url));
193 connect (reply,
194 &QNetworkReply::finished,
195 this,
196 [this, reply] { HandleGotForm (reply); });
197 }
198
199 void VkAuthManager::RequestAuthKey ()
200 {
201 if (IsRequestScheduled_ && ScheduleTimer_->isActive ())
202 ScheduleTimer_->stop ();
203
204 if (IsRequesting_)
205 return;
206
207 RequestURL (URL_);
208 IsRequesting_ = true;
209 }
210
211 bool VkAuthManager::CheckReply (QUrl location)
212 {
213 if (location.path () != "/blank.html"_ql)
214 return CheckError (location);
215
216 location = QUrl::fromEncoded (location.toEncoded ().replace ('#', '?'));
217 const QUrlQuery query { location };
218 Token_ = query.queryItemValue (QStringLiteral ("access_token"));
219 ValidFor_ = query.queryItemValue (QStringLiteral ("expires_in")).toInt ();
220 ReceivedAt_ = QDateTime::currentDateTime ();
221 qDebug () << Q_FUNC_INFO << Token_ << ValidFor_;
222 IsRequesting_ = false;
223
224 InvokeQueues (Token_);
225 emit gotAuthKey (Token_);
226 emit justAuthenticated ();
227
228 return true;
229 }
230
231 bool VkAuthManager::CheckError (const QUrl& url)
232 {
233 if (url.path () != "/error"_ql)
234 return false;
235
236 const auto errNum = QUrlQuery { url }.queryItemValue (QStringLiteral ("err")).toInt ();
237
238 IsRequesting_ = false;
239
240 qWarning () << Q_FUNC_INFO
241 << "got error"
242 << errNum;
243 if (errNum == 2)
244 {
245 ClearAuthData ();
246
247 RequestAuthKey ();
248 return true;
249 }
250
251 const auto& e = Util::MakeNotification (QStringLiteral ("VK.com"),
252 tr ("VK.com authentication for %1 failed because of error %2. "
253 "Report upstream please.")
254 .arg (AccountHR_)
255 .arg (errNum),
257 Proxy_->GetEntityManager ()->HandleEntity (e);
258
259 return true;
260 }
261
262 void VkAuthManager::ScheduleTrack (const QString& key)
263 {
264 if (HasTracked_)
265 return;
266
267 if (!Proxy_->GetSettingsManager ()->property ("TrackVK").toBool ())
268 return;
269
270 HasTracked_ = true;
271
272 QUrl url { QStringLiteral ("https://api.vk.com/method/stats.trackVisitor") };
273 Util::UrlOperator { url }
274 (QStringLiteral ("access_token"), key);
275
276 auto reply = AuthNAM_->get (QNetworkRequest { url });
277 connect (reply,
278 &QNetworkReply::finished,
279 reply,
280 &QNetworkReply::deleteLater);
281 }
282
284 {
285 Cookies_->Load ({});
286 Token_.clear ();
287 ReceivedAt_ = QDateTime ();
288 ValidFor_ = 0;
289 }
290
291 namespace
292 {
293 template<typename F>
294 class CloseEventFilter : public QObject
295 {
296 const F Handler_;
297 public:
298 CloseEventFilter (const F& handler, QObject *handlee)
299 : QObject { handlee }
300 , Handler_ { handler }
301 {
302 handlee->installEventFilter (this);
303 }
304
305 bool eventFilter (QObject*, QEvent *event) override
306 {
307 if (event->type () == QEvent::Close)
308 Handler_ ();
309 return false;
310 }
311 };
312 }
313
315 {
316 const auto& browsers = Proxy_->GetPluginsManager ()->GetAllCastableTo<IWebBrowser*> ();
317 if (browsers.isEmpty ())
318 {
319 const auto& e = Util::MakeNotification (tr ("VK.com authentication"),
320 tr ("Could not authenticate %1 since authentication requires a browser plugin. "
321 "Consider installing one like Poshuku.")
322 .arg (AccountHR_),
324 Proxy_->GetEntityManager ()->HandleEntity (e);
325 emit authCanceled ();
326 return;
327 }
328
329 if (!Browser_)
330 Browser_ = browsers.value (0)->CreateWidget ();
331 auto viewWidget = Browser_->GetQWidget ();
332 viewWidget->setWindowTitle (tr ("VK.com authentication for %1")
333 .arg (AccountHR_));
334 viewWidget->setWindowFlags (Qt::Window);
335 viewWidget->resize (800, 600);
336 viewWidget->show ();
337
338 Browser_->Load (URL_);
339 connect (viewWidget,
340 SIGNAL (urlChanged (const QUrl&)),
341 this,
342 SLOT (handleUrlChanged (const QUrl&)));
343
344 new CloseEventFilter { [this] { emit authCanceled (); }, viewWidget };
345 }
346
347 void VkAuthManager::HandleGotForm (QNetworkReply *reply)
348 {
349 reply->deleteLater ();
350
351 if (reply->error () != QNetworkReply::NoError &&
352 reply->error () != QNetworkReply::AuthenticationRequiredError)
353 {
354 qWarning () << Q_FUNC_INFO
355 << reply->error ()
356 << reply->errorString ();
357
358 IsRequesting_ = false;
359
360 if (!IsRequestScheduled_)
361 {
362 IsRequestScheduled_ = true;
363 ScheduleTimer_->start (30000);
364 }
365
366 return;
367 }
368
369 const auto& location = reply->header (QNetworkRequest::LocationHeader).toUrl ();
370 if (location.isEmpty ())
371 {
372 Reauth ();
373 return;
374 }
375
376 if (CheckReply (location))
377 return;
378
379 RequestURL (location);
380 }
381
382 void VkAuthManager::handleUrlChanged (const QUrl& url)
383 {
384 if (!CheckReply (url))
385 return;
386
387 emit cookiesChanged (Cookies_->Save ());
388 Browser_.reset ();
389 }
390}
Base class for plugins that provide a web browser.
Definition iwebbrowser.h:84
virtual std::unique_ptr< IWebWidget > CreateWidget() const =0
Returns the IWebWidget for use in another modules of LeechCraft.
virtual QWidget * GetQWidget()=0
Returns the IWebWidget as a QWidget.
A simple scheduling manager for a queue of functors.
void Schedule(std::function< void()> functor, QObject *dependent=nullptr, QueuePriority prio=QueuePriority::Normal)
Adds the given functor.
Util::DefaultScopeGuard ScheduleGuard_t
ScheduleGuard_t ManageQueue(RequestQueue_ptr)
PrioRequestQueue_t * PrioRequestQueue_ptr
QFuture< AuthKeyResult_t > GetAuthKeyFuture()
void UpdateScope(const QStringList &)
std::variant< SilentMode > AuthKeyError_t
void cookiesChanged(const QByteArray &)
void gotAuthKey(const QString &)
VkAuthManager(const QString &accountName, const QString &clientId, const QStringList &scope, const QByteArray &cookies, const ICoreProxy_ptr &, QueueManager *=nullptr, QObject *=nullptr)
Manipulates query part of an QUrl object.
Definition urloperator.h:45
std::shared_ptr< ICoreProxy > ICoreProxy_ptr
Definition icoreproxy.h:181
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition util.h:155
Entity MakeNotification(const QString &header, const QString &text, Priority priority)
An utility function to make a Entity with notification.
Definition util.cpp:95