comparison sat/plugins/plugin_misc_ip.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 003b8b4b56a7
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _, D_ 20 from sat.core.i18n import _, D_
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23
23 log = getLogger(__name__) 24 log = getLogger(__name__)
24 from sat.tools import xml_tools 25 from sat.tools import xml_tools
25 from twisted.web import client as webclient 26 from twisted.web import client as webclient
26 from twisted.web import error as web_error 27 from twisted.web import error as web_error
27 from twisted.internet import defer 28 from twisted.internet import defer
32 from zope.interface import implements 33 from zope.interface import implements
33 from wokkel import disco, iwokkel 34 from wokkel import disco, iwokkel
34 from twisted.words.protocols.jabber.xmlstream import XMPPHandler 35 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
35 from twisted.words.protocols.jabber.error import StanzaError 36 from twisted.words.protocols.jabber.error import StanzaError
36 import urlparse 37 import urlparse
38
37 try: 39 try:
38 import netifaces 40 import netifaces
39 except ImportError: 41 except ImportError:
40 log.warning(u"netifaces is not available, it help discovering IPs, you can install it on https://pypi.python.org/pypi/netifaces") 42 log.warning(
43 u"netifaces is not available, it help discovering IPs, you can install it on https://pypi.python.org/pypi/netifaces"
44 )
41 netifaces = None 45 netifaces = None
42 46
43 47
44 PLUGIN_INFO = { 48 PLUGIN_INFO = {
45 C.PI_NAME: "IP discovery", 49 C.PI_NAME: "IP discovery",
48 C.PI_MODES: C.PLUG_MODE_BOTH, 52 C.PI_MODES: C.PLUG_MODE_BOTH,
49 C.PI_PROTOCOLS: ["XEP-0279"], 53 C.PI_PROTOCOLS: ["XEP-0279"],
50 C.PI_RECOMMENDATIONS: ["NAT-PORT"], 54 C.PI_RECOMMENDATIONS: ["NAT-PORT"],
51 C.PI_MAIN: "IPPlugin", 55 C.PI_MAIN: "IPPlugin",
52 C.PI_HANDLER: "yes", 56 C.PI_HANDLER: "yes",
53 C.PI_DESCRIPTION: _("""This plugin help to discover our external IP address.""") 57 C.PI_DESCRIPTION: _("""This plugin help to discover our external IP address."""),
54 } 58 }
55 59
56 # TODO: GET_IP_PAGE should be configurable in sat.conf 60 # TODO: GET_IP_PAGE should be configurable in sat.conf
57 GET_IP_PAGE = "http://salut-a-toi.org/whereami/" # This page must only return external IP of the requester 61 GET_IP_PAGE = (
62 "http://salut-a-toi.org/whereami/"
63 ) # This page must only return external IP of the requester
58 GET_IP_LABEL = D_(u"Allow external get IP") 64 GET_IP_LABEL = D_(u"Allow external get IP")
59 GET_IP_CATEGORY = "General" 65 GET_IP_CATEGORY = "General"
60 GET_IP_NAME = "allow_get_ip" 66 GET_IP_NAME = "allow_get_ip"
61 GET_IP_CONFIRM_TITLE = D_(u"Confirm external site request") 67 GET_IP_CONFIRM_TITLE = D_(u"Confirm external site request")
62 GET_IP_CONFIRM = D_(u"""To facilitate data transfer, we need to contact a website. 68 GET_IP_CONFIRM = D_(
69 u"""To facilitate data transfer, we need to contact a website.
63 A request will be done on {page} 70 A request will be done on {page}
64 That means that administrators of {domain} can know that you use "{app_name}" and your IP Address. 71 That means that administrators of {domain} can know that you use "{app_name}" and your IP Address.
65 72
66 IP address is an identifier to locate you on Internet (similar to a phone number). 73 IP address is an identifier to locate you on Internet (similar to a phone number).
67 74
68 Do you agree to do this request ? 75 Do you agree to do this request ?
69 """).format( 76 """
70 page = GET_IP_PAGE, 77 ).format(
71 domain = urlparse.urlparse(GET_IP_PAGE).netloc, 78 page=GET_IP_PAGE, domain=urlparse.urlparse(GET_IP_PAGE).netloc, app_name=C.APP_NAME
72 app_name = C.APP_NAME) 79 )
73 NS_IP_CHECK = "urn:xmpp:sic:1" 80 NS_IP_CHECK = "urn:xmpp:sic:1"
74 81
75 PARAMS = """ 82 PARAMS = """
76 <params> 83 <params>
77 <general> 84 <general>
78 <category name="{category}"> 85 <category name="{category}">
79 <param name="{name}" label="{label}" type="bool" /> 86 <param name="{name}" label="{label}" type="bool" />
80 </category> 87 </category>
81 </general> 88 </general>
82 </params> 89 </params>
83 """.format(category=GET_IP_CATEGORY, name=GET_IP_NAME, label=GET_IP_LABEL) 90 """.format(
91 category=GET_IP_CATEGORY, name=GET_IP_NAME, label=GET_IP_LABEL
92 )
84 93
85 94
86 class IPPlugin(object): 95 class IPPlugin(object):
87 # TODO: refresh IP if a new connection is detected 96 # TODO: refresh IP if a new connection is detected
88 # TODO: manage IPv6 when implemented in SàT 97 # TODO: manage IPv6 when implemented in SàT
92 self.host = host 101 self.host = host
93 host.memory.updateParams(PARAMS) 102 host.memory.updateParams(PARAMS)
94 103
95 # NAT-Port 104 # NAT-Port
96 try: 105 try:
97 self._nat = host.plugins['NAT-PORT'] 106 self._nat = host.plugins["NAT-PORT"]
98 except KeyError: 107 except KeyError:
99 log.debug(u"NAT port plugin not available") 108 log.debug(u"NAT port plugin not available")
100 self._nat = None 109 self._nat = None
101 110
102 # XXX: cache is kept until SàT is restarted 111 # XXX: cache is kept until SàT is restarted
116 """Return value of parameter with autorisation of user to do external requests 125 """Return value of parameter with autorisation of user to do external requests
117 126
118 if parameter is not set, a dialog is shown to use to get its confirmation, and parameted is set according to answer 127 if parameter is not set, a dialog is shown to use to get its confirmation, and parameted is set according to answer
119 @return (defer.Deferred[bool]): True if external request is autorised 128 @return (defer.Deferred[bool]): True if external request is autorised
120 """ 129 """
121 allow_get_ip = self.host.memory.params.getParamA(GET_IP_NAME, GET_IP_CATEGORY, use_default=False) 130 allow_get_ip = self.host.memory.params.getParamA(
131 GET_IP_NAME, GET_IP_CATEGORY, use_default=False
132 )
122 133
123 if allow_get_ip is None: 134 if allow_get_ip is None:
124 # we don't have autorisation from user yet to use get_ip, we ask him 135 # we don't have autorisation from user yet to use get_ip, we ask him
125 def setParam(allowed): 136 def setParam(allowed):
126 # FIXME: we need to use boolConst as setParam only manage str/unicode 137 # FIXME: we need to use boolConst as setParam only manage str/unicode
127 # need to be fixed when params will be refactored 138 # need to be fixed when params will be refactored
128 self.host.memory.setParam(GET_IP_NAME, C.boolConst(allowed), GET_IP_CATEGORY) 139 self.host.memory.setParam(
140 GET_IP_NAME, C.boolConst(allowed), GET_IP_CATEGORY
141 )
129 return allowed 142 return allowed
130 d = xml_tools.deferConfirm(self.host, _(GET_IP_CONFIRM), _(GET_IP_CONFIRM_TITLE), profile=client.profile) 143
144 d = xml_tools.deferConfirm(
145 self.host,
146 _(GET_IP_CONFIRM),
147 _(GET_IP_CONFIRM_TITLE),
148 profile=client.profile,
149 )
131 d.addCallback(setParam) 150 d.addCallback(setParam)
132 return d 151 return d
133 152
134 return defer.succeed(allow_get_ip) 153 return defer.succeed(allow_get_ip)
135 154
138 157
139 For now, just remove IPv4 local addresses 158 For now, just remove IPv4 local addresses
140 @param ip_addr(str): IP addresse 159 @param ip_addr(str): IP addresse
141 @return (bool): True if addresse is acceptable 160 @return (bool): True if addresse is acceptable
142 """ 161 """
143 return not ip_addr.startswith('127.') 162 return not ip_addr.startswith("127.")
144 163
145 def _insertFirst(self, addresses, ip_addr): 164 def _insertFirst(self, addresses, ip_addr):
146 """Insert ip_addr as first item in addresses 165 """Insert ip_addr as first item in addresses
147 166
148 @param ip_addr(str): IP addresse 167 @param ip_addr(str): IP addresse
162 @return (D(str)): return local IP 181 @return (D(str)): return local IP
163 """ 182 """
164 url = urlparse.urlparse(ext_url) 183 url = urlparse.urlparse(ext_url)
165 port = url.port 184 port = url.port
166 if port is None: 185 if port is None:
167 if url.scheme=='http': 186 if url.scheme == "http":
168 port = 80 187 port = 80
169 elif url.scheme=='https': 188 elif url.scheme == "https":
170 port = 443 189 port = 443
171 else: 190 else:
172 log.error(u"Unknown url scheme: {}".format(url.scheme)) 191 log.error(u"Unknown url scheme: {}".format(url.scheme))
173 defer.returnValue(None) 192 defer.returnValue(None)
174 if url.hostname is None: 193 if url.hostname is None:
175 log.error(u"Can't find url hostname for {}".format(GET_IP_PAGE)) 194 log.error(u"Can't find url hostname for {}".format(GET_IP_PAGE))
176 195
177 point = endpoints.TCP4ClientEndpoint(reactor, url.hostname, port) 196 point = endpoints.TCP4ClientEndpoint(reactor, url.hostname, port)
197
178 def gotConnection(p): 198 def gotConnection(p):
179 local_ip = p.transport.getHost().host 199 local_ip = p.transport.getHost().host
180 p.transport.loseConnection() 200 p.transport.loseConnection()
181 return local_ip 201 return local_ip
182 202
194 """ 214 """
195 # TODO: manage permission requesting (e.g. for UMTS link) 215 # TODO: manage permission requesting (e.g. for UMTS link)
196 if self._local_ip_cache is not None: 216 if self._local_ip_cache is not None:
197 defer.returnValue(self._local_ip_cache) 217 defer.returnValue(self._local_ip_cache)
198 addresses = [] 218 addresses = []
199 localhost = ['127.0.0.1'] 219 localhost = ["127.0.0.1"]
200 220
201 # we first try our luck with netifaces 221 # we first try our luck with netifaces
202 if netifaces is not None: 222 if netifaces is not None:
203 addresses = [] 223 addresses = []
204 for interface in netifaces.interfaces(): 224 for interface in netifaces.interfaces():
206 try: 226 try:
207 inet_list = if_addresses[netifaces.AF_INET] 227 inet_list = if_addresses[netifaces.AF_INET]
208 except KeyError: 228 except KeyError:
209 continue 229 continue
210 for data in inet_list: 230 for data in inet_list:
211 addresse = data['addr'] 231 addresse = data["addr"]
212 if self._filterAddresse(addresse): 232 if self._filterAddresse(addresse):
213 addresses.append(addresse) 233 addresses.append(addresse)
214 234
215 # then we use our connection to server 235 # then we use our connection to server
216 ip = client.xmlstream.transport.getHost().host 236 ip = client.xmlstream.transport.getHost().host
249 @return (deferred): external IP address or None if it can't be discovered 269 @return (deferred): external IP address or None if it can't be discovered
250 """ 270 """
251 if self._external_ip_cache is not None: 271 if self._external_ip_cache is not None:
252 defer.returnValue(self._external_ip_cache) 272 defer.returnValue(self._external_ip_cache)
253 273
254
255 # we first try with XEP-0279 274 # we first try with XEP-0279
256 ip_check = yield self.host.hasFeature(client, NS_IP_CHECK) 275 ip_check = yield self.host.hasFeature(client, NS_IP_CHECK)
257 if ip_check: 276 if ip_check:
258 log.debug(u"Server IP Check available, we use it to retrieve our IP") 277 log.debug(u"Server IP Check available, we use it to retrieve our IP")
259 iq_elt = client.IQ("get") 278 iq_elt = client.IQ("get")
260 iq_elt.addElement((NS_IP_CHECK, 'address')) 279 iq_elt.addElement((NS_IP_CHECK, "address"))
261 try: 280 try:
262 result_elt = yield iq_elt.send() 281 result_elt = yield iq_elt.send()
263 address_elt = result_elt.elements(NS_IP_CHECK, 'address').next() 282 address_elt = result_elt.elements(NS_IP_CHECK, "address").next()
264 ip_elt = address_elt.elements(NS_IP_CHECK,'ip').next() 283 ip_elt = address_elt.elements(NS_IP_CHECK, "ip").next()
265 except StopIteration: 284 except StopIteration:
266 log.warning(u"Server returned invalid result on XEP-0279 request, we ignore it") 285 log.warning(
286 u"Server returned invalid result on XEP-0279 request, we ignore it"
287 )
267 except StanzaError as e: 288 except StanzaError as e:
268 log.warning(u"error while requesting ip to server: {}".format(e)) 289 log.warning(u"error while requesting ip to server: {}".format(e))
269 else: 290 else:
270 # FIXME: server IP may not be the same as external IP (server can be on local machine or network) 291 # FIXME: server IP may not be the same as external IP (server can be on local machine or network)
271 # IP should be checked to see if we have a local one, and rejected in this case 292 # IP should be checked to see if we have a local one, and rejected in this case
287 ip = (yield webclient.getPage(GET_IP_PAGE)) if allow_get_ip else None 308 ip = (yield webclient.getPage(GET_IP_PAGE)) if allow_get_ip else None
288 except (internet_error.DNSLookupError, internet_error.TimeoutError): 309 except (internet_error.DNSLookupError, internet_error.TimeoutError):
289 log.warning(u"Can't access Domain Name System") 310 log.warning(u"Can't access Domain Name System")
290 ip = None 311 ip = None
291 except web_error.Error as e: 312 except web_error.Error as e:
292 log.warning(u"Error while retrieving IP on {url}: {message}".format(url=GET_IP_PAGE, message=e)) 313 log.warning(
314 u"Error while retrieving IP on {url}: {message}".format(
315 url=GET_IP_PAGE, message=e
316 )
317 )
293 ip = None 318 ip = None
294 else: 319 else:
295 self._external_ip_cache = ip 320 self._external_ip_cache = ip
296 defer.returnValue(ip) 321 defer.returnValue(ip)
297 322
298 323
299 class IPPlugin_handler(XMPPHandler): 324 class IPPlugin_handler(XMPPHandler):
300 implements(iwokkel.IDisco) 325 implements(iwokkel.IDisco)
301 326
302 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 327 def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
303 return [disco.DiscoFeature(NS_IP_CHECK)] 328 return [disco.DiscoFeature(NS_IP_CHECK)]
304 329
305 def getDiscoItems(self, requestor, target, nodeIdentifier=''): 330 def getDiscoItems(self, requestor, target, nodeIdentifier=""):
306 return [] 331 return []