Mercurial > libervia-backend
comparison sat/plugins/plugin_misc_ip.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | 003b8b4b56a7 |
children | b64dd7c1496d |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT plugin for IP address discovery | 4 # SAT plugin for IP address discovery |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
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 import urllib.parse | |
20 from sat.core.i18n import _, D_ | 21 from sat.core.i18n import _, D_ |
21 from sat.core.constants import Const as C | 22 from sat.core.constants import Const as C |
22 from sat.core.log import getLogger | 23 from sat.core.log import getLogger |
23 | |
24 log = getLogger(__name__) | |
25 from sat.tools import xml_tools | 24 from sat.tools import xml_tools |
25 from wokkel import disco, iwokkel | |
26 from twisted.web import client as webclient | 26 from twisted.web import client as webclient |
27 from twisted.web import error as web_error | 27 from twisted.web import error as web_error |
28 from twisted.internet import defer | 28 from twisted.internet import defer |
29 from twisted.internet import reactor | 29 from twisted.internet import reactor |
30 from twisted.internet import protocol | 30 from twisted.internet import protocol |
31 from twisted.internet import endpoints | 31 from twisted.internet import endpoints |
32 from twisted.internet import error as internet_error | 32 from twisted.internet import error as internet_error |
33 from zope.interface import implements | 33 from zope.interface import implementer |
34 from wokkel import disco, iwokkel | |
35 from twisted.words.protocols.jabber.xmlstream import XMPPHandler | 34 from twisted.words.protocols.jabber.xmlstream import XMPPHandler |
36 from twisted.words.protocols.jabber.error import StanzaError | 35 from twisted.words.protocols.jabber.error import StanzaError |
37 import urlparse | 36 |
37 log = getLogger(__name__) | |
38 | 38 |
39 try: | 39 try: |
40 import netifaces | 40 import netifaces |
41 except ImportError: | 41 except ImportError: |
42 log.warning( | 42 log.warning( |
43 u"netifaces is not available, it help discovering IPs, you can install it on https://pypi.python.org/pypi/netifaces" | 43 "netifaces is not available, it help discovering IPs, you can install it on https://pypi.python.org/pypi/netifaces" |
44 ) | 44 ) |
45 netifaces = None | 45 netifaces = None |
46 | 46 |
47 | 47 |
48 PLUGIN_INFO = { | 48 PLUGIN_INFO = { |
59 | 59 |
60 # TODO: GET_IP_PAGE should be configurable in sat.conf | 60 # TODO: GET_IP_PAGE should be configurable in sat.conf |
61 GET_IP_PAGE = ( | 61 GET_IP_PAGE = ( |
62 "http://salut-a-toi.org/whereami/" | 62 "http://salut-a-toi.org/whereami/" |
63 ) # This page must only return external IP of the requester | 63 ) # This page must only return external IP of the requester |
64 GET_IP_LABEL = D_(u"Allow external get IP") | 64 GET_IP_LABEL = D_("Allow external get IP") |
65 GET_IP_CATEGORY = "General" | 65 GET_IP_CATEGORY = "General" |
66 GET_IP_NAME = "allow_get_ip" | 66 GET_IP_NAME = "allow_get_ip" |
67 GET_IP_CONFIRM_TITLE = D_(u"Confirm external site request") | 67 GET_IP_CONFIRM_TITLE = D_("Confirm external site request") |
68 GET_IP_CONFIRM = D_( | 68 GET_IP_CONFIRM = D_( |
69 u"""To facilitate data transfer, we need to contact a website. | 69 """To facilitate data transfer, we need to contact a website. |
70 A request will be done on {page} | 70 A request will be done on {page} |
71 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. |
72 | 72 |
73 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). |
74 | 74 |
75 Do you agree to do this request ? | 75 Do you agree to do this request ? |
76 """ | 76 """ |
77 ).format( | 77 ).format( |
78 page=GET_IP_PAGE, domain=urlparse.urlparse(GET_IP_PAGE).netloc, app_name=C.APP_NAME | 78 page=GET_IP_PAGE, domain=urllib.parse.urlparse(GET_IP_PAGE).netloc, app_name=C.APP_NAME |
79 ) | 79 ) |
80 NS_IP_CHECK = "urn:xmpp:sic:1" | 80 NS_IP_CHECK = "urn:xmpp:sic:1" |
81 | 81 |
82 PARAMS = """ | 82 PARAMS = """ |
83 <params> | 83 <params> |
103 | 103 |
104 # NAT-Port | 104 # NAT-Port |
105 try: | 105 try: |
106 self._nat = host.plugins["NAT-PORT"] | 106 self._nat = host.plugins["NAT-PORT"] |
107 except KeyError: | 107 except KeyError: |
108 log.debug(u"NAT port plugin not available") | 108 log.debug("NAT port plugin not available") |
109 self._nat = None | 109 self._nat = None |
110 | 110 |
111 # XXX: cache is kept until SàT is restarted | 111 # XXX: cache is kept until SàT is restarted |
112 # if IP may have changed, use self.refreshIP | 112 # if IP may have changed, use self.refreshIP |
113 self._external_ip_cache = None | 113 self._external_ip_cache = None |
178 """Get local IP by doing a connection on an external url | 178 """Get local IP by doing a connection on an external url |
179 | 179 |
180 @param ext_utl(str): url to connect to | 180 @param ext_utl(str): url to connect to |
181 @return (D(str)): return local IP | 181 @return (D(str)): return local IP |
182 """ | 182 """ |
183 url = urlparse.urlparse(ext_url) | 183 url = urllib.parse.urlparse(ext_url) |
184 port = url.port | 184 port = url.port |
185 if port is None: | 185 if port is None: |
186 if url.scheme == "http": | 186 if url.scheme == "http": |
187 port = 80 | 187 port = 80 |
188 elif url.scheme == "https": | 188 elif url.scheme == "https": |
189 port = 443 | 189 port = 443 |
190 else: | 190 else: |
191 log.error(u"Unknown url scheme: {}".format(url.scheme)) | 191 log.error("Unknown url scheme: {}".format(url.scheme)) |
192 defer.returnValue(None) | 192 defer.returnValue(None) |
193 if url.hostname is None: | 193 if url.hostname is None: |
194 log.error(u"Can't find url hostname for {}".format(GET_IP_PAGE)) | 194 log.error("Can't find url hostname for {}".format(GET_IP_PAGE)) |
195 | 195 |
196 point = endpoints.TCP4ClientEndpoint(reactor, url.hostname, port) | 196 point = endpoints.TCP4ClientEndpoint(reactor, url.hostname, port) |
197 | 197 |
198 def gotConnection(p): | 198 def gotConnection(p): |
199 local_ip = p.transport.getHost().host | 199 local_ip = p.transport.getHost().host |
255 defer.returnValue(addresses or localhost) | 255 defer.returnValue(addresses or localhost) |
256 | 256 |
257 try: | 257 try: |
258 ip_tuple = yield self._getIPFromExternal(GET_IP_PAGE) | 258 ip_tuple = yield self._getIPFromExternal(GET_IP_PAGE) |
259 except (internet_error.DNSLookupError, internet_error.TimeoutError): | 259 except (internet_error.DNSLookupError, internet_error.TimeoutError): |
260 log.warning(u"Can't access Domain Name System") | 260 log.warning("Can't access Domain Name System") |
261 defer.returnValue(addresses or localhost) | 261 defer.returnValue(addresses or localhost) |
262 self._insertFirst(addresses, ip_tuple.local) | 262 self._insertFirst(addresses, ip_tuple.local) |
263 defer.returnValue(addresses) | 263 defer.returnValue(addresses) |
264 | 264 |
265 @defer.inlineCallbacks | 265 @defer.inlineCallbacks |
272 defer.returnValue(self._external_ip_cache) | 272 defer.returnValue(self._external_ip_cache) |
273 | 273 |
274 # we first try with XEP-0279 | 274 # we first try with XEP-0279 |
275 ip_check = yield self.host.hasFeature(client, NS_IP_CHECK) | 275 ip_check = yield self.host.hasFeature(client, NS_IP_CHECK) |
276 if ip_check: | 276 if ip_check: |
277 log.debug(u"Server IP Check available, we use it to retrieve our IP") | 277 log.debug("Server IP Check available, we use it to retrieve our IP") |
278 iq_elt = client.IQ("get") | 278 iq_elt = client.IQ("get") |
279 iq_elt['to'] = client.host | |
279 iq_elt.addElement((NS_IP_CHECK, "address")) | 280 iq_elt.addElement((NS_IP_CHECK, "address")) |
280 try: | 281 try: |
281 result_elt = yield iq_elt.send() | 282 result_elt = yield iq_elt.send() |
282 address_elt = result_elt.elements(NS_IP_CHECK, "address").next() | 283 address_elt = next(result_elt.elements(NS_IP_CHECK, "address")) |
283 ip_elt = address_elt.elements(NS_IP_CHECK, "ip").next() | 284 ip_elt = next(address_elt.elements(NS_IP_CHECK, "ip")) |
284 except StopIteration: | 285 except StopIteration: |
285 log.warning( | 286 log.warning( |
286 u"Server returned invalid result on XEP-0279 request, we ignore it" | 287 "Server returned invalid result on XEP-0279 request, we ignore it" |
287 ) | 288 ) |
288 except StanzaError as e: | 289 except StanzaError as e: |
289 log.warning(u"error while requesting ip to server: {}".format(e)) | 290 log.warning("error while requesting ip to server: {}".format(e)) |
290 else: | 291 else: |
291 # FIXME: server IP may not be the same as external IP (server can be on local machine or network) | 292 # FIXME: server IP may not be the same as external IP (server can be on local machine or network) |
292 # IP should be checked to see if we have a local one, and rejected in this case | 293 # IP should be checked to see if we have a local one, and rejected in this case |
293 external_ip = str(ip_elt) | 294 external_ip = str(ip_elt) |
294 log.debug(u"External IP found: {}".format(external_ip)) | 295 log.debug("External IP found: {}".format(external_ip)) |
295 self._external_ip_cache = external_ip | 296 self._external_ip_cache = external_ip |
296 defer.returnValue(self._external_ip_cache) | 297 defer.returnValue(self._external_ip_cache) |
297 | 298 |
298 # then with NAT-Port | 299 # then with NAT-Port |
299 if self._nat is not None: | 300 if self._nat is not None: |
303 defer.returnValue(nat_ip) | 304 defer.returnValue(nat_ip) |
304 | 305 |
305 # and finally by requesting external website | 306 # and finally by requesting external website |
306 allow_get_ip = yield self._externalAllowed(client) | 307 allow_get_ip = yield self._externalAllowed(client) |
307 try: | 308 try: |
308 ip = (yield webclient.getPage(GET_IP_PAGE)) if allow_get_ip else None | 309 ip = ((yield webclient.getPage(GET_IP_PAGE.encode('utf-8'))) |
310 if allow_get_ip else None) | |
309 except (internet_error.DNSLookupError, internet_error.TimeoutError): | 311 except (internet_error.DNSLookupError, internet_error.TimeoutError): |
310 log.warning(u"Can't access Domain Name System") | 312 log.warning("Can't access Domain Name System") |
311 ip = None | 313 ip = None |
312 except web_error.Error as e: | 314 except web_error.Error as e: |
313 log.warning( | 315 log.warning( |
314 u"Error while retrieving IP on {url}: {message}".format( | 316 "Error while retrieving IP on {url}: {message}".format( |
315 url=GET_IP_PAGE, message=e | 317 url=GET_IP_PAGE, message=e |
316 ) | 318 ) |
317 ) | 319 ) |
318 ip = None | 320 ip = None |
319 else: | 321 else: |
320 self._external_ip_cache = ip | 322 self._external_ip_cache = ip |
321 defer.returnValue(ip) | 323 defer.returnValue(ip) |
322 | 324 |
323 | 325 |
326 @implementer(iwokkel.IDisco) | |
324 class IPPlugin_handler(XMPPHandler): | 327 class IPPlugin_handler(XMPPHandler): |
325 implements(iwokkel.IDisco) | |
326 | 328 |
327 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | 329 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): |
328 return [disco.DiscoFeature(NS_IP_CHECK)] | 330 return [disco.DiscoFeature(NS_IP_CHECK)] |
329 | 331 |
330 def getDiscoItems(self, requestor, target, nodeIdentifier=""): | 332 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |