LeechCraft 0.6.70-14794-g33744ae6ce
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 {
74 return !Token_.isEmpty () &&
75 (!ValidFor_ || ReceivedAt_.secsTo (QDateTime::currentDateTime ()) < ValidFor_);
76 }
77
79 {
80 return !Token_.isEmpty () || !Cookies_->allCookies ().isEmpty ();
81 }
82
83 void VkAuthManager::UpdateScope (const QStringList& scope)
84 {
85 const auto& newUrl = URLFromClientID (ID_, scope);
86 if (URL_ == newUrl)
87 return;
88
89 URL_ = newUrl;
90 Token_.clear ();
91 ReceivedAt_ = QDateTime ();
92 ValidFor_ = 0;
93 }
94
96 {
97 if (!IsAuthenticated ())
98 {
99 if (!SilentMode_)
100 RequestAuthKey ();
101 else
102 {
103 for (const auto& queue : PrioManagedQueues_)
104 queue->clear ();
105 for (const auto& queue : ManagedQueues_)
106 queue->clear ();
107 }
108
109 return;
110 }
111
112 InvokeQueues (Token_);
113 emit gotAuthKey (Token_);
114 }
115
117 {
119 iface.reportStarted ();
120
121 if (SilentMode_ && !IsAuthenticated ())
122 ReportFutureResult (iface, AuthKeyError_t { SilentMode {} });
123 else
124 {
125 connect (this,
127 [this, iface] () mutable { ReportFutureResult (iface, Token_); });
128 GetAuthKey ();
129 }
130
131 return iface.future ();
132 }
133
135 {
136 if (!Queue_)
137 {
138 qWarning () << Q_FUNC_INFO
139 << "cannot manage request queue if queue manager wasn't set";
140 return {};
141 }
142
143 ManagedQueues_ << queue;
144
145 return Util::MakeScopeGuard ([this, queue] { ManagedQueues_.removeAll (queue); });
146 }
147
149 {
150 if (!Queue_)
151 {
152 qWarning () << Q_FUNC_INFO
153 << "cannot manage request queue if queue manager wasn't set";
154 return {};
155 }
156
157 PrioManagedQueues_ << queue;
158
159 return Util::MakeScopeGuard ([this, queue] { PrioManagedQueues_.removeAll (queue); });
160 }
161
163 {
164 SilentMode_ = silent;
165 }
166
167 void VkAuthManager::InvokeQueues (const QString& token)
168 {
169 ScheduleTrack (token);
170
171 for (auto queue : PrioManagedQueues_)
172 while (!queue->isEmpty ())
173 {
174 const auto& pair = queue->takeFirst ();
175 const auto& f = pair.first;
176 Queue_->Schedule ([f, token] { f (token); }, nullptr, pair.second);
177 }
178
179 for (auto queue : ManagedQueues_)
180 while (!queue->isEmpty ())
181 {
182 const auto& f = queue->takeFirst ();
183 Queue_->Schedule ([f, token] { f (token); });
184 }
185 }
186
187 void VkAuthManager::RequestURL (const QUrl& url)
188 {
189 qDebug () << Q_FUNC_INFO << url;
190 auto reply = AuthNAM_->get (QNetworkRequest (url));
191 connect (reply,
192 &QNetworkReply::finished,
193 this,
194 [this, reply] { HandleGotForm (reply); });
195 }
196
197 void VkAuthManager::RequestAuthKey ()
198 {
199 if (IsRequestScheduled_ && ScheduleTimer_->isActive ())
200 ScheduleTimer_->stop ();
201
202 if (IsRequesting_)
203 return;
204
205 RequestURL (URL_);
206 IsRequesting_ = true;
207 }
208
209 bool VkAuthManager::CheckReply (QUrl location)
210 {
211 if (location.path () != "/blank.html"_ql)
212 return CheckError (location);
213
214 location = QUrl::fromEncoded (location.toEncoded ().replace ('#', '?'));
215 const QUrlQuery query { location };
216 Token_ = query.queryItemValue (QStringLiteral ("access_token"));
217 ValidFor_ = query.queryItemValue (QStringLiteral ("expires_in")).toInt ();
218 ReceivedAt_ = QDateTime::currentDateTime ();
219 qDebug () << Q_FUNC_INFO << Token_ << ValidFor_;
220 IsRequesting_ = false;
221
222 InvokeQueues (Token_);
223 emit gotAuthKey (Token_);
224 emit justAuthenticated ();
225
226 return true;
227 }
228
229 bool VkAuthManager::CheckError (const QUrl& url)
230 {
231 if (url.path () != "/error"_ql)
232 return false;
233
234 const auto errNum = QUrlQuery { url }.queryItemValue (QStringLiteral ("err")).toInt ();
235
236 IsRequesting_ = false;
237
238 qWarning () << Q_FUNC_INFO
239 << "got error"
240 << errNum;
241 if (errNum == 2)
242 {
243 ClearAuthData ();
244
245 RequestAuthKey ();
246 return true;
247 }
248
249 const auto& e = Util::MakeNotification (QStringLiteral ("VK.com"),
250 tr ("VK.com authentication for %1 failed because of error %2. "
251 "Report upstream please.")
252 .arg (AccountHR_)
253 .arg (errNum),
255 Proxy_->GetEntityManager ()->HandleEntity (e);
256
257 return true;
258 }
259
260 void VkAuthManager::ScheduleTrack (const QString& key)
261 {
262 if (HasTracked_)
263 return;
264
265 if (!Proxy_->GetSettingsManager ()->property ("TrackVK").toBool ())
266 return;
267
268 HasTracked_ = true;
269
270 QUrl url { QStringLiteral ("https://api.vk.com/method/stats.trackVisitor") };
271 Util::UrlOperator { url }
272 (QStringLiteral ("access_token"), key);
273
274 auto reply = AuthNAM_->get (QNetworkRequest { url });
275 connect (reply,
276 &QNetworkReply::finished,
277 reply,
278 &QNetworkReply::deleteLater);
279 }
280
282 {
283 Cookies_->Load ({});
284 Token_.clear ();
285 ReceivedAt_ = QDateTime ();
286 ValidFor_ = 0;
287 }
288
289 namespace
290 {
291 template<typename F>
292 class CloseEventFilter : public QObject
293 {
294 const F Handler_;
295 public:
296 CloseEventFilter (const F& handler, QObject *handlee)
297 : QObject { handlee }
298 , Handler_ { handler }
299 {
300 handlee->installEventFilter (this);
301 }
302
303 bool eventFilter (QObject*, QEvent *event) override
304 {
305 if (event->type () == QEvent::Close)
306 Handler_ ();
307 return false;
308 }
309 };
310 }
311
313 {
314 const auto& browsers = Proxy_->GetPluginsManager ()->GetAllCastableTo<IWebBrowser*> ();
315 if (browsers.isEmpty ())
316 {
317 const auto& e = Util::MakeNotification (tr ("VK.com authentication"),
318 tr ("Could not authenticate %1 since authentication requires a browser plugin. "
319 "Consider installing one like Poshuku.")
320 .arg (AccountHR_),
322 Proxy_->GetEntityManager ()->HandleEntity (e);
323 emit authCanceled ();
324 return;
325 }
326
327 auto view = browsers.value (0)->GetWidget ();
328 auto viewWidget = view->GetQWidget ();
329 viewWidget->setWindowTitle (tr ("VK.com authentication for %1")
330 .arg (AccountHR_));
331 viewWidget->setWindowFlags (Qt::Window);
332 viewWidget->resize (800, 600);
333 viewWidget->show ();
334
335 view->Load (URL_);
336 connect (viewWidget,
337 SIGNAL (urlChanged (const QUrl&)),
338 this,
339 SLOT (handleUrlChanged (const QUrl&)));
340
341 new CloseEventFilter { [this] { emit authCanceled (); }, viewWidget };
342 }
343
344 void VkAuthManager::HandleGotForm (QNetworkReply *reply)
345 {
346 reply->deleteLater ();
347
348 if (reply->error () != QNetworkReply::NoError &&
349 reply->error () != QNetworkReply::AuthenticationRequiredError)
350 {
351 qWarning () << Q_FUNC_INFO
352 << reply->error ()
353 << reply->errorString ();
354
355 IsRequesting_ = false;
356
357 if (!IsRequestScheduled_)
358 {
359 IsRequestScheduled_ = true;
360 ScheduleTimer_->start (30000);
361 }
362
363 return;
364 }
365
366 const auto& location = reply->header (QNetworkRequest::LocationHeader).toUrl ();
367 if (location.isEmpty ())
368 {
369 Reauth ();
370 return;
371 }
372
373 if (CheckReply (location))
374 return;
375
376 RequestURL (location);
377 }
378
379 void VkAuthManager::handleUrlChanged (const QUrl& url)
380 {
381 if (!CheckReply (url))
382 return;
383
384 emit cookiesChanged (Cookies_->Save ());
385 sender ()->deleteLater ();
386 }
387}
Base class for plugins that provide a web browser.
Definition: iwebbrowser.h:83
A simple scheduling manager for a queue of functors.
Definition: queuemanager.h:44
void Schedule(std::function< void()> functor, QObject *dependent=nullptr, QueuePriority prio=QueuePriority::Normal)
Adds the given functor.
ScheduleGuard_t ManageQueue(RequestQueue_ptr)
QFuture< AuthKeyResult_t > GetAuthKeyFuture()
void UpdateScope(const QStringList &)
std::variant< SilentMode > AuthKeyError_t
Definition: vkauthmanager.h:87
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:49
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:136
Entity MakeNotification(const QString &header, const QString &text, Priority priority)
An utility function to make a Entity with notification.
Definition: util.cpp:95