From c79636863d8bae013e929c0ab842fd40d0cd13c2 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 5 Dec 2019 18:17:35 +0200 Subject: [PATCH 01/13] If tray is not available, disable settings group "Tray" and related checkboxes; set checkBoxMinimizeToTray to false by default --- src/bitmessageqt/settings.py | 39 +++++++++++++++++++++++++----------- src/bitmessageqt/settings.ui | 2 +- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 982328cc..7fb0ea91 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -47,20 +47,33 @@ class SettingsDialog(QtGui.QDialog): def adjust_from_config(self, config): """Adjust all widgets state according to config settings""" # pylint: disable=too-many-branches,too-many-statements - self.checkBoxStartOnLogon.setChecked( - config.getboolean('bitmessagesettings', 'startonlogon')) - self.checkBoxMinimizeToTray.setChecked( - config.getboolean('bitmessagesettings', 'minimizetotray')) - self.checkBoxTrayOnClose.setChecked( - config.safeGetBoolean('bitmessagesettings', 'trayonclose')) + if not self.parent.tray.isSystemTrayAvailable(): + self.groupBoxTray.setEnabled(False) + self.groupBoxTray.setTitle(_translate( + "MainWindow", "Tray (not available in your system)")) + for setting in ( + 'minimizetotray', 'trayonclose', 'startintray'): + config.set('bitmessagesettings', setting, 'false') + else: + self.checkBoxMinimizeToTray.setChecked( + config.getboolean('bitmessagesettings', 'minimizetotray')) + self.checkBoxTrayOnClose.setChecked( + config.safeGetBoolean('bitmessagesettings', 'trayonclose')) + self.checkBoxStartInTray.setChecked( + config.getboolean('bitmessagesettings', 'startintray')) + self.checkBoxHideTrayConnectionNotifications.setChecked( - config.getboolean("bitmessagesettings", "hidetrayconnectionnotifications")) + config.getboolean( + 'bitmessagesettings', 'hidetrayconnectionnotifications')) self.checkBoxShowTrayNotifications.setChecked( config.getboolean('bitmessagesettings', 'showtraynotifications')) - self.checkBoxStartInTray.setChecked( - config.getboolean('bitmessagesettings', 'startintray')) + + self.checkBoxStartOnLogon.setChecked( + config.getboolean('bitmessagesettings', 'startonlogon')) + self.checkBoxWillinglySendToMobile.setChecked( - config.safeGetBoolean('bitmessagesettings', 'willinglysendtomobile')) + config.safeGetBoolean( + 'bitmessagesettings', 'willinglysendtomobile')) self.checkBoxUseIdenticons.setChecked( config.safeGetBoolean('bitmessagesettings', 'useidenticons')) self.checkBoxReplyBelow.setChecked( @@ -82,10 +95,12 @@ class SettingsDialog(QtGui.QDialog): "MainWindow", "Start-on-login not yet supported on your OS.")) self.checkBoxMinimizeToTray.setDisabled(True) self.checkBoxMinimizeToTray.setText(_translate( - "MainWindow", "Minimize-to-tray not yet supported on your OS.")) + "MainWindow", + "Minimize-to-tray not yet supported on your OS.")) self.checkBoxShowTrayNotifications.setDisabled(True) self.checkBoxShowTrayNotifications.setText(_translate( - "MainWindow", "Tray notifications not yet supported on your OS.")) + "MainWindow", + "Tray notifications not yet supported on your OS.")) elif 'linux' in sys.platform: self.checkBoxStartOnLogon.setDisabled(True) self.checkBoxStartOnLogon.setText(_translate( diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 963f2e64..33278e94 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -75,7 +75,7 @@ Minimize to tray - true + false From 5a35de6bca4385431905be5a59f004f7bfd2896c Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 20 Dec 2019 15:20:14 +0200 Subject: [PATCH 02/13] Fix sendOnionPeerObj() broken in 9923e97 --- src/class_singleWorker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index a275b79d..d035c092 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -467,8 +467,8 @@ class singleWorker(StoppableThread): def sendOnionPeerObj(self, peer=None): """Send onionpeer object representing peer""" if not peer: # find own onionhostname - for peer_ in state.ownAddresses: - if peer_.host.endswith('.onion'): + for peer in state.ownAddresses: + if peer.host.endswith('.onion'): break else: return From 03316496b7c3380c5ac408f86d049855dbcedac6 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 14 Dec 2019 12:53:51 +0200 Subject: [PATCH 03/13] Stop UDPSocket on socket.error 101 (Network is unreachable) --- src/network/udp.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/udp.py b/src/network/udp.py index cf694567..d8c5f340 100644 --- a/src/network/udp.py +++ b/src/network/udp.py @@ -146,6 +146,9 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes retval = self.socket.sendto( self.write_buf, ('', self.port)) except socket.error as e: - logger.error("socket error on sendato: %s", e) + logger.error("socket error on sendto: %s", e) + if e.errno == 101: + self.announcing = False + self.socket.close() retval = 0 self.slice_write_buf(retval) From 9119507b033372cdca432eb45c3d3d897455bd4b Mon Sep 17 00:00:00 2001 From: sandakersmann Date: Fri, 27 Dec 2019 18:23:02 +0100 Subject: [PATCH 04/13] Changed copyright year to 2020 --- COPYING | 2 +- LICENSE | 2 +- src/api.py | 2 +- src/bitmessagemain.py | 2 +- src/bitmessageqt/about.ui | 2 +- src/bitmessageqt/dialogs.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/COPYING b/COPYING index 2e0ae6c1..078bf213 100644 --- a/COPYING +++ b/COPYING @@ -1,5 +1,5 @@ Copyright (c) 2012-2016 Jonathan Warren -Copyright (c) 2012-2019 The Bitmessage Developers +Copyright (c) 2012-2020 The Bitmessage Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LICENSE b/LICENSE index 3be738c0..6bb86242 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) Copyright (c) 2012-2016 Jonathan Warren -Copyright (c) 2012-2019 The Bitmessage Developers +Copyright (c) 2012-2020 The Bitmessage Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/src/api.py b/src/api.py index f9d0a518..3201fba5 100644 --- a/src/api.py +++ b/src/api.py @@ -2,7 +2,7 @@ # pylint: disable=too-many-statements # Copyright (c) 2012-2016 Jonathan Warren -# Copyright (c) 2012-2019 The Bitmessage developers +# Copyright (c) 2012-2020 The Bitmessage developers """ This is not what you run to run the Bitmessage API. Instead, enable the API diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 176125cc..96885b5e 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -3,7 +3,7 @@ The PyBitmessage startup script """ # Copyright (c) 2012-2016 Jonathan Warren -# Copyright (c) 2012-2019 The Bitmessage developers +# Copyright (c) 2012-2020 The Bitmessage developers # Distributed under the MIT/X11 software license. See the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/bitmessageqt/about.ui b/src/bitmessageqt/about.ui index a912927a..7073bbd1 100644 --- a/src/bitmessageqt/about.ui +++ b/src/bitmessageqt/about.ui @@ -46,7 +46,7 @@ - <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2012-2019 The Bitmessage Developers</p></body></html> + <html><head/><body><p>Copyright © 2012-2016 Jonathan Warren<br/>Copyright © 2012-2020 The Bitmessage Developers</p></body></html> Qt::AlignLeft diff --git a/src/bitmessageqt/dialogs.py b/src/bitmessageqt/dialogs.py index b4bcd2fd..c667edb1 100644 --- a/src/bitmessageqt/dialogs.py +++ b/src/bitmessageqt/dialogs.py @@ -47,7 +47,7 @@ class AboutDialog(QtGui.QDialog, RetranslateMixin): try: self.label_2.setText( self.label_2.text().replace( - '2019', str(last_commit.get('time').year) + '2020', str(last_commit.get('time').year) )) except AttributeError: pass From 168c4a5696510bb3c6256b519352a6b0413dd713 Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Tue, 3 Dec 2019 12:59:27 +0100 Subject: [PATCH 05/13] Checkdeps fix - an exception can be triggered if too many requirements are fulfilled --- checkdeps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checkdeps.py b/checkdeps.py index 03782037..45dc2fc9 100755 --- a/checkdeps.py +++ b/checkdeps.py @@ -164,7 +164,7 @@ if (not compiler or prereqs) and OPSYS in PACKAGE_MANAGER: if not compiler: compilerToPackages() prereqToPackages() - if mandatory: + if prereqs and mandatory: sys.exit(1) else: print("All the dependencies satisfied, you can install PyBitmessage") From c35f48bd0bdd7581571be6ee111d6530c00246b8 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sun, 22 Sep 2019 15:35:10 +0300 Subject: [PATCH 06/13] Fix setting socksproxytype and return in proxyconfig_stem: socksproxytype is set to SOCKS5 temporary if proxyconfig succeeds. --- src/plugins/proxyconfig_stem.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/proxyconfig_stem.py b/src/plugins/proxyconfig_stem.py index bdbfe8ca..e56aecc6 100644 --- a/src/plugins/proxyconfig_stem.py +++ b/src/plugins/proxyconfig_stem.py @@ -43,6 +43,7 @@ def connect_plugin(config): # pylint: disable=too-many-branches ): # remote proxy is choosen for outbound connections, # nothing to do here, but need to set socksproxytype to SOCKS5! + config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5') logwrite( 'sockshostname is set to remote address,' ' aborting stem proxy configuration') @@ -77,6 +78,8 @@ def connect_plugin(config): # pylint: disable=too-many-branches logwrite('Started tor on port %s' % port) break + config.setTemp('bitmessagesettings', 'socksproxytype', 'SOCKS5') + if config.safeGetBoolean('bitmessagesettings', 'sockslisten'): # need a hidden service for inbound connections try: @@ -117,6 +120,5 @@ def connect_plugin(config): # pylint: disable=too-many-branches config.set( onionhostname, 'keytype', response.private_key_type) config.save() - config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5') - return True + return True From 44cb975a611dcca0f40f328f44f517962f013a52 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 18 Oct 2019 17:52:00 +0300 Subject: [PATCH 07/13] Fixed bug in plugin.get_plugins(), edited docstrings --- src/plugins/__init__.py | 7 +++++++ src/plugins/plugin.py | 27 ++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/plugins/__init__.py b/src/plugins/__init__.py index e69de29b..285009df 100644 --- a/src/plugins/__init__.py +++ b/src/plugins/__init__.py @@ -0,0 +1,7 @@ +""" +Simple plugin system based on setuptools +---------------------------------------- + + +""" +# .. include:: pybitmessage.plugins.plugin.rst diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py index e671a73f..629de0a6 100644 --- a/src/plugins/plugin.py +++ b/src/plugins/plugin.py @@ -1,19 +1,28 @@ # -*- coding: utf-8 -*- """ -src/plugins/plugin.py -=================================== +Operating with plugins """ +import logging + import pkg_resources +logger = logging.getLogger('default') + + def get_plugins(group, point='', name=None, fallback=None): """ - Iterate through plugins (`connect_plugin` attribute of entry point) - which name starts with `point` or equals to `name`. - If `fallback` kwarg specified, plugin with that name yield last. + :param str group: plugin group + :param str point: plugin name prefix + :param name: exact plugin name + :param fallback: fallback plugin name + + Iterate through plugins (``connect_plugin`` attribute of entry point) + which name starts with ``point`` or equals to ``name``. + If ``fallback`` kwarg specified, plugin with that name yield last. """ for ep in pkg_resources.iter_entry_points('bitmessage.' + group): - if name and ep.name == name or ep.name.startswith(point): + if name and ep.name == name or not point or ep.name.startswith(point): try: plugin = ep.load().connect_plugin if ep.name == fallback: @@ -25,6 +34,8 @@ def get_plugins(group, point='', name=None, fallback=None): ValueError, pkg_resources.DistributionNotFound, pkg_resources.UnknownExtra): + logger.debug( + 'Problem while loading %s', ep.name, exc_info=True) continue try: yield _fallback @@ -33,6 +44,8 @@ def get_plugins(group, point='', name=None, fallback=None): def get_plugin(*args, **kwargs): - """Returns first available plugin `from get_plugins()` if any.""" + """ + :return: first available plugin from :func:`get_plugins` if any. + """ for plugin in get_plugins(*args, **kwargs): return plugin From 5160a68c28a3493e4c0bb55a4169cef350e2058e Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 18 Oct 2019 17:52:44 +0300 Subject: [PATCH 08/13] Moved start_proxyconfig to helper_startup; no more prints in helper_startup --- src/bitmessagemain.py | 32 ++++++---------------------- src/helper_startup.py | 49 ++++++++++++++++++++++++++++++++++++------- src/tests/core.py | 9 ++++---- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 96885b5e..48ed9738 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -39,7 +39,8 @@ import shutdown from bmconfigparser import BMConfigParser from debug import logger # this should go before any threads from helper_startup import ( - isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections + isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections, + start_proxyconfig ) from inventory import Inventory from knownnodes import readKnownNodes @@ -168,30 +169,9 @@ def signal_handler(signum, frame): class Main(object): """Main PyBitmessage class""" - @staticmethod - def start_proxyconfig(config): - """Check socksproxytype and start any proxy configuration plugin""" - proxy_type = config.safeGet('bitmessagesettings', 'socksproxytype') - if proxy_type not in ('none', 'SOCKS4a', 'SOCKS5'): - # pylint: disable=relative-import - from plugins.plugin import get_plugin - try: - proxyconfig_start = time.time() - if not get_plugin('proxyconfig', name=proxy_type)(config): - raise TypeError - except TypeError: - logger.error( - 'Failed to run proxy config plugin %s', - proxy_type, exc_info=True) - shutdown.doCleanShutdown() - sys.exit(2) - else: - logger.info( - 'Started proxy config plugin %s in %s sec', - proxy_type, time.time() - proxyconfig_start) - - def start(self): # pylint: disable=too-many-statements, too-many-branches, too-many-locals + def start(self): """Start main application""" + # pylint: disable=too-many-statements,too-many-branches,too-many-locals _fixSocket() config = BMConfigParser() @@ -350,7 +330,7 @@ class Main(object): # start network components if networking is enabled if state.enableNetwork: - self.start_proxyconfig(config) + start_proxyconfig() BMConnectionPool() asyncoreThread = BMNetworkThread() asyncoreThread.daemon = True @@ -410,7 +390,7 @@ class Main(object): self.stop() elif not state.enableGUI: from tests import core as test_core # pylint: disable=relative-import - test_core_result = test_core.run(self) + test_core_result = test_core.run() state.enableGUI = True self.stop() test_core.cleanup() diff --git a/src/helper_startup.py b/src/helper_startup.py index 9aaad5ef..9711c339 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -2,11 +2,12 @@ Startup operations. """ # pylint: disable=too-many-branches,too-many-statements -from __future__ import print_function +import logging import os import platform import sys +import time from distutils.version import StrictVersion import defaults @@ -15,6 +16,13 @@ import paths import state from bmconfigparser import BMConfigParser +try: + from plugins.plugin import get_plugin +except ImportError: + get_plugin = None + + +logger = logging.getLogger('default') # The user may de-select Portable Mode in the settings if they want # the config files to stay in the application data folder. @@ -30,14 +38,14 @@ def loadConfig(): needToCreateKeysFile = config.safeGet( 'bitmessagesettings', 'settingsversion') is None if not needToCreateKeysFile: - print( + logger.info( 'Loading config files from directory specified' - ' on startup: %s' % state.appdata) + ' on startup: %s', state.appdata) else: config.read(paths.lookupExeFolder() + 'keys.dat') try: config.get('bitmessagesettings', 'settingsversion') - print('Loading config files from same directory as program.') + logger.info('Loading config files from same directory as program.') needToCreateKeysFile = False state.appdata = paths.lookupExeFolder() except: @@ -48,7 +56,8 @@ def loadConfig(): needToCreateKeysFile = config.safeGet( 'bitmessagesettings', 'settingsversion') is None if not needToCreateKeysFile: - print('Loading existing config files from', state.appdata) + logger.info( + 'Loading existing config files from %s', state.appdata) if needToCreateKeysFile: @@ -103,9 +112,10 @@ def loadConfig(): # Just use the same directory as the program and forget about # the appdata folder state.appdata = '' - print('Creating new config files in same directory as program.') + logger.info( + 'Creating new config files in same directory as program.') else: - print('Creating new config files in', state.appdata) + logger.info('Creating new config files in %s', state.appdata) if not os.path.exists(state.appdata): os.makedirs(state.appdata) if not sys.platform.startswith('win'): @@ -255,7 +265,7 @@ def updateConfig(): 'bitmessagesettings', 'hidetrayconnectionnotifications', 'false') if config.safeGetInt('bitmessagesettings', 'maxoutboundconnections') < 1: config.set('bitmessagesettings', 'maxoutboundconnections', '8') - print('WARNING: your maximum outbound connections must be a number.') + logger.warning('Your maximum outbound connections must be a number.') # TTL is now user-specifiable. Let's add an option to save # whatever the user selects. @@ -278,3 +288,26 @@ def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections(): return False except Exception: pass + + +def start_proxyconfig(): + """Check socksproxytype and start any proxy configuration plugin""" + if not get_plugin: + return + config = BMConfigParser() + proxy_type = config.safeGet('bitmessagesettings', 'socksproxytype') + if proxy_type and proxy_type not in ('none', 'SOCKS4a', 'SOCKS5'): + try: + proxyconfig_start = time.time() + if not get_plugin('proxyconfig', name=proxy_type)(config): + raise TypeError() + except TypeError: + # cannot import shutdown here ): + logger.error( + 'Failed to run proxy config plugin %s', + proxy_type, exc_info=True) + os._exit(0) # pylint: disable=protected-access + else: + logger.info( + 'Started proxy config plugin %s in %s sec', + proxy_type, time.time() - proxyconfig_start) diff --git a/src/tests/core.py b/src/tests/core.py index d2456064..84971dc5 100644 --- a/src/tests/core.py +++ b/src/tests/core.py @@ -14,6 +14,7 @@ import unittest import knownnodes import state from bmconfigparser import BMConfigParser +from helper_startup import start_proxyconfig from helper_msgcoding import MsgEncode, MsgDecode from network import asyncore_pollchoose as asyncore from network.connectionpool import BMConnectionPool @@ -21,8 +22,8 @@ from network.node import Peer from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection from queues import excQueue + knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat') -program = None def pickle_knownnodes(): @@ -196,14 +197,12 @@ class TestCore(unittest.TestCase): self._check_bootstrap() self._initiate_bootstrap() BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'stem') - program.start_proxyconfig(BMConfigParser()) + start_proxyconfig() self._check_bootstrap() -def run(prog): +def run(): """Starts all tests defined in this module""" - global program # pylint: disable=global-statement - program = prog loader = unittest.TestLoader() loader.sortTestMethodsUsing = None suite = loader.loadTestsFromTestCase(TestCore) From e3ccc3c7c8e4d3662adb8e20e20f1d8a32687e83 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Thu, 3 Oct 2019 18:34:27 +0300 Subject: [PATCH 09/13] Support for socksproxytype plugins in Settings dialog --- src/bitmessageqt/settings.py | 64 +++++++++++++++++++----------------- src/bitmessageqt/settings.ui | 4 +-- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 7fb0ea91..8fe1ecba 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -1,3 +1,4 @@ +import ConfigParser import os import sys @@ -16,6 +17,7 @@ import tempfile import widgets from bmconfigparser import BMConfigParser from helper_sql import sqlExecute, sqlStoredProcedure +from helper_startup import start_proxyconfig from network.asyncore_pollchoose import set_rates from tr import _translate @@ -32,6 +34,16 @@ class SettingsDialog(QtGui.QDialog): self.net_restart_needed = False self.timer = QtCore.QTimer() + try: + import pkg_resources + except ImportError: + pass + else: + # Append proxy types defined in plugins + for ep in pkg_resources.iter_entry_points( + 'bitmessage.proxyconfig'): + self.comboBoxProxyType.addItem(ep.name) + self.lineEditMaxOutboundConnections.setValidator( QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections)) @@ -117,21 +129,16 @@ class SettingsDialog(QtGui.QDialog): self.checkBoxOnionOnly.setChecked( config.safeGetBoolean('bitmessagesettings', 'onionservicesonly')) - proxy_type = config.safeGet( - 'bitmessagesettings', 'socksproxytype', 'none') - if proxy_type == 'none': - self.comboBoxProxyType.setCurrentIndex(0) - self.lineEditSocksHostname.setEnabled(False) - self.lineEditSocksPort.setEnabled(False) - self.lineEditSocksUsername.setEnabled(False) - self.lineEditSocksPassword.setEnabled(False) - self.checkBoxAuthentication.setEnabled(False) - self.checkBoxSocksListen.setEnabled(False) - self.checkBoxOnionOnly.setEnabled(False) - elif proxy_type == 'SOCKS4a': - self.comboBoxProxyType.setCurrentIndex(1) - elif proxy_type == 'SOCKS5': - self.comboBoxProxyType.setCurrentIndex(2) + try: + # Get real value, not temporary + self._proxy_type = ConfigParser.SafeConfigParser.get( + config, 'bitmessagesettings', 'socksproxytype') + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + self._proxy_type = 'none' + self.comboBoxProxyType.setCurrentIndex( + 0 if self._proxy_type == 'none' + else self.comboBoxProxyType.findText(self._proxy_type)) + self.comboBoxProxyTypeChanged(self.comboBoxProxyType.currentIndex()) self.lineEditSocksHostname.setText( config.get('bitmessagesettings', 'sockshostname')) @@ -219,7 +226,7 @@ class SettingsDialog(QtGui.QDialog): self.checkBoxAuthentication.setEnabled(False) self.checkBoxSocksListen.setEnabled(False) self.checkBoxOnionOnly.setEnabled(False) - elif comboBoxIndex in (1, 2): + else: self.lineEditSocksHostname.setEnabled(True) self.lineEditSocksPort.setEnabled(True) self.checkBoxAuthentication.setEnabled(True) @@ -321,27 +328,22 @@ class SettingsDialog(QtGui.QDialog): upnpThread = upnp.uPnPThread() upnpThread.start() - proxy_type = self.config.safeGet( - 'bitmessagesettings', 'socksproxytype', 'none') - if ( - proxy_type == 'none' and - self.comboBoxProxyType.currentText()[0:5] == 'SOCKS' and - shared.statusIconColor != 'red' - ): - self.net_restart_needed = True - if ( - proxy_type[0:5] == 'SOCKS' and - self.comboBoxProxyType.currentText()[0:5] != 'SOCKS' - ): + proxytype_index = self.comboBoxProxyType.currentIndex() + if proxytype_index == 0: + if self._proxy_type != 'none' and shared.statusIconColor != 'red': + self.net_restart_needed = True + elif self.comboBoxProxyType.currentText() != self._proxy_type: self.net_restart_needed = True self.parent.statusbar.clearMessage() self.config.set( 'bitmessagesettings', 'socksproxytype', - str(self.comboBoxProxyType.currentText()) - if self.comboBoxProxyType.currentText()[0:5] == 'SOCKS' - else 'none' + 'none' if self.comboBoxProxyType.currentIndex() == 0 + else str(self.comboBoxProxyType.currentText()) ) + if proxytype_index > 2: # last literal proxytype in ui + start_proxyconfig() + self.config.set('bitmessagesettings', 'socksauthentication', str( self.checkBoxAuthentication.isChecked())) self.config.set('bitmessagesettings', 'sockshostname', str( diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 33278e94..0ffbf442 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -419,12 +419,12 @@ - SOCKS4a + SOCKS4a - SOCKS5 + SOCKS5 From 2bddae511a84fae6c4830ce7903e442c77de5673 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Mon, 21 Oct 2019 15:20:29 +0300 Subject: [PATCH 10/13] Fixed some mistakes in tor dependent tests and marked them for skipping until the finish of debug. --- requirements.txt | 1 - src/tests/core.py | 48 +++++++++++++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/requirements.txt b/requirements.txt index be429a9f..c55e5cf1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ python_prctl psutil pycrypto -stem diff --git a/src/tests/core.py b/src/tests/core.py index 84971dc5..d56076c3 100644 --- a/src/tests/core.py +++ b/src/tests/core.py @@ -14,14 +14,19 @@ import unittest import knownnodes import state from bmconfigparser import BMConfigParser -from helper_startup import start_proxyconfig from helper_msgcoding import MsgEncode, MsgDecode +from helper_startup import start_proxyconfig from network import asyncore_pollchoose as asyncore from network.connectionpool import BMConnectionPool from network.node import Peer from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection from queues import excQueue +try: + import stem.version as stem_version +except ImportError: + stem_version = None + knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat') @@ -171,12 +176,29 @@ class TestCore(unittest.TestCase): self.assertIsInstance(con, connection_base) self.assertNotEqual(peer.host, '127.0.0.1') return - else: # pylint: disable=useless-else-on-loop - self.fail( - 'Failed to connect during %s sec' % (time.time() - _started)) + self.fail( + 'Failed to connect during %s sec' % (time.time() - _started)) - def test_onionservicesonly(self): - """test onionservicesonly networking mode""" + def test_bootstrap(self): + """test bootstrapping""" + self._initiate_bootstrap() + self._check_bootstrap() + + @unittest.skipUnless(stem_version, 'No stem, skipping tor dependent test') + def test_bootstrap_tor(self): + """test bootstrapping with tor""" + self._initiate_bootstrap() + BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'stem') + start_proxyconfig() + self._check_bootstrap() + + @unittest.skipUnless(stem_version, 'No stem, skipping tor dependent test') + def test_onionservicesonly(self): # this should start after bootstrap + """ + set onionservicesonly, wait for 3 connections and check them all + are onions + """ + BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'SOCKS5') BMConfigParser().set('bitmessagesettings', 'onionservicesonly', 'true') self._initiate_bootstrap() BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') @@ -185,21 +207,15 @@ class TestCore(unittest.TestCase): for n, peer in enumerate(BMConnectionPool().outboundConnections): if n > 2: return - if not peer.host.endswith('.onion'): + if ( + not peer.host.endswith('.onion') + and not peer.host.startswith('bootstrap') + ): self.fail( 'Found non onion hostname %s in outbound connections!' % peer.host) self.fail('Failed to connect to at least 3 nodes within 360 sec') - def test_bootstrap(self): - """test bootstrapping""" - self._initiate_bootstrap() - self._check_bootstrap() - self._initiate_bootstrap() - BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'stem') - start_proxyconfig() - self._check_bootstrap() - def run(): """Starts all tests defined in this module""" From 52d5c1ff03707829b5055ef71015c0da369fb09d Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Tue, 22 Oct 2019 10:10:41 +0300 Subject: [PATCH 11/13] Document proxyconfig_stem --- src/plugins/proxyconfig_stem.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/plugins/proxyconfig_stem.py b/src/plugins/proxyconfig_stem.py index e56aecc6..cbc6395d 100644 --- a/src/plugins/proxyconfig_stem.py +++ b/src/plugins/proxyconfig_stem.py @@ -1,7 +1,15 @@ # -*- coding: utf-8 -*- """ -src/plugins/proxyconfig_stem.py -=================================== +Configure tor proxy and hidden service with +`stem `_ depending on *bitmessagesettings*: + + * try to start own tor instance on *socksport* if *sockshostname* + is unset or set to localhost; + * if *socksport* is already in use that instance is used only for + hidden service (if *sockslisten* is also set True); + * create ephemeral hidden service v3 if there is already *onionhostname*; + * otherwise use stem's 'BEST' version and save onion keys to the new + section using *onionhostname* as name for future use. """ import os import logging @@ -36,10 +44,16 @@ class DebugLogger(object): def connect_plugin(config): # pylint: disable=too-many-branches - """Run stem proxy configurator""" + """ + Run stem proxy configurator + + :param config: current configuration instance + :type config: :class:`pybitmessage.bmconfigparser.BMConfigParser` + :return: True if configuration was done successfully + """ logwrite = DebugLogger() - if config.safeGet('bitmessagesettings', 'sockshostname') not in ( - 'localhost', '127.0.0.1', '' + if config.safeGet('bitmessagesettings', 'sockshostname', '') not in ( + 'localhost', '127.0.0.1', '' ): # remote proxy is choosen for outbound connections, # nothing to do here, but need to set socksproxytype to SOCKS5! From 1c4d7655c3a58e067db4a872b747fe5a39c3c181 Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Sat, 7 Dec 2019 21:33:30 +0200 Subject: [PATCH 12/13] Fix socksproxytype in the support message, made that a separate function getSOCKSProxyType(config) in the settings. --- src/bitmessageqt/settings.py | 24 ++++++++++++++++-------- src/bitmessageqt/support.py | 4 ++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 8fe1ecba..011d38ed 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -22,6 +22,19 @@ from network.asyncore_pollchoose import set_rates from tr import _translate +def getSOCKSProxyType(config): + """Get user socksproxytype setting from *config*""" + try: + result = ConfigParser.SafeConfigParser.get( + config, 'bitmessagesettings', 'socksproxytype') + except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): + return + else: + if result.lower() in ('', 'none', 'false'): + result = None + return result + + class SettingsDialog(QtGui.QDialog): """The "Settings" dialog""" def __init__(self, parent=None, firstrun=False): @@ -129,14 +142,9 @@ class SettingsDialog(QtGui.QDialog): self.checkBoxOnionOnly.setChecked( config.safeGetBoolean('bitmessagesettings', 'onionservicesonly')) - try: - # Get real value, not temporary - self._proxy_type = ConfigParser.SafeConfigParser.get( - config, 'bitmessagesettings', 'socksproxytype') - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - self._proxy_type = 'none' + self._proxy_type = getSOCKSProxyType(config) self.comboBoxProxyType.setCurrentIndex( - 0 if self._proxy_type == 'none' + 0 if not self._proxy_type else self.comboBoxProxyType.findText(self._proxy_type)) self.comboBoxProxyTypeChanged(self.comboBoxProxyType.currentIndex()) @@ -330,7 +338,7 @@ class SettingsDialog(QtGui.QDialog): proxytype_index = self.comboBoxProxyType.currentIndex() if proxytype_index == 0: - if self._proxy_type != 'none' and shared.statusIconColor != 'red': + if self._proxy_type and shared.statusIconColor != 'red': self.net_restart_needed = True elif self.comboBoxProxyType.currentText() != self._proxy_type: self.net_restart_needed = True diff --git a/src/bitmessageqt/support.py b/src/bitmessageqt/support.py index 2a1ddb18..d6d4543d 100644 --- a/src/bitmessageqt/support.py +++ b/src/bitmessageqt/support.py @@ -15,6 +15,7 @@ from openclpow import openclAvailable, openclEnabled import paths import proofofwork from pyelliptic.openssl import OpenSSL +from settings import getSOCKSProxyType import queues import network.stats import state @@ -118,8 +119,7 @@ def createSupportMessage(myapp): BMConfigParser().safeGet('bitmessagesettings', 'opencl') ) if openclEnabled() else "None" locale = getTranslationLanguage() - socks = BMConfigParser().safeGet( - 'bitmessagesettings', 'socksproxytype', "N/A") + socks = getSOCKSProxyType(BMConfigParser()) or "N/A" upnp = BMConfigParser().safeGet('bitmessagesettings', 'upnp', "N/A") connectedhosts = len(network.stats.connectedHostsList()) From 61f64f72c36b315ef8663176d6cc2474af4457ea Mon Sep 17 00:00:00 2001 From: Dmitri Bogomolov <4glitch@gmail.com> Date: Fri, 20 Dec 2019 10:53:31 +0200 Subject: [PATCH 13/13] Fixing port for hidden service --- src/plugins/proxyconfig_stem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/proxyconfig_stem.py b/src/plugins/proxyconfig_stem.py index cbc6395d..494519f2 100644 --- a/src/plugins/proxyconfig_stem.py +++ b/src/plugins/proxyconfig_stem.py @@ -112,7 +112,8 @@ def connect_plugin(config): # pylint: disable=too-many-branches onionkeytype = config.safeGet(onionhostname, 'keytype') response = controller.create_ephemeral_hidden_service( - config.safeGetInt('bitmessagesettings', 'onionport', 8444), + {config.safeGetInt('bitmessagesettings', 'onionport', 8444): + config.safeGetInt('bitmessagesettings', 'port', 8444)}, key_type=(onionkeytype or 'NEW'), key_content=(onionkey or onionhostname and 'ED25519-V3' or 'BEST') )