|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2010 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a network proxy factory. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import os |
|
13 |
|
14 from PyQt5.QtCore import Qt, QUrl, QCoreApplication, QRegExp |
|
15 from PyQt5.QtWidgets import QDialog |
|
16 from PyQt5.QtNetwork import QNetworkProxyFactory, QNetworkProxy, \ |
|
17 QNetworkProxyQuery |
|
18 |
|
19 from E5Gui import E5MessageBox |
|
20 |
|
21 import Preferences |
|
22 import Globals |
|
23 import Utilities |
|
24 |
|
25 |
|
26 def schemeFromProxyType(proxyType): |
|
27 """ |
|
28 Module function to determine the scheme name from the proxy type. |
|
29 |
|
30 @param proxyType type of the proxy (QNetworkProxy.ProxyType) |
|
31 @return scheme (string, one of Http, Https, Ftp) |
|
32 """ |
|
33 scheme = "" |
|
34 if proxyType == QNetworkProxy.HttpProxy: |
|
35 scheme = "Http" |
|
36 elif proxyType == QNetworkProxy.HttpCachingProxy: |
|
37 scheme = "Https" |
|
38 elif proxyType == QNetworkProxy.FtpCachingProxy: |
|
39 scheme = "Ftp" |
|
40 elif proxyType == QNetworkProxy.NoProxy: |
|
41 scheme = "NoProxy" |
|
42 return scheme |
|
43 |
|
44 |
|
45 def proxyAuthenticationRequired(proxy, auth): |
|
46 """ |
|
47 Module slot to handle a proxy authentication request. |
|
48 |
|
49 @param proxy reference to the proxy object (QNetworkProxy) |
|
50 @param auth reference to the authenticator object (QAuthenticator) |
|
51 """ |
|
52 info = QCoreApplication.translate( |
|
53 "E5NetworkProxyFactory", |
|
54 "<b>Connect to proxy '{0}' using:</b>")\ |
|
55 .format(Utilities.html_encode(proxy.hostName())) |
|
56 |
|
57 from UI.AuthenticationDialog import AuthenticationDialog |
|
58 dlg = AuthenticationDialog(info, proxy.user(), True) |
|
59 dlg.setData(proxy.user(), proxy.password()) |
|
60 if dlg.exec_() == QDialog.Accepted: |
|
61 username, password = dlg.getData() |
|
62 auth.setUser(username) |
|
63 auth.setPassword(password) |
|
64 if dlg.shallSave(): |
|
65 scheme = schemeFromProxyType(proxy.type()) |
|
66 if scheme and scheme != "NoProxy": |
|
67 Preferences.setUI("ProxyUser/{0}".format(scheme), username) |
|
68 Preferences.setUI("ProxyPassword/{0}".format(scheme), password) |
|
69 proxy.setUser(username) |
|
70 proxy.setPassword(password) |
|
71 |
|
72 |
|
73 class HostnameMatcher(object): |
|
74 """ |
|
75 Class implementing a matcher for host names. |
|
76 """ |
|
77 def __init__(self, pattern): |
|
78 """ |
|
79 Constructor |
|
80 |
|
81 @param pattern pattern to be matched against |
|
82 @type str |
|
83 """ |
|
84 self.__regExp = None |
|
85 self.setPattern(pattern) |
|
86 |
|
87 def setPattern(self, pattern): |
|
88 """ |
|
89 Public method to set the match pattern. |
|
90 |
|
91 @param pattern pattern to be matched against |
|
92 """ |
|
93 self.__pattern = pattern |
|
94 |
|
95 if "?" in pattern or "*" in pattern: |
|
96 regexp = "^.*{0}.*$".format( |
|
97 pattern |
|
98 .replace(".", "\\.") |
|
99 .replace("*", ".*") |
|
100 .replace("?", ".") |
|
101 ) |
|
102 self.__regExp = QRegExp(regexp, Qt.CaseInsensitive) |
|
103 |
|
104 def pattern(self): |
|
105 """ |
|
106 Public method to get the match pattern. |
|
107 |
|
108 @return match pattern |
|
109 @rtype str |
|
110 """ |
|
111 return self.__pattern |
|
112 |
|
113 def match(self, host): |
|
114 """ |
|
115 Public method to test the given string. |
|
116 |
|
117 @param host host name to be matched |
|
118 @type str |
|
119 @return flag indicating a successful match |
|
120 @rtype bool |
|
121 """ |
|
122 if self.__regExp is None: |
|
123 return self.__pattern in host |
|
124 |
|
125 return self.__regExp.indexIn(host) > -1 |
|
126 |
|
127 |
|
128 class E5NetworkProxyFactory(QNetworkProxyFactory): |
|
129 """ |
|
130 Class implementing a network proxy factory. |
|
131 """ |
|
132 def __init__(self): |
|
133 """ |
|
134 Constructor |
|
135 """ |
|
136 super(E5NetworkProxyFactory, self).__init__() |
|
137 |
|
138 self.__hostnameMatchers = [] |
|
139 self.__exceptions = "" |
|
140 |
|
141 def __setExceptions(self, exceptions): |
|
142 """ |
|
143 Private method to set the host name exceptions. |
|
144 |
|
145 @param exceptions list of exceptions separated by ',' |
|
146 @type str |
|
147 """ |
|
148 self.__hostnameMatchers = [] |
|
149 self.__exceptions = exceptions |
|
150 for exception in self.__exceptions.split(","): |
|
151 self.__hostnameMatchers.append(HostnameMatcher(exception.strip())) |
|
152 |
|
153 def queryProxy(self, query): |
|
154 """ |
|
155 Public method to determine a proxy for a given query. |
|
156 |
|
157 @param query reference to the query object (QNetworkProxyQuery) |
|
158 @return list of proxies in order of preference (list of QNetworkProxy) |
|
159 """ |
|
160 if query.queryType() == QNetworkProxyQuery.UrlRequest and \ |
|
161 query.protocolTag() in ["http", "https", "ftp"]: |
|
162 # use proxy at all ? |
|
163 if not Preferences.getUI("UseProxy"): |
|
164 return [QNetworkProxy(QNetworkProxy.NoProxy)] |
|
165 |
|
166 # test for exceptions |
|
167 exceptions = Preferences.getUI("ProxyExceptions") |
|
168 if exceptions != self.__exceptions: |
|
169 self.__setExceptions(exceptions) |
|
170 urlHost = query.url().host() |
|
171 for matcher in self.__hostnameMatchers: |
|
172 if matcher.match(urlHost): |
|
173 return [QNetworkProxy(QNetworkProxy.NoProxy)] |
|
174 |
|
175 # determine proxy |
|
176 if Preferences.getUI("UseSystemProxy"): |
|
177 proxyList = QNetworkProxyFactory.systemProxyForQuery(query) |
|
178 if not Globals.isWindowsPlatform() and \ |
|
179 len(proxyList) == 1 and \ |
|
180 proxyList[0].type() == QNetworkProxy.NoProxy: |
|
181 # try it the Python way |
|
182 # scan the environment for variables named <scheme>_proxy |
|
183 # scan over whole environment to make this case insensitive |
|
184 for name, value in os.environ.items(): |
|
185 name = name.lower() |
|
186 if value and name[-6:] == '_proxy' and \ |
|
187 name[:-6] == query.protocolTag().lower(): |
|
188 url = QUrl(value) |
|
189 if url.scheme() in ["http", "https"]: |
|
190 proxyType = QNetworkProxy.HttpProxy |
|
191 elif url.scheme() == "ftp": |
|
192 proxyType = QNetworkProxy.FtpCachingProxy |
|
193 else: |
|
194 proxyType = QNetworkProxy.HttpProxy |
|
195 proxy = QNetworkProxy( |
|
196 proxyType, url.host(), url.port(), |
|
197 url.userName(), url.password()) |
|
198 proxyList = [proxy] |
|
199 break |
|
200 if proxyList: |
|
201 scheme = schemeFromProxyType(proxyList[0].type()) |
|
202 if scheme == "": |
|
203 scheme = "Http" |
|
204 if scheme != "NoProxy": |
|
205 proxyList[0].setUser( |
|
206 Preferences.getUI("ProxyUser/{0}".format(scheme))) |
|
207 proxyList[0].setPassword( |
|
208 Preferences.getUI( |
|
209 "ProxyPassword/{0}".format(scheme))) |
|
210 return proxyList |
|
211 else: |
|
212 return [QNetworkProxy(QNetworkProxy.NoProxy)] |
|
213 else: |
|
214 if Preferences.getUI("UseHttpProxyForAll"): |
|
215 protocolKey = "Http" |
|
216 else: |
|
217 protocolKey = query.protocolTag().capitalize() |
|
218 host = Preferences.getUI("ProxyHost/{0}".format(protocolKey)) |
|
219 if not host: |
|
220 E5MessageBox.critical( |
|
221 None, |
|
222 QCoreApplication.translate( |
|
223 "E5NetworkProxyFactory", |
|
224 "Proxy Configuration Error"), |
|
225 QCoreApplication.translate( |
|
226 "E5NetworkProxyFactory", |
|
227 """Proxy usage was activated""" |
|
228 """ but no proxy host for protocol""" |
|
229 """ '{0}' configured.""").format(protocolKey)) |
|
230 return [QNetworkProxy(QNetworkProxy.DefaultProxy)] |
|
231 else: |
|
232 if protocolKey in ["Http", "Https", "Ftp"]: |
|
233 if query.protocolTag() == "ftp": |
|
234 proxyType = QNetworkProxy.FtpCachingProxy |
|
235 else: |
|
236 proxyType = QNetworkProxy.HttpProxy |
|
237 proxy = QNetworkProxy( |
|
238 proxyType, host, |
|
239 Preferences.getUI("ProxyPort/" + protocolKey), |
|
240 Preferences.getUI("ProxyUser/" + protocolKey), |
|
241 Preferences.getUI("ProxyPassword/" + protocolKey)) |
|
242 else: |
|
243 proxy = QNetworkProxy(QNetworkProxy.DefaultProxy) |
|
244 return [proxy, QNetworkProxy(QNetworkProxy.DefaultProxy)] |
|
245 else: |
|
246 return [QNetworkProxy(QNetworkProxy.NoProxy)] |