LeechCraft  0.6.70-9312-g4cc613a2df
Modular cross-platform feature rich live environment.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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  * Boost Software License - Version 1.0 - August 17th, 2003
6  *
7  * Permission is hereby granted, free of charge, to any person or organization
8  * obtaining a copy of the software and accompanying documentation covered by
9  * this license (the "Software") to use, reproduce, display, distribute,
10  * execute, and transmit the Software, and to prepare derivative works of the
11  * Software, and to permit third-parties to whom the Software is furnished to
12  * do so, all subject to the following:
13  *
14  * The copyright notices in the Software and this entire statement, including
15  * the above license grant, this restriction and the following disclaimer,
16  * must be included in all copies of the Software, in whole or in part, and
17  * all derivative works of the Software, unless such copies or derivative
18  * works are solely in the form of machine-executable object code generated by
19  * a source language processor.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  **********************************************************************/
29 
30 #include "customcookiejar.h"
31 #include <set>
32 #include <algorithm>
33 #include <QNetworkCookie>
34 #include <QtDebug>
35 #include <QDateTime>
36 #include <QtConcurrentRun>
37 #include <util/sll/util.h>
38 #include <util/threads/futures.h>
39 
40 namespace LeechCraft
41 {
42 namespace Util
43 {
45  : QNetworkCookieJar (parent)
46  {
47  }
48 
50  {
51  FilterTrackingCookies_ = filter;
52  }
53 
54  void CustomCookieJar::SetEnabled (bool enabled)
55  {
56  Enabled_ = enabled;
57  }
58 
60  {
61  MatchDomainExactly_ = enabled;
62  }
63 
65  {
66  WL_ = list;
67  }
68 
70  {
71  BL_ = list;
72  }
73 
74  QByteArray CustomCookieJar::Save () const
75  {
76  QList<QNetworkCookie> cookies = allCookies ();
77  QByteArray result;
78  for (const auto& cookie : cookies)
79  {
80  if (cookie.isSessionCookie ())
81  continue;
82 
83  result += cookie.toRawForm ();
84  result += "\n";
85  }
86  return result;
87  }
88 
89  void CustomCookieJar::Load (const QByteArray& data)
90  {
91  QList<QNetworkCookie> cookies, filteredCookies;
92  for (const auto& ba : data.split ('\n'))
93  cookies << QNetworkCookie::parseCookies (ba);
94 
95  const auto& now = QDateTime::currentDateTime ();
96  for (const auto& cookie : cookies)
97  {
98  if (FilterTrackingCookies_ &&
99  cookie.name ().startsWith ("__utm"))
100  continue;
101 
102  if (cookie.expirationDate () < now)
103  continue;
104 
105  filteredCookies << cookie;
106  }
107  emit cookiesAdded (filteredCookies);
108  setAllCookies (filteredCookies);
109  }
110 
112  {
113  const auto& cookies = allCookies ();
114  QList<QNetworkCookie> result;
115  const auto& now = QDateTime::currentDateTime ();
116  for (const auto& cookie : cookies)
117  {
118  if (!cookie.isSessionCookie () &&
119  cookie.expirationDate () < now)
120  continue;
121 
122  if (result.contains (cookie))
123  continue;
124 
125  result << cookie;
126  }
127  qDebug () << Q_FUNC_INFO << cookies.size () << result.size ();
128  setAllCookies (result);
129  }
130 
132  {
133  if (!Enabled_)
134  return {};
135 
136  QList<QNetworkCookie> filtered;
137  for (const auto& cookie : QNetworkCookieJar::cookiesForUrl (url))
138  if (!filtered.contains (cookie))
139  filtered << cookie;
140  return filtered;
141  }
142 
143  namespace
144  {
145  bool MatchDomain (QString domain, QString cookieDomain)
146  {
147  auto normalize = [] (QString& s)
148  {
149  if (s.startsWith ('.'))
150  s = s.mid (1);
151  };
152  normalize (domain);
153  normalize (cookieDomain);
154 
155  if (domain == cookieDomain)
156  return true;
157 
158  const auto idx = domain.indexOf (cookieDomain);
159  return idx > 0 && domain.at (idx - 1) == '.';
160  }
161 
162  bool Check (const QList<QRegExp>& list, const QString& str)
163  {
164  for (auto& rx : list)
165  if (str == rx.pattern () || rx.exactMatch (str))
166  return true;
167 
168  return false;
169  }
170 
171  struct CookiesDiff
172  {
175  };
176 
177  auto CookieToTuple (const QNetworkCookie& c)
178  {
179  return std::make_tuple (c.isHttpOnly (),
180  c.isSecure (),
181  c.isSessionCookie (),
182  c.name (),
183  c.domain (),
184  c.path (),
185  c.value (),
186  c.expirationDate ());
187  }
188 
189  struct CookieLess
190  {
191  bool operator() (const QNetworkCookie& left, const QNetworkCookie& right) const
192  {
193  return CookieToTuple (left) < CookieToTuple (right);
194  }
195  };
196 
197  CookiesDiff CheckDifferences (const QList<QNetworkCookie>& previousList,
198  const QList<QNetworkCookie>& currentList)
199  {
200  using Set_t = std::set<QNetworkCookie, CookieLess>;
201  Set_t previous { previousList.begin (), previousList.end () };
202  Set_t current { currentList.begin (), currentList.end () };
203 
204  CookiesDiff diff;
205  std::set_difference (previous.begin (), previous.end (),
206  current.begin (), current.end (),
207  std::back_inserter (diff.Removed_),
208  CookieLess {});
209  std::set_difference (current.begin (), current.end (),
210  previous.begin (), previous.end (),
211  std::back_inserter (diff.Added_),
212  CookieLess {});
213  return diff;
214  }
215  }
216 
217  bool CustomCookieJar::setCookiesFromUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url)
218  {
219  if (!Enabled_)
220  return false;
221 
222  QList<QNetworkCookie> filtered;
223  filtered.reserve (cookieList.size ());
224  for (auto cookie : cookieList)
225  {
226  if (cookie.domain ().isEmpty ())
227  cookie.setDomain (url.host ());
228 
229  bool checkWhitelist = false;
230  const auto wlGuard = Util::MakeScopeGuard ([&]
231  {
232  if (checkWhitelist && Check (WL_, cookie.domain ()))
233  filtered << cookie;
234  });
235 
236  if (MatchDomainExactly_ && !MatchDomain (url.host (), cookie.domain ()))
237  {
238  checkWhitelist = true;
239  continue;
240  }
241 
242  if (FilterTrackingCookies_ &&
243  cookie.name ().startsWith ("__utm"))
244  {
245  checkWhitelist = true;
246  continue;
247  }
248 
249  if (!Check (BL_, cookie.domain ()))
250  filtered << cookie;
251  }
252 
253  const auto& existing = cookiesForUrl (url);
254  if (existing.isEmpty ())
255  emit cookiesAdded (filtered);
256  else
257  Util::Sequence (this, QtConcurrent::run (CheckDifferences, existing, filtered)) >>
258  [this] (const CookiesDiff& diff)
259  {
260  if (!diff.Removed_.isEmpty ())
261  emit cookiesRemoved (diff.Removed_);
262  if (!diff.Added_.isEmpty ())
263  emit cookiesAdded (diff.Added_);
264  };
265 
266  return QNetworkCookieJar::setCookiesFromUrl (filtered, url);
267  }
268 }
269 }
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition: util.h:157
QList< QNetworkCookie > Removed_
QList< QNetworkCookie > Added_
detail::SequenceProxy< detail::SequencerRetType_t< QFuture< T > >, QFuture< T >, detail::EmptyDestructionTag > Sequence(QObject *parent, const QFuture< T > &future)
Creates a sequencer that allows chaining multiple futures.
Definition: futures.h:743