LeechCraft  0.6.70-15082-g543737046d
Modular cross-platform feature rich live environment.
customcookiejar.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 "customcookiejar.h"
10 #include <set>
11 #include <algorithm>
12 #include <QNetworkCookie>
13 #include <QtDebug>
14 #include <QDateTime>
15 #include <QtConcurrentRun>
16 #include <util/sll/util.h>
17 #include <util/threads/futures.h>
18 
19 namespace LC::Util
20 {
21  CustomCookieJar::CustomCookieJar (QObject *parent)
22  : QNetworkCookieJar (parent)
23  {
24  }
25 
26  void CustomCookieJar::SetFilterTrackingCookies (bool filter)
27  {
28  FilterTrackingCookies_ = filter;
29  }
30 
31  void CustomCookieJar::SetEnabled (bool enabled)
32  {
33  Enabled_ = enabled;
34  }
35 
36  void CustomCookieJar::SetExactDomainMatch (bool enabled)
37  {
38  MatchDomainExactly_ = enabled;
39  }
40 
41  void CustomCookieJar::SetWhitelist (const QList<QRegExp>& list)
42  {
43  WL_ = list;
44  }
45 
46  void CustomCookieJar::SetBlacklist (const QList<QRegExp>& list)
47  {
48  BL_ = list;
49  }
50 
51  QByteArray CustomCookieJar::Save () const
52  {
53  auto cookies = allCookies ();
54  QByteArray result;
55  for (const auto& cookie : cookies)
56  {
57  result += cookie.toRawForm ();
58  result += "\n";
59  }
60  return result;
61  }
62 
63  namespace
64  {
65  bool IsExpired (const QNetworkCookie& cookie, const QDateTime& now)
66  {
67  return !cookie.isSessionCookie () && cookie.expirationDate () < now;
68  }
69  }
70 
71  void CustomCookieJar::Load (const QByteArray& data)
72  {
73  QList<QNetworkCookie> cookies, filteredCookies;
74  for (const auto& ba : data.split ('\n'))
75  cookies << QNetworkCookie::parseCookies (ba);
76 
77  const auto& now = QDateTime::currentDateTime ();
78  for (const auto& cookie : cookies)
79  {
80  if (FilterTrackingCookies_ &&
81  cookie.name ().startsWith ("__utm"))
82  continue;
83 
84  if (IsExpired (cookie, now))
85  continue;
86 
87  filteredCookies << cookie;
88  }
89  emit cookiesAdded (filteredCookies);
90  setAllCookies (filteredCookies);
91  }
92 
93  void CustomCookieJar::CollectGarbage ()
94  {
95  const auto& cookies = allCookies ();
96  QList<QNetworkCookie> result;
97  const auto& now = QDateTime::currentDateTime ();
98  for (const auto& cookie : cookies)
99  {
100  if (IsExpired (cookie, now))
101  continue;
102 
103  if (result.contains (cookie))
104  continue;
105 
106  result << cookie;
107  }
108  qDebug () << Q_FUNC_INFO << cookies.size () << result.size ();
109  setAllCookies (result);
110  }
111 
112  QList<QNetworkCookie> CustomCookieJar::cookiesForUrl (const QUrl& url) const
113  {
114  if (!Enabled_)
115  return {};
116 
117  QList<QNetworkCookie> filtered;
118  for (const auto& cookie : QNetworkCookieJar::cookiesForUrl (url))
119  if (!filtered.contains (cookie))
120  filtered << cookie;
121  return filtered;
122  }
123 
124  namespace
125  {
126  bool MatchDomain (const QString& rawDomain, const QString& rawCookieDomain)
127  {
128  auto normalize = [] (QStringView s)
129  {
130  return s.startsWith ('.') ? s.mid (1) : s;
131  };
132  const auto& domain = normalize (rawDomain);
133  const auto& cookieDomain = normalize (rawCookieDomain);
134 
135  if (domain == cookieDomain)
136  return true;
137 
138  const auto idx = domain.indexOf (cookieDomain);
139  return idx > 0 && domain.at (idx - 1) == '.';
140  }
141 
142  bool Check (const QList<QRegExp>& list, const QString& str)
143  {
144  return std::any_of (list.begin (), list.end (),
145  [&str] (const auto& rx) { return str == rx.pattern () || rx.exactMatch (str); });
146  }
147 
148  struct CookiesDiff
149  {
152  };
153 
154  auto CookieToTuple (const QNetworkCookie& c)
155  {
156  return std::make_tuple (c.isHttpOnly (),
157  c.isSecure (),
158  c.isSessionCookie (),
159  c.name (),
160  c.domain (),
161  c.path (),
162  c.value (),
163  c.expirationDate ());
164  }
165 
166  struct CookieLess
167  {
168  bool operator() (const QNetworkCookie& left, const QNetworkCookie& right) const
169  {
170  return CookieToTuple (left) < CookieToTuple (right);
171  }
172  };
173 
174  CookiesDiff CheckDifferences (const QList<QNetworkCookie>& previousList,
175  const QList<QNetworkCookie>& currentList)
176  {
177  using Set_t = std::set<QNetworkCookie, CookieLess>;
178  Set_t previous { previousList.begin (), previousList.end () };
179  Set_t current { currentList.begin (), currentList.end () };
180 
181  CookiesDiff diff;
182  std::set_difference (previous.begin (), previous.end (),
183  current.begin (), current.end (),
184  std::back_inserter (diff.Removed_),
185  CookieLess {});
186  std::set_difference (current.begin (), current.end (),
187  previous.begin (), previous.end (),
188  std::back_inserter (diff.Added_),
189  CookieLess {});
190  return diff;
191  }
192  }
193 
194  bool CustomCookieJar::setCookiesFromUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url)
195  {
196  if (!Enabled_)
197  return false;
198 
199  QList<QNetworkCookie> filtered;
200  filtered.reserve (cookieList.size ());
201  for (auto cookie : cookieList)
202  {
203  if (cookie.domain ().isEmpty ())
204  cookie.setDomain (url.host ());
205 
206  bool checkWhitelist = false;
207  const auto wlGuard = Util::MakeScopeGuard ([&]
208  {
209  if (checkWhitelist && Check (WL_, cookie.domain ()))
210  filtered << cookie;
211  });
212 
213  if (MatchDomainExactly_ && !MatchDomain (url.host (), cookie.domain ()))
214  {
215  checkWhitelist = true;
216  continue;
217  }
218 
219  if (FilterTrackingCookies_ &&
220  cookie.name ().startsWith ("__utm"))
221  {
222  checkWhitelist = true;
223  continue;
224  }
225 
226  if (!Check (BL_, cookie.domain ()))
227  filtered << cookie;
228  }
229 
230  const auto& existing = cookiesForUrl (url);
231  if (existing.isEmpty ())
232  emit cookiesAdded (filtered);
233  else
234  Util::Sequence (this, QtConcurrent::run (CheckDifferences, existing, filtered)) >>
235  [this] (const CookiesDiff& diff)
236  {
237  if (!diff.Removed_.isEmpty ())
238  emit cookiesRemoved (diff.Removed_);
239  if (!diff.Added_.isEmpty ())
240  emit cookiesAdded (diff.Added_);
241  };
242 
243  return QNetworkCookieJar::setCookiesFromUrl (filtered, url);
244  }
245 }
QList< QRegExp >
LC::Util
Definition: icoreproxy.h:33
Added_
QList< QNetworkCookie > Added_
Definition: customcookiejar.cpp:156
futures.h
util.h
Removed_
QList< QNetworkCookie > Removed_
Definition: customcookiejar.cpp:157
customcookiejar.h
LC::Util::MakeScopeGuard
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition: util.h:148