|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a QNetworkAccessManager subclass. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import os |
|
13 |
|
14 from PyQt5.QtCore import pyqtSignal, QByteArray |
|
15 from PyQt5.QtWidgets import QDialog |
|
16 from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, \ |
|
17 QNetworkReply |
|
18 |
|
19 from E5Network.E5NetworkProxyFactory import E5NetworkProxyFactory, \ |
|
20 proxyAuthenticationRequired |
|
21 try: |
|
22 from PyQt5.QtNetwork import QSslSocket |
|
23 from E5Network.E5SslErrorHandler import E5SslErrorHandler |
|
24 SSL_AVAILABLE = True |
|
25 except ImportError: |
|
26 SSL_AVAILABLE = False |
|
27 |
|
28 import Preferences |
|
29 import Utilities |
|
30 from Globals import qVersionTuple |
|
31 |
|
32 |
|
33 class NetworkAccessManager(QNetworkAccessManager): |
|
34 """ |
|
35 Class implementing a QNetworkAccessManager subclass. |
|
36 |
|
37 @signal requestCreated emitted after the request has been created passing |
|
38 the operation, a reference to the network request and a reference to |
|
39 the network reply (QNetworkAccessManager.Operation, QNetworkRequest, |
|
40 QNetworkReply) |
|
41 """ |
|
42 requestCreated = pyqtSignal( |
|
43 QNetworkAccessManager.Operation, QNetworkRequest, QNetworkReply) |
|
44 |
|
45 def __init__(self, engine, parent=None): |
|
46 """ |
|
47 Constructor |
|
48 |
|
49 @param engine reference to the help engine (QHelpEngine) |
|
50 @param parent reference to the parent object (QObject) |
|
51 """ |
|
52 super(NetworkAccessManager, self).__init__(parent) |
|
53 |
|
54 self.__adblockNetwork = None |
|
55 |
|
56 self.__schemeHandlers = {} # dictionary of scheme handlers |
|
57 |
|
58 self.__proxyFactory = E5NetworkProxyFactory() |
|
59 self.setProxyFactory(self.__proxyFactory) |
|
60 |
|
61 self.__setDiskCache() |
|
62 self.languagesChanged() |
|
63 |
|
64 if SSL_AVAILABLE: |
|
65 self.__sslErrorHandler = E5SslErrorHandler(self) |
|
66 self.sslErrors.connect(self.__sslErrorHandler.sslErrorsReplySlot) |
|
67 |
|
68 self.proxyAuthenticationRequired.connect(proxyAuthenticationRequired) |
|
69 self.authenticationRequired.connect(self.__authenticationRequired) |
|
70 |
|
71 self.__doNotTrack = Preferences.getHelp("DoNotTrack") |
|
72 self.__sendReferer = Preferences.getHelp("SendReferer") |
|
73 |
|
74 # register scheme handlers |
|
75 if engine: |
|
76 from .QtHelpAccessHandler import QtHelpAccessHandler |
|
77 self.setSchemeHandler("qthelp", QtHelpAccessHandler(engine, self)) |
|
78 |
|
79 from .EricAccessHandler import EricAccessHandler |
|
80 self.setSchemeHandler("eric", EricAccessHandler(self)) |
|
81 |
|
82 from .AboutAccessHandler import AboutAccessHandler |
|
83 self.setSchemeHandler("about", AboutAccessHandler(self)) |
|
84 |
|
85 from Helpviewer.AdBlock.AdBlockAccessHandler import \ |
|
86 AdBlockAccessHandler |
|
87 self.setSchemeHandler("abp", AdBlockAccessHandler(self)) |
|
88 |
|
89 from .FtpAccessHandler import FtpAccessHandler |
|
90 self.setSchemeHandler("ftp", FtpAccessHandler(self)) |
|
91 |
|
92 from .FileAccessHandler import FileAccessHandler |
|
93 self.setSchemeHandler("file", FileAccessHandler(self)) |
|
94 |
|
95 def setSchemeHandler(self, scheme, handler): |
|
96 """ |
|
97 Public method to register a scheme handler. |
|
98 |
|
99 @param scheme access scheme (string) |
|
100 @param handler reference to the scheme handler object |
|
101 (SchemeAccessHandler) |
|
102 """ |
|
103 self.__schemeHandlers[scheme] = handler |
|
104 |
|
105 def createRequest(self, op, request, outgoingData=None): |
|
106 """ |
|
107 Public method to create a request. |
|
108 |
|
109 @param op the operation to be performed |
|
110 (QNetworkAccessManager.Operation) |
|
111 @param request reference to the request object (QNetworkRequest) |
|
112 @param outgoingData reference to an IODevice containing data to be sent |
|
113 (QIODevice) |
|
114 @return reference to the created reply object (QNetworkReply) |
|
115 """ |
|
116 scheme = request.url().scheme() |
|
117 if scheme == "https" and \ |
|
118 (not SSL_AVAILABLE or not QSslSocket.supportsSsl()): |
|
119 from .NetworkProtocolUnknownErrorReply import \ |
|
120 NetworkProtocolUnknownErrorReply |
|
121 return NetworkProtocolUnknownErrorReply(scheme, self) |
|
122 |
|
123 import Helpviewer.HelpWindow |
|
124 |
|
125 if op == QNetworkAccessManager.PostOperation and \ |
|
126 outgoingData is not None: |
|
127 outgoingDataByteArray = outgoingData.peek(1024 * 1024) |
|
128 Helpviewer.HelpWindow.HelpWindow.passwordManager().post( |
|
129 request, outgoingDataByteArray) |
|
130 |
|
131 reply = None |
|
132 if scheme in self.__schemeHandlers: |
|
133 reply = self.__schemeHandlers[scheme]\ |
|
134 .createRequest(op, request, outgoingData) |
|
135 if reply is not None: |
|
136 return reply |
|
137 |
|
138 # give GreaseMonkey the chance to create a request |
|
139 reply = Helpviewer.HelpWindow.HelpWindow.greaseMonkeyManager()\ |
|
140 .createRequest(op, request, outgoingData) |
|
141 if reply is not None: |
|
142 return reply |
|
143 |
|
144 req = QNetworkRequest(request) |
|
145 if req.rawHeader(b"X-Eric6-UserLoadAction") == QByteArray(b"1"): |
|
146 req.setRawHeader(b"X-Eric6-UserLoadAction", QByteArray()) |
|
147 req.setAttribute(QNetworkRequest.User + 200, b"") |
|
148 else: |
|
149 req.setAttribute( |
|
150 QNetworkRequest.User + 200, req.rawHeader(b"Referer")) |
|
151 |
|
152 if hasattr(QNetworkRequest, 'HttpPipeliningAllowedAttribute'): |
|
153 req.setAttribute( |
|
154 QNetworkRequest.HttpPipeliningAllowedAttribute, True) |
|
155 if not self.__acceptLanguage.isEmpty(): |
|
156 req.setRawHeader(b"Accept-Language", self.__acceptLanguage) |
|
157 |
|
158 # AdBlock code |
|
159 if op == QNetworkAccessManager.GetOperation: |
|
160 if self.__adblockNetwork is None: |
|
161 self.__adblockNetwork = \ |
|
162 Helpviewer.HelpWindow.HelpWindow.adBlockManager().network() |
|
163 reply = self.__adblockNetwork.block(req) |
|
164 if reply is not None: |
|
165 reply.setParent(self) |
|
166 return reply |
|
167 |
|
168 # set cache policy |
|
169 if op == QNetworkAccessManager.GetOperation: |
|
170 urlHost = req.url().host() |
|
171 for host in Preferences.getHelp("NoCacheHosts"): |
|
172 if host in urlHost: |
|
173 req.setAttribute( |
|
174 QNetworkRequest.CacheLoadControlAttribute, |
|
175 QNetworkRequest.AlwaysNetwork) |
|
176 break |
|
177 else: |
|
178 req.setAttribute( |
|
179 QNetworkRequest.CacheLoadControlAttribute, |
|
180 Preferences.getHelp("CachePolicy")) |
|
181 else: |
|
182 req.setAttribute( |
|
183 QNetworkRequest.CacheLoadControlAttribute, |
|
184 QNetworkRequest.AlwaysNetwork) |
|
185 |
|
186 # Do Not Track feature |
|
187 if self.__doNotTrack: |
|
188 req.setRawHeader(b"DNT", b"1") |
|
189 req.setRawHeader(b"X-Do-Not-Track", b"1") |
|
190 |
|
191 # Send referer header? |
|
192 if not self.__sendReferer and \ |
|
193 req.url().host() not in Preferences.getHelp("SendRefererWhitelist"): |
|
194 req.setRawHeader(b"Referer", b"") |
|
195 |
|
196 reply = QNetworkAccessManager.createRequest( |
|
197 self, op, req, outgoingData) |
|
198 self.requestCreated.emit(op, req, reply) |
|
199 |
|
200 return reply |
|
201 |
|
202 def __authenticationRequired(self, reply, auth): |
|
203 """ |
|
204 Private slot to handle an authentication request. |
|
205 |
|
206 @param reply reference to the reply object (QNetworkReply) |
|
207 @param auth reference to the authenticator object (QAuthenticator) |
|
208 """ |
|
209 urlRoot = "{0}://{1}"\ |
|
210 .format(reply.url().scheme(), reply.url().authority()) |
|
211 realm = auth.realm() |
|
212 if not realm and 'realm' in auth.options(): |
|
213 realm = auth.option("realm") |
|
214 if realm: |
|
215 info = self.tr("<b>Enter username and password for '{0}', " |
|
216 "realm '{1}'</b>").format(urlRoot, realm) |
|
217 else: |
|
218 info = self.tr("<b>Enter username and password for '{0}'</b>")\ |
|
219 .format(urlRoot) |
|
220 |
|
221 from UI.AuthenticationDialog import AuthenticationDialog |
|
222 import Helpviewer.HelpWindow |
|
223 |
|
224 dlg = AuthenticationDialog(info, auth.user(), |
|
225 Preferences.getUser("SavePasswords"), |
|
226 Preferences.getUser("SavePasswords")) |
|
227 if Preferences.getUser("SavePasswords"): |
|
228 username, password = \ |
|
229 Helpviewer.HelpWindow.HelpWindow.passwordManager().getLogin( |
|
230 reply.url(), realm) |
|
231 if username: |
|
232 dlg.setData(username, password) |
|
233 if dlg.exec_() == QDialog.Accepted: |
|
234 username, password = dlg.getData() |
|
235 auth.setUser(username) |
|
236 auth.setPassword(password) |
|
237 if Preferences.getUser("SavePasswords") and dlg.shallSave(): |
|
238 Helpviewer.HelpWindow.HelpWindow.passwordManager().setLogin( |
|
239 reply.url(), realm, username, password) |
|
240 |
|
241 def preferencesChanged(self): |
|
242 """ |
|
243 Public slot to signal a change of preferences. |
|
244 """ |
|
245 self.__setDiskCache() |
|
246 |
|
247 self.__doNotTrack = Preferences.getHelp("DoNotTrack") |
|
248 self.__sendReferer = Preferences.getHelp("SendReferer") |
|
249 |
|
250 def languagesChanged(self): |
|
251 """ |
|
252 Public slot to (re-)load the list of accepted languages. |
|
253 """ |
|
254 from Helpviewer.HelpLanguagesDialog import HelpLanguagesDialog |
|
255 languages = Preferences.toList( |
|
256 Preferences.Prefs.settings.value( |
|
257 "Help/AcceptLanguages", |
|
258 HelpLanguagesDialog.defaultAcceptLanguages())) |
|
259 self.__acceptLanguage = HelpLanguagesDialog.httpString(languages) |
|
260 |
|
261 def __setDiskCache(self): |
|
262 """ |
|
263 Private method to set the disk cache. |
|
264 """ |
|
265 if Preferences.getHelp("DiskCacheEnabled"): |
|
266 from PyQt5.QtWebKit import qWebKitVersion |
|
267 from .NetworkDiskCache import NetworkDiskCache |
|
268 diskCache = NetworkDiskCache(self) |
|
269 location = os.path.join( |
|
270 Utilities.getConfigDir(), "browser", 'cache', |
|
271 "{0}-Qt{1}.{2}".format(qWebKitVersion(), *qVersionTuple())) |
|
272 size = Preferences.getHelp("DiskCacheSize") * 1024 * 1024 |
|
273 diskCache.setCacheDirectory(location) |
|
274 diskCache.setMaximumCacheSize(size) |
|
275 else: |
|
276 diskCache = None |
|
277 self.setCache(diskCache) |