Helpviewer/Network/NetworkAccessManager.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a QNetworkAccessManager subclass.
8 """
9
10 import os
11
12 from PyQt4.QtCore import *
13 from PyQt4.QtGui import QDialog, QMessageBox
14 from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, \
15 QNetworkProxy
16 try:
17 from PyQt4.QtNetwork import QSsl, QSslCertificate, QSslConfiguration, QSslSocket
18 SSL_AVAILABLE = True
19 except ImportError:
20 SSL_AVAILABLE = False
21
22 from UI.AuthenticationDialog import AuthenticationDialog
23 import UI.PixmapCache
24
25 from Helpviewer.HelpLanguagesDialog import HelpLanguagesDialog
26 import Helpviewer.HelpWindow
27
28 from NetworkReply import NetworkReply
29 from NetworkProtocolUnknownErrorReply import NetworkProtocolUnknownErrorReply
30 from NetworkDiskCache import NetworkDiskCache
31
32 from QtHelpAccessHandler import QtHelpAccessHandler
33 from PyrcAccessHandler import PyrcAccessHandler
34 from AboutAccessHandler import AboutAccessHandler
35
36 from Helpviewer.AdBlock.AdBlockAccessHandler import AdBlockAccessHandler
37
38 import Preferences
39 import Utilities
40
41 class NetworkAccessManager(QNetworkAccessManager):
42 """
43 Class implementing a QNetworkAccessManager subclass.
44
45 @signal requestCreated(QNetworkAccessManager::Operation, const QNetworkRequest&, QNetworkReply*)
46 emitted after the request has been created
47 """
48 def __init__(self, engine, parent = None):
49 """
50 Constructor
51
52 @param engine reference to the help engine (QHelpEngine)
53 @param parent reference to the parent object (QObject)
54 """
55 QNetworkAccessManager.__init__(self, parent)
56
57 self.__adblockNetwork = None
58
59 self.__schemeHandlers = {} # dictionary of scheme handlers
60
61 self.__setAccessManagerProxy()
62 self.__setDiskCache()
63 self.languagesChanged()
64
65 if SSL_AVAILABLE:
66 sslCfg = QSslConfiguration.defaultConfiguration()
67 caList = sslCfg.caCertificates()
68 caNew = QSslCertificate.fromData(Preferences.Prefs.settings\
69 .value("Help/CaCertificates").toByteArray())
70 for cert in caNew:
71 caList.append(cert)
72 sslCfg.setCaCertificates(caList)
73 QSslConfiguration.setDefaultConfiguration(sslCfg)
74
75 self.connect(self,
76 SIGNAL('sslErrors(QNetworkReply *, const QList<QSslError> &)'),
77 self.__sslErrors)
78
79 self.connect(self,
80 SIGNAL('proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)'),
81 self.__proxyAuthenticationRequired)
82 self.connect(self,
83 SIGNAL('authenticationRequired(QNetworkReply *, QAuthenticator *)'),
84 self.__authenticationRequired)
85
86 # register scheme handlers
87 self.setSchemeHandler("qthelp", QtHelpAccessHandler(engine, self))
88 self.setSchemeHandler("pyrc", PyrcAccessHandler(self))
89 self.setSchemeHandler("about", AboutAccessHandler(self))
90 self.setSchemeHandler("abp", AdBlockAccessHandler(self))
91
92 def setSchemeHandler(self, scheme, handler):
93 """
94 Public method to register a scheme handler.
95
96 @param scheme access scheme (string)
97 @param handler reference to the scheme handler object (SchemeAccessHandler)
98 """
99 self.__schemeHandlers[scheme] = handler
100
101 def createRequest(self, op, request, outgoingData = None):
102 """
103 Protected method to create a request.
104
105 @param op the operation to be performed (QNetworkAccessManager.Operation)
106 @param request reference to the request object (QNetworkRequest)
107 @param outgoingData reference to an IODevice containing data to be sent
108 (QIODevice)
109 @return reference to the created reply object (QNetworkReply)
110 """
111 scheme = request.url().scheme()
112 if scheme == "https" and (not SSL_AVAILABLE or not QSslSocket.supportsSsl()):
113 return NetworkProtocolUnknownErrorReply(scheme)
114
115 if op == QNetworkAccessManager.PostOperation and outgoingData is not None:
116 outgoingDataByteArray = outgoingData.peek(1024 * 1024)
117 Helpviewer.HelpWindow.HelpWindow.passwordManager().post(
118 request, outgoingDataByteArray)
119
120 reply = None
121 if scheme in self.__schemeHandlers:
122 reply = self.__schemeHandlers[scheme]\
123 .createRequest(op, request, outgoingData)
124 if reply is not None:
125 return reply
126
127 if not self.__acceptLanguage.isEmpty():
128 req = QNetworkRequest(request)
129 req.setRawHeader("Accept-Language", self.__acceptLanguage)
130 else:
131 req = request
132
133 # AdBlock code
134 if op == QNetworkAccessManager.GetOperation:
135 if self.__adblockNetwork is None:
136 self.__adblockNetwork = \
137 Helpviewer.HelpWindow.HelpWindow.adblockManager().network()
138 reply = self.__adblockNetwork.block(req)
139 if reply is not None:
140 return reply
141
142 reply = QNetworkAccessManager.createRequest(self, op, req, outgoingData)
143 self.emit(SIGNAL("requestCreated(QNetworkAccessManager::Operation, const QNetworkRequest&, QNetworkReply*)"),
144 op, req, reply)
145
146 return reply
147
148 def __setAccessManagerProxy(self):
149 """
150 Private method to set the proxy used by the network access manager.
151 """
152 if Preferences.getUI("UseProxy"):
153 host = Preferences.getUI("ProxyHost")
154 if not host:
155 QMessageBox.critical(None,
156 self.trUtf8("Web Browser"),
157 self.trUtf8("""Proxy usage was activated"""
158 """ but no proxy host configured."""))
159 return
160 else:
161 pProxyType = Preferences.getUI("ProxyType")
162 if pProxyType == 0:
163 proxyType = QNetworkProxy.HttpProxy
164 elif pProxyType == 1:
165 proxyType = QNetworkProxy.HttpCachingProxy
166 elif pProxyType == 2:
167 proxyType = QNetworkProxy.Socks5Proxy
168 self.__proxy = QNetworkProxy(proxyType, host,
169 Preferences.getUI("ProxyPort"),
170 Preferences.getUI("ProxyUser"),
171 Preferences.getUI("ProxyPassword"))
172 self.__proxy.setCapabilities(QNetworkProxy.Capabilities(
173 QNetworkProxy.CachingCapability | \
174 QNetworkProxy.HostNameLookupCapability))
175 else:
176 self.__proxy = QNetworkProxy(QNetworkProxy.NoProxy)
177 self.setProxy(self.__proxy)
178
179 def __authenticationRequired(self, reply, auth):
180 """
181 Private slot to handle an authentication request.
182
183 @param reply reference to the reply object (QNetworkReply)
184 @param auth reference to the authenticator object (QAuthenticator)
185 """
186 urlRoot = "{0}://{1}"\
187 .format(reply.url().scheme(), reply.url().authority())
188 if not auth.realm():
189 info = self.trUtf8("<b>Enter username and password for '{0}'</b>")\
190 .format(urlRoot)
191 else:
192 info = self.trUtf8("<b>Enter username and password for '{0}', "
193 "realm '{1}'</b>").format(urlRoot, auth.realm())
194
195 dlg = AuthenticationDialog(info, auth.user(),
196 Preferences.getHelp("SavePasswords"),
197 Preferences.getHelp("SavePasswords"))
198 if Preferences.getHelp("SavePasswords"):
199 username, password = \
200 Helpviewer.HelpWindow.HelpWindow.passwordManager().getLogin(
201 reply.url(), auth.realm())
202 if username:
203 dlg.setData(username, password)
204 if dlg.exec_() == QDialog.Accepted:
205 username, password = dlg.getData()
206 auth.setUser(username)
207 auth.setPassword(password)
208 if Preferences.getHelp("SavePasswords"):
209 Helpviewer.HelpWindow.HelpWindow.passwordManager().setLogin(
210 reply.url(), auth.realm(), username, password)
211
212 def __proxyAuthenticationRequired(self, proxy, auth):
213 """
214 Private slot to handle a proxy authentication request.
215
216 @param proxy reference to the proxy object (QNetworkProxy)
217 @param auth reference to the authenticator object (QAuthenticator)
218 """
219 info = self.trUtf8("<b>Connect to proxy '{0}' using:</b>")\
220 .format(Qt.escape(proxy.hostName()))
221
222 dlg = AuthenticationDialog(info, proxy.user(), True)
223 if dlg.exec_() == QDialog.Accepted:
224 username, password = dlg.getData()
225 auth.setUser(username)
226 auth.setPassword(password)
227 if dlg.shallSave():
228 Preferences.setUI("ProxyUser", username)
229 Preferences.setUI("ProxyPassword", password)
230 self.__proxy.setUser(username)
231 self.__proxy.setPassword(password)
232
233 def __sslErrors(self, reply, errors):
234 """
235 Private slot to handle SSL errors.
236
237 @param reply reference to the reply object (QNetworkReply)
238 @param errors list of SSL errors (list of QSslError)
239 """
240 caMerge = QSslCertificate.fromData(Preferences.Prefs.settings\
241 .value("Help/CaCertificates").toByteArray())
242 caNew = []
243
244 errorStrings = []
245 for err in errors:
246 if err.certificate() in caMerge:
247 continue
248 errorStrings.append(err.errorString())
249 if not err.certificate().isNull():
250 caNew.append(err.certificate())
251 if not errorStrings:
252 reply.ignoreSslErrors()
253 return
254
255 errorString = '.</li><li>'.join(errorStrings)
256 ret = QMessageBox.warning(None,
257 self.trUtf8("SSL Errors"),
258 self.trUtf8("""<p>SSL Errors for <br /><b>{0}</b>"""
259 """<ul><li>{1}</li></ul></p>"""
260 """<p>Do you want to ignore these errors?</p>""")\
261 .format(reply.url().toString(), errorString),
262 QMessageBox.StandardButtons(
263 QMessageBox.No | \
264 QMessageBox.Yes),
265 QMessageBox.No)
266
267 if ret == QMessageBox.Yes:
268 if len(caNew) > 0:
269 certinfos = []
270 for cert in caNew:
271 certinfos.append(self.__certToString(cert))
272 ret = QMessageBox.question(None,
273 self.trUtf8("Certificates"),
274 self.trUtf8("""<p>Certificates:<br/>{0}<br/>"""
275 """Do you want to accept all these certificates?</p>""")\
276 .format("".join(certinfos)),
277 QMessageBox.StandardButtons(\
278 QMessageBox.No | \
279 QMessageBox.Yes),
280 QMessageBox.No)
281 if ret == QMessageBox.Yes:
282 for cert in caNew:
283 caMerge.append(cert)
284
285 sslCfg = QSslConfiguration.defaultConfiguration()
286 caList = sslCfg.caCertificates()
287 for cert in caNew:
288 caList.append(cert)
289 sslCfg.setCaCertificates(caList)
290 QSslConfiguration.setDefaultConfiguration(sslCfg)
291 reply.setSslConfiguration(sslCfg)
292
293 pems = QByteArray()
294 for cert in caMerge:
295 pems.append(cert.toPem() + '\n')
296 Preferences.Prefs.settings.setValue("Help/CaCertificates",
297 QVariant(pems))
298
299 reply.ignoreSslErrors()
300
301 def __certToString(self, cert):
302 """
303 Private method to convert a certificate to a formatted string.
304
305 @param cert certificate to convert (QSslCertificate)
306 @return formatted string (string)
307 """
308 result = "<p>"
309
310 result += cert.subjectInfo(QSslCertificate.CommonName)
311
312 result += self.trUtf8("<br/>Issuer: {0}")\
313 .format(cert.issuerInfo(QSslCertificate.CommonName))
314
315 result += self.trUtf8("<br/>Not valid before: {0}<br/>Valid Until: {1}")\
316 .format(cert.effectiveDate().toString(Qt.ISODate),
317 cert.expiryDate().toString(Qt.ISODate))
318
319 names = cert.alternateSubjectNames()
320 tmpList = names.get(QSsl.DnsEntry, [])
321 if tmpList:
322 result += self.trUtf8("<br/>Alternate Names:<ul><li>{0}</li></ul>")\
323 .format("</li><li>".join(tmpList))
324
325 result += "</p>"
326
327 return result
328
329 def preferencesChanged(self):
330 """
331 Public slot to signal a change of preferences.
332 """
333 self.__setAccessManagerProxy()
334 self.__setDiskCache()
335
336 def languagesChanged(self):
337 """
338 Public slot to (re-)load the list of accepted languages.
339 """
340 languages = Preferences.Prefs.settings.value(
341 "Help/AcceptLanguages",
342 QVariant(HelpLanguagesDialog.defaultAcceptLanguages()))\
343 .toStringList()
344 self.__acceptLanguage = HelpLanguagesDialog.httpString(languages)
345
346 def __setDiskCache(self):
347 """
348 Private method to set the disk cache.
349 """
350 if NetworkDiskCache is not None:
351 if Preferences.getHelp("DiskCacheEnabled"):
352 diskCache = NetworkDiskCache(self)
353 location = os.path.join(Utilities.getConfigDir(), "browser", 'cache')
354 size = Preferences.getHelp("DiskCacheSize") * 1024 * 1024
355 diskCache.setCacheDirectory(location)
356 diskCache.setMaximumCacheSize(size)
357 else:
358 diskCache = None
359 self.setCache(diskCache)

eric ide

mercurial