commit after conflicts

This commit is contained in:
lakshyacis 2020-01-06 16:44:13 +05:30
parent 102ea32d28
commit e436b8ecf6
No known key found for this signature in database
GPG Key ID: D2C539C8EC63E9EB
24 changed files with 262 additions and 146 deletions

View File

@ -1,5 +1,5 @@
Copyright (c) 2012-2016 Jonathan Warren 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2012-2016 Jonathan Warren 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 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 this software and associated documentation files (the "Software"), to deal in

View File

@ -164,7 +164,7 @@ if (not compiler or prereqs) and OPSYS in PACKAGE_MANAGER:
if not compiler: if not compiler:
compilerToPackages() compilerToPackages()
prereqToPackages() prereqToPackages()
if mandatory: if prereqs and mandatory:
sys.exit(1) sys.exit(1)
else: else:
print("All the dependencies satisfied, you can install PyBitmessage") print("All the dependencies satisfied, you can install PyBitmessage")

View File

@ -1,4 +1,3 @@
python_prctl python_prctl
psutil psutil
pycrypto pycrypto
stem

View File

@ -30,7 +30,8 @@ import queues
import shared import shared
import shutdown import shutdown
import state import state
from addresses import addBMIfNotPresent, calculateInventoryHash, decodeAddress, decodeVarint, varintDecodeError
from addresses import addBMIfNotPresent, calculateInventoryHash, decodeAddress, decodeVarint, varintDecodeError
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from helper_ackPayload import genAckPayload from helper_ackPayload import genAckPayload

View File

@ -1303,6 +1303,7 @@ class NavigateApp(App): # pylint: disable=too-many-public-methods
def build(self): def build(self):
"""Method builds the widget""" """Method builds the widget"""
print(os.path.join(os.path.dirname(__file__), 'main.kv'))
main_widget = Builder.load_file( main_widget = Builder.load_file(
os.path.join(os.path.dirname(__file__), 'main.kv')) os.path.join(os.path.dirname(__file__), 'main.kv'))
self.nav_drawer = Navigatorss() self.nav_drawer = Navigatorss()

View File

@ -3,7 +3,7 @@
The PyBitmessage startup script The PyBitmessage startup script
""" """
# Copyright (c) 2012-2016 Jonathan Warren # 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 # Distributed under the MIT/X11 software license. See the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -32,7 +32,8 @@ from bmconfigparser import BMConfigParser
# this should go before any threads # this should go before any threads
from debug import logger from debug import logger
from helper_startup import ( from helper_startup import (
isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections,
start_proxyconfig
) )
from inventory import Inventory from inventory import Inventory
from knownnodes import readKnownNodes from knownnodes import readKnownNodes
@ -188,10 +189,9 @@ class Main(object):
logger.info( logger.info(
'Started proxy config plugin %s in %s sec', 'Started proxy config plugin %s in %s sec',
proxy_type, time.time() - proxyconfig_start) proxy_type, time.time() - proxyconfig_start)
def start(self): def start(self):
"""Start main application""" """Start main application"""
# pylint: disable=too-many-statements, too-many-branches, too-many-locals # pylint: disable=too-many-statements,too-many-branches,too-many-locals
_fixSocket() _fixSocket()
config = BMConfigParser() config = BMConfigParser()
@ -268,11 +268,10 @@ class Main(object):
set_thread_name("PyBitmessage") set_thread_name("PyBitmessage")
state.dandelion = config.safeGetInt('network', 'dandelion') state.dandelion = config.safeGet('network', 'dandelion')
# dandelion requires outbound connections, without them, # dandelion requires outbound connections, without them,
# stem objects will get stuck forever # stem objects will get stuck forever
if state.dandelion and not config.safeGetBoolean( if state.dandelion and not (config.safeGet('bitmessagesettings', 'sendoutgoingconnections') == 'True'):
'bitmessagesettings', 'sendoutgoingconnections'):
state.dandelion = 0 state.dandelion = 0
if state.testmode or config.safeGetBoolean( if state.testmode or config.safeGetBoolean(
@ -350,7 +349,7 @@ class Main(object):
singleAPIThread.start() singleAPIThread.start()
# start network components if networking is enabled # start network components if networking is enabled
if state.enableNetwork: if state.enableNetwork:
self.start_proxyconfig(config) start_proxyconfig()
BMConnectionPool() BMConnectionPool()
asyncoreThread = BMNetworkThread() asyncoreThread = BMNetworkThread()
asyncoreThread.daemon = True asyncoreThread.daemon = True
@ -408,9 +407,8 @@ class Main(object):
if (state.testmode and time.time() - state.last_api_response >= 30): if (state.testmode and time.time() - state.last_api_response >= 30):
self.stop() self.stop()
elif not state.enableGUI: elif not state.enableGUI:
# pylint: disable=relative-import from tests import core as test_core # pylint: disable=relative-import
from tests import core as test_core test_core_result = test_core.run()
test_core_result = test_core.run(self)
state.enableGUI = True state.enableGUI = True
self.stop() self.stop()
test_core.cleanup() test_core.cleanup()

View File

@ -46,7 +46,7 @@
<item alignment="Qt::AlignLeft"> <item alignment="Qt::AlignLeft">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copyright © 2012-2016 Jonathan Warren&lt;br/&gt;Copyright © 2012-2019 The Bitmessage Developers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copyright © 2012-2016 Jonathan Warren&lt;br/&gt;Copyright © 2012-2020 The Bitmessage Developers&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeft</set> <set>Qt::AlignLeft</set>

View File

@ -47,7 +47,7 @@ class AboutDialog(QtGui.QDialog, RetranslateMixin):
try: try:
self.label_2.setText( self.label_2.setText(
self.label_2.text().replace( self.label_2.text().replace(
'2019', str(last_commit.get('time').year) '2020', str(last_commit.get('time').year)
)) ))
except AttributeError: except AttributeError:
pass pass

View File

@ -1,3 +1,4 @@
import ConfigParser
import os import os
import sys import sys
@ -16,10 +17,24 @@ import tempfile
import widgets import widgets
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from helper_sql import sqlExecute, sqlStoredProcedure from helper_sql import sqlExecute, sqlStoredProcedure
from helper_startup import start_proxyconfig
from network.asyncore_pollchoose import set_rates from network.asyncore_pollchoose import set_rates
from tr import _translate 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): class SettingsDialog(QtGui.QDialog):
"""The "Settings" dialog""" """The "Settings" dialog"""
def __init__(self, parent=None, firstrun=False): def __init__(self, parent=None, firstrun=False):
@ -32,6 +47,16 @@ class SettingsDialog(QtGui.QDialog):
self.net_restart_needed = False self.net_restart_needed = False
self.timer = QtCore.QTimer() 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( self.lineEditMaxOutboundConnections.setValidator(
QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections)) QtGui.QIntValidator(0, 8, self.lineEditMaxOutboundConnections))
@ -47,20 +72,33 @@ class SettingsDialog(QtGui.QDialog):
def adjust_from_config(self, config): def adjust_from_config(self, config):
"""Adjust all widgets state according to config settings""" """Adjust all widgets state according to config settings"""
# pylint: disable=too-many-branches,too-many-statements # pylint: disable=too-many-branches,too-many-statements
self.checkBoxStartOnLogon.setChecked( if not self.parent.tray.isSystemTrayAvailable():
config.getboolean('bitmessagesettings', 'startonlogon')) self.groupBoxTray.setEnabled(False)
self.checkBoxMinimizeToTray.setChecked( self.groupBoxTray.setTitle(_translate(
config.getboolean('bitmessagesettings', 'minimizetotray')) "MainWindow", "Tray (not available in your system)"))
self.checkBoxTrayOnClose.setChecked( for setting in (
config.safeGetBoolean('bitmessagesettings', 'trayonclose')) '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( self.checkBoxHideTrayConnectionNotifications.setChecked(
config.getboolean("bitmessagesettings", "hidetrayconnectionnotifications")) config.getboolean(
'bitmessagesettings', 'hidetrayconnectionnotifications'))
self.checkBoxShowTrayNotifications.setChecked( self.checkBoxShowTrayNotifications.setChecked(
config.getboolean('bitmessagesettings', 'showtraynotifications')) config.getboolean('bitmessagesettings', 'showtraynotifications'))
self.checkBoxStartInTray.setChecked(
config.getboolean('bitmessagesettings', 'startintray')) self.checkBoxStartOnLogon.setChecked(
config.getboolean('bitmessagesettings', 'startonlogon'))
self.checkBoxWillinglySendToMobile.setChecked( self.checkBoxWillinglySendToMobile.setChecked(
config.safeGetBoolean('bitmessagesettings', 'willinglysendtomobile')) config.safeGetBoolean(
'bitmessagesettings', 'willinglysendtomobile'))
self.checkBoxUseIdenticons.setChecked( self.checkBoxUseIdenticons.setChecked(
config.safeGetBoolean('bitmessagesettings', 'useidenticons')) config.safeGetBoolean('bitmessagesettings', 'useidenticons'))
self.checkBoxReplyBelow.setChecked( self.checkBoxReplyBelow.setChecked(
@ -82,10 +120,12 @@ class SettingsDialog(QtGui.QDialog):
"MainWindow", "Start-on-login not yet supported on your OS.")) "MainWindow", "Start-on-login not yet supported on your OS."))
self.checkBoxMinimizeToTray.setDisabled(True) self.checkBoxMinimizeToTray.setDisabled(True)
self.checkBoxMinimizeToTray.setText(_translate( 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.setDisabled(True)
self.checkBoxShowTrayNotifications.setText(_translate( 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: elif 'linux' in sys.platform:
self.checkBoxStartOnLogon.setDisabled(True) self.checkBoxStartOnLogon.setDisabled(True)
self.checkBoxStartOnLogon.setText(_translate( self.checkBoxStartOnLogon.setText(_translate(
@ -102,21 +142,11 @@ class SettingsDialog(QtGui.QDialog):
self.checkBoxOnionOnly.setChecked( self.checkBoxOnionOnly.setChecked(
config.safeGetBoolean('bitmessagesettings', 'onionservicesonly')) config.safeGetBoolean('bitmessagesettings', 'onionservicesonly'))
proxy_type = config.safeGet( self._proxy_type = getSOCKSProxyType(config)
'bitmessagesettings', 'socksproxytype', 'none') self.comboBoxProxyType.setCurrentIndex(
if proxy_type == 'none': 0 if not self._proxy_type
self.comboBoxProxyType.setCurrentIndex(0) else self.comboBoxProxyType.findText(self._proxy_type))
self.lineEditSocksHostname.setEnabled(False) self.comboBoxProxyTypeChanged(self.comboBoxProxyType.currentIndex())
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)
self.lineEditSocksHostname.setText( self.lineEditSocksHostname.setText(
config.get('bitmessagesettings', 'sockshostname')) config.get('bitmessagesettings', 'sockshostname'))
@ -204,7 +234,7 @@ class SettingsDialog(QtGui.QDialog):
self.checkBoxAuthentication.setEnabled(False) self.checkBoxAuthentication.setEnabled(False)
self.checkBoxSocksListen.setEnabled(False) self.checkBoxSocksListen.setEnabled(False)
self.checkBoxOnionOnly.setEnabled(False) self.checkBoxOnionOnly.setEnabled(False)
elif comboBoxIndex in (1, 2): else:
self.lineEditSocksHostname.setEnabled(True) self.lineEditSocksHostname.setEnabled(True)
self.lineEditSocksPort.setEnabled(True) self.lineEditSocksPort.setEnabled(True)
self.checkBoxAuthentication.setEnabled(True) self.checkBoxAuthentication.setEnabled(True)
@ -306,27 +336,22 @@ class SettingsDialog(QtGui.QDialog):
upnpThread = upnp.uPnPThread() upnpThread = upnp.uPnPThread()
upnpThread.start() upnpThread.start()
proxy_type = self.config.safeGet( proxytype_index = self.comboBoxProxyType.currentIndex()
'bitmessagesettings', 'socksproxytype', 'none') if proxytype_index == 0:
if ( if self._proxy_type and shared.statusIconColor != 'red':
proxy_type == 'none' and self.net_restart_needed = True
self.comboBoxProxyType.currentText()[0:5] == 'SOCKS' and elif self.comboBoxProxyType.currentText() != self._proxy_type:
shared.statusIconColor != 'red'
):
self.net_restart_needed = True
if (
proxy_type[0:5] == 'SOCKS' and
self.comboBoxProxyType.currentText()[0:5] != 'SOCKS'
):
self.net_restart_needed = True self.net_restart_needed = True
self.parent.statusbar.clearMessage() self.parent.statusbar.clearMessage()
self.config.set( self.config.set(
'bitmessagesettings', 'socksproxytype', 'bitmessagesettings', 'socksproxytype',
str(self.comboBoxProxyType.currentText()) 'none' if self.comboBoxProxyType.currentIndex() == 0
if self.comboBoxProxyType.currentText()[0:5] == 'SOCKS' else str(self.comboBoxProxyType.currentText())
else 'none'
) )
if proxytype_index > 2: # last literal proxytype in ui
start_proxyconfig()
self.config.set('bitmessagesettings', 'socksauthentication', str( self.config.set('bitmessagesettings', 'socksauthentication', str(
self.checkBoxAuthentication.isChecked())) self.checkBoxAuthentication.isChecked()))
self.config.set('bitmessagesettings', 'sockshostname', str( self.config.set('bitmessagesettings', 'sockshostname', str(

View File

@ -75,7 +75,7 @@
<string>Minimize to tray</string> <string>Minimize to tray</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -419,12 +419,12 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>SOCKS4a</string> <string notr="true">SOCKS4a</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>SOCKS5</string> <string notr="true">SOCKS5</string>
</property> </property>
</item> </item>
</widget> </widget>

View File

@ -15,6 +15,7 @@ from openclpow import openclAvailable, openclEnabled
import paths import paths
import proofofwork import proofofwork
from pyelliptic.openssl import OpenSSL from pyelliptic.openssl import OpenSSL
from settings import getSOCKSProxyType
import queues import queues
import network.stats import network.stats
import state import state
@ -118,8 +119,7 @@ def createSupportMessage(myapp):
BMConfigParser().safeGet('bitmessagesettings', 'opencl') BMConfigParser().safeGet('bitmessagesettings', 'opencl')
) if openclEnabled() else "None" ) if openclEnabled() else "None"
locale = getTranslationLanguage() locale = getTranslationLanguage()
socks = BMConfigParser().safeGet( socks = getSOCKSProxyType(BMConfigParser()) or "N/A"
'bitmessagesettings', 'socksproxytype', "N/A")
upnp = BMConfigParser().safeGet('bitmessagesettings', 'upnp', "N/A") upnp = BMConfigParser().safeGet('bitmessagesettings', 'upnp', "N/A")
connectedhosts = len(network.stats.connectedHostsList()) connectedhosts = len(network.stats.connectedHostsList())

View File

@ -141,13 +141,13 @@ class objectProcessor(threading.Thread):
# bypass nonce and time, retain object type/version/stream + body # bypass nonce and time, retain object type/version/stream + body
readPosition = 16 readPosition = 16
if data[readPosition:] in shared.ackdataForWhichImWatching: if bytes(data[readPosition:]) in shared.ackdataForWhichImWatching:
logger.info('This object is an acknowledgement bound for me.') logger.info('This object is an acknowledgement bound for me.')
del shared.ackdataForWhichImWatching[data[readPosition:]] del shared.ackdataForWhichImWatching[data[readPosition:]]
sqlExecute( sqlExecute(
'UPDATE sent SET status=?, lastactiontime=?' 'UPDATE sent SET status=?, lastactiontime=?'
' WHERE ackdata=?', ' WHERE ackdata=?',
'ackreceived', int(time.time()), data[readPosition:]) ' ackreceived', int(time.time()), data[readPosition:])
queues.UISignalQueue.put(( queues.UISignalQueue.put((
'updateSentItemStatusByAckdata', 'updateSentItemStatusByAckdata',
(data[readPosition:], (data[readPosition:],
@ -221,7 +221,7 @@ class objectProcessor(threading.Thread):
'the hash requested in this getpubkey request is: %s', 'the hash requested in this getpubkey request is: %s',
hexlify(requestedHash)) hexlify(requestedHash))
# if this address hash is one of mine # if this address hash is one of mine
if requestedHash in shared.myAddressesByHash: if bytes(requestedHash) in shared.myAddressesByHash:
myAddress = shared.myAddressesByHash[requestedHash] myAddress = shared.myAddressesByHash[requestedHash]
elif requestedAddressVersionNumber >= 4: elif requestedAddressVersionNumber >= 4:
requestedTag = data[readPosition:readPosition + 32] requestedTag = data[readPosition:readPosition + 32]
@ -233,7 +233,7 @@ class objectProcessor(threading.Thread):
logger.debug( logger.debug(
'the tag requested in this getpubkey request is: %s', 'the tag requested in this getpubkey request is: %s',
hexlify(requestedTag)) hexlify(requestedTag))
if requestedTag in shared.myAddressesByTag: if bytes(requestedTag) in shared.myAddressesByTag:
myAddress = shared.myAddressesByTag[requestedTag] myAddress = shared.myAddressesByTag[requestedTag]
if myAddress == '': if myAddress == '':
@ -328,7 +328,7 @@ class objectProcessor(threading.Thread):
dataToStore = data[20:readPosition] dataToStore = data[20:readPosition]
sha = hashlib.new('sha512') sha = hashlib.new('sha512')
sha.update( sha.update(
'\x04' + publicSigningKey + '\x04' + publicEncryptionKey) '\x04'.encode() + publicSigningKey + '\x04'.encode() + publicEncryptionKey)
ripe = RIPEMD160Hash(sha.digest()).digest() ripe = RIPEMD160Hash(sha.digest()).digest()
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
@ -367,9 +367,9 @@ class objectProcessor(threading.Thread):
' Sanity check failed.') ' Sanity check failed.')
return return
readPosition += 4 readPosition += 4
publicSigningKey = '\x04' + data[readPosition:readPosition + 64] publicSigningKey = ('\x04').encode() + data[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
publicEncryptionKey = '\x04' + data[readPosition:readPosition + 64] publicEncryptionKey = ('\x04').encode() + data[readPosition:readPosition + 64]
readPosition += 64 readPosition += 64
_, specifiedNonceTrialsPerByteLength = decodeVarint( _, specifiedNonceTrialsPerByteLength = decodeVarint(
data[readPosition:readPosition + 10]) data[readPosition:readPosition + 10])
@ -433,7 +433,7 @@ class objectProcessor(threading.Thread):
return return
tag = data[readPosition:readPosition + 32] tag = data[readPosition:readPosition + 32]
if tag not in state.neededPubkeys: if tag not in bytes(state.neededPubkeys):
logger.info( logger.info(
'We don\'t need this v4 pubkey. We didn\'t ask for it.') 'We don\'t need this v4 pubkey. We didn\'t ask for it.')
return return
@ -866,7 +866,7 @@ class objectProcessor(threading.Thread):
elif broadcastVersion == 5: elif broadcastVersion == 5:
embeddedTag = data[readPosition:readPosition + 32] embeddedTag = data[readPosition:readPosition + 32]
readPosition += 32 readPosition += 32
if embeddedTag not in shared.MyECSubscriptionCryptorObjects: if bytes(embeddedTag) not in shared.MyECSubscriptionCryptorObjects:
logger.debug('We\'re not interested in this broadcast.') logger.debug('We\'re not interested in this broadcast.')
return return
# We are interested in this broadcast because of its tag. # We are interested in this broadcast because of its tag.

View File

@ -471,8 +471,8 @@ class singleWorker(StoppableThread):
def sendOnionPeerObj(self, peer=None): def sendOnionPeerObj(self, peer=None):
"""Send onionpeer object representing peer""" """Send onionpeer object representing peer"""
if not peer: # find own onionhostname if not peer: # find own onionhostname
for peer_ in state.ownAddresses: for peer in state.ownAddresses:
if peer_.host.endswith('.onion'): if peer.host.endswith('.onion'):
break break
else: else:
return return

View File

@ -2,11 +2,12 @@
Startup operations. Startup operations.
""" """
# pylint: disable=too-many-branches,too-many-statements # pylint: disable=too-many-branches,too-many-statements
from __future__ import print_function # import configparser
import logging
import os import os
import platform import platform
import sys import sys
import time
from distutils.version import StrictVersion from distutils.version import StrictVersion
import defaults import defaults
@ -15,6 +16,13 @@ import paths
import state import state
from bmconfigparser import BMConfigParser 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 user may de-select Portable Mode in the settings if they want
# the config files to stay in the application data folder. # the config files to stay in the application data folder.
@ -31,14 +39,14 @@ def loadConfig():
needToCreateKeysFile = config.safeGet( needToCreateKeysFile = config.safeGet(
'bitmessagesettings', 'settingsversion') is None 'bitmessagesettings', 'settingsversion') is None
if not needToCreateKeysFile: if not needToCreateKeysFile:
print( logger.info(
'Loading config files from directory specified' 'Loading config files from directory specified'
' on startup: %s' % state.appdata) ' on startup: %s', state.appdata)
else: else:
config.read(paths.lookupExeFolder() + 'keys.dat') config.read(paths.lookupExeFolder() + 'keys.dat')
try: try:
config.get('bitmessagesettings', 'settingsversion') 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 needToCreateKeysFile = False
state.appdata = paths.lookupExeFolder() state.appdata = paths.lookupExeFolder()
except: except:
@ -49,7 +57,8 @@ def loadConfig():
needToCreateKeysFile = config.safeGet( needToCreateKeysFile = config.safeGet(
'bitmessagesettings', 'settingsversion') is None 'bitmessagesettings', 'settingsversion') is None
if not needToCreateKeysFile: if not needToCreateKeysFile:
print('Loading existing config files from', state.appdata) logger.info(
'Loading existing config files from %s', state.appdata)
if needToCreateKeysFile: if needToCreateKeysFile:
@ -104,9 +113,10 @@ def loadConfig():
# Just use the same directory as the program and forget about # Just use the same directory as the program and forget about
# the appdata folder # the appdata folder
state.appdata = '' state.appdata = ''
print('Creating new config files in same directory as program.') logger.info(
'Creating new config files in same directory as program.')
else: 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): if not os.path.exists(state.appdata):
os.makedirs(state.appdata) os.makedirs(state.appdata)
if not sys.platform.startswith('win'): if not sys.platform.startswith('win'):
@ -258,7 +268,7 @@ def updateConfig():
'bitmessagesettings', 'hidetrayconnectionnotifications', 'false') 'bitmessagesettings', 'hidetrayconnectionnotifications', 'false')
if config.safeGetInt('bitmessagesettings', 'maxoutboundconnections') < 1: if config.safeGetInt('bitmessagesettings', 'maxoutboundconnections') < 1:
config.set('bitmessagesettings', 'maxoutboundconnections', '8') 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 # TTL is now user-specifiable. Let's add an option to save
# whatever the user selects. # whatever the user selects.
@ -281,3 +291,26 @@ def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
return False return False
except Exception: except Exception:
pass 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)

View File

@ -22,7 +22,7 @@ def assemble_addr(peerList):
len(peerList[i:i + MAX_ADDR_COUNT])) len(peerList[i:i + MAX_ADDR_COUNT]))
for stream, peer, timestamp in peerList[i:i + MAX_ADDR_COUNT]: for stream, peer, timestamp in peerList[i:i + MAX_ADDR_COUNT]:
payload += struct.pack( payload += struct.pack(
'>Q', timestamp) # 64-bit time '>Q', int(timestamp)) # 64-bit time
payload += struct.pack('>I', stream) payload += struct.pack('>I', stream)
payload += struct.pack( payload += struct.pack(
'>q', 1) # service bit flags offered by this node '>q', 1) # service bit flags offered by this node

View File

@ -172,9 +172,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
except BMObjectAlreadyHaveError: except BMObjectAlreadyHaveError:
logger.debug( logger.debug(
'%(host)s:%(port)i already got object, skipping', '%(host)s:%(port)i already got object, skipping',
self.destination._asdict()) self.destinaestion._asdict())
except struct.error: except struct.error:
logger.debug('decoding error, skipping') logger.debug('decoding error, skipping')
except ValueError:
pass
elif self.socket.type == socket.SOCK_DGRAM: elif self.socket.type == socket.SOCK_DGRAM:
# broken read, ignore # broken read, ignore
pass pass
@ -206,9 +208,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
"""Decode node details from the payload""" """Decode node details from the payload"""
# protocol.checkIPAddress() # protocol.checkIPAddress()
services, host, port = self.decode_payload_content("Q16sH") services, host, port = self.decode_payload_content("Q16sH")
if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF'.encode('raw_unicode_escape'):
host = socket.inet_ntop(socket.AF_INET, host[12:16]) host = socket.inet_ntop(socket.AF_INET, host[12:16])
elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43': elif host[0:6] == '\xfd\x87\xd8\x7e\xeb\x43'.encode('raw_unicode_escape'):
# Onion, based on BMD/bitcoind # Onion, based on BMD/bitcoind
host = base64.b32encode(host[6:]).lower() + ".onion" host = base64.b32encode(host[6:]).lower() + ".onion"
else: else:
@ -385,7 +387,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if dandelion and not state.dandelion: if dandelion and not state.dandelion:
return True return True
for i in map(str, items): for i in map(bytes, items):
if i in Inventory() and not Dandelion().hasHash(i): if i in Inventory() and not Dandelion().hasHash(i):
continue continue
if dandelion and not Dandelion().hasHash(i): if dandelion and not Dandelion().hasHash(i):
@ -438,7 +440,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
try: try:
self.object.checkObjectByType() self.object.checkObjectByType()
objectProcessorQueue.put(( objectProcessorQueue.put((
self.object.objectType, buffer(self.object.data))) self.object.objectType, memoryview(self.object.data)))
except BMObjectInvalidError: except BMObjectInvalidError:
BMProto.stopDownloadingObject(self.object.inventoryHash, True) BMProto.stopDownloadingObject(self.object.inventoryHash, True)
else: else:
@ -449,12 +451,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
if self.object.inventoryHash in Inventory() and Dandelion().hasHash(self.object.inventoryHash): if self.object.inventoryHash in Inventory() and Dandelion().hasHash(self.object.inventoryHash):
Dandelion().removeHash(self.object.inventoryHash, "cycle detection") Dandelion().removeHash(self.object.inventoryHash, "cycle detection")
[self.object.inventoryHash] = (
Inventory()[self.object.inventoryHash] = (
self.object.objectType, self.object.streamNumber, self.object.objectType, self.object.streamNumber,
buffer(self.payload[objectOffset:]), self.object.expiresTime, memoryview(self.payload[objectOffset:]), self.object.expiresTime,
buffer(self.object.tag) memoryview(self.object.tag)
) )
Inventory()[self.object.inventoryHash]
self.handleReceivedObject( self.handleReceivedObject(
self.object.streamNumber, self.object.inventoryHash) self.object.streamNumber, self.object.inventoryHash)
invQueue.put(( invQueue.put((

View File

@ -149,6 +149,9 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
retval = self.socket.sendto( retval = self.socket.sendto(
self.write_buf, ('<broadcast>', self.port)) self.write_buf, ('<broadcast>', self.port))
except socket.error as e: 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 retval = 0
self.slice_write_buf(retval) self.slice_write_buf(retval)

View File

@ -0,0 +1,7 @@
"""
Simple plugin system based on setuptools
----------------------------------------
"""
# .. include:: pybitmessage.plugins.plugin.rst

View File

@ -1,19 +1,28 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
src/plugins/plugin.py Operating with plugins
===================================
""" """
import logging
import pkg_resources import pkg_resources
logger = logging.getLogger('default')
def get_plugins(group, point='', name=None, fallback=None): def get_plugins(group, point='', name=None, fallback=None):
""" """
Iterate through plugins (`connect_plugin` attribute of entry point) :param str group: plugin group
which name starts with `point` or equals to `name`. :param str point: plugin name prefix
If `fallback` kwarg specified, plugin with that name yield last. :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): 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: try:
plugin = ep.load().connect_plugin plugin = ep.load().connect_plugin
if ep.name == fallback: if ep.name == fallback:
@ -25,6 +34,8 @@ def get_plugins(group, point='', name=None, fallback=None):
ValueError, ValueError,
pkg_resources.DistributionNotFound, pkg_resources.DistributionNotFound,
pkg_resources.UnknownExtra): pkg_resources.UnknownExtra):
logger.debug(
'Problem while loading %s', ep.name, exc_info=True)
continue continue
try: try:
yield _fallback yield _fallback
@ -33,6 +44,8 @@ def get_plugins(group, point='', name=None, fallback=None):
def get_plugin(*args, **kwargs): 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): for plugin in get_plugins(*args, **kwargs):
return plugin return plugin

View File

@ -1,7 +1,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
src/plugins/proxyconfig_stem.py Configure tor proxy and hidden service with
=================================== `stem <https://stem.torproject.org/>`_ 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 os
import logging import logging
@ -36,13 +44,20 @@ class DebugLogger(object):
def connect_plugin(config): # pylint: disable=too-many-branches 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() logwrite = DebugLogger()
if config.safeGet('bitmessagesettings', 'sockshostname') not in ( if config.safeGet('bitmessagesettings', 'sockshostname', '') not in (
'localhost', '127.0.0.1', '' 'localhost', '127.0.0.1', ''
): ):
# remote proxy is choosen for outbound connections, # remote proxy is choosen for outbound connections,
# nothing to do here, but need to set socksproxytype to SOCKS5! # nothing to do here, but need to set socksproxytype to SOCKS5!
config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
logwrite( logwrite(
'sockshostname is set to remote address,' 'sockshostname is set to remote address,'
' aborting stem proxy configuration') ' aborting stem proxy configuration')
@ -77,6 +92,8 @@ def connect_plugin(config): # pylint: disable=too-many-branches
logwrite('Started tor on port %s' % port) logwrite('Started tor on port %s' % port)
break break
config.setTemp('bitmessagesettings', 'socksproxytype', 'SOCKS5')
if config.safeGetBoolean('bitmessagesettings', 'sockslisten'): if config.safeGetBoolean('bitmessagesettings', 'sockslisten'):
# need a hidden service for inbound connections # need a hidden service for inbound connections
try: try:
@ -95,7 +112,8 @@ def connect_plugin(config): # pylint: disable=too-many-branches
onionkeytype = config.safeGet(onionhostname, 'keytype') onionkeytype = config.safeGet(onionhostname, 'keytype')
response = controller.create_ephemeral_hidden_service( 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_type=(onionkeytype or 'NEW'),
key_content=(onionkey or onionhostname and 'ED25519-V3' or 'BEST') key_content=(onionkey or onionhostname and 'ED25519-V3' or 'BEST')
) )
@ -117,6 +135,5 @@ def connect_plugin(config): # pylint: disable=too-many-branches
config.set( config.set(
onionhostname, 'keytype', response.private_key_type) onionhostname, 'keytype', response.private_key_type)
config.save() config.save()
config.set('bitmessagesettings', 'socksproxytype', 'SOCKS5')
return True return True

View File

@ -278,14 +278,13 @@ def isProofOfWorkSufficient(
def CreatePacket(command, payload=''): def CreatePacket(command, payload=''):
"""Construct and return a number of bytes from a payload""" """Construct and return a number of bytes from a payload"""
payload = payload if type(payload) == bytes else payload.encode() payload = payload if type(payload) in [bytes,bytearray] else payload.encode()
payload_length = len(payload) payload_length = len(payload)
checksum = hashlib.sha512(payload).digest()[0:4] checksum = hashlib.sha512(payload).digest()[0:4]
byte = bytearray(Header.size + payload_length) byte = bytearray(Header.size + payload_length)
Header.pack_into(byte, 0, 0xE9BEB4D9, command.encode(), payload_length, checksum) Header.pack_into(byte, 0, 0xE9BEB4D9, command.encode(), payload_length, checksum)
byte[Header.size:] = payload byte[Header.size:] = payload
return byte return bytes(byte)
def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=False, nodeid=None): def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=False, nodeid=None):
@ -325,10 +324,8 @@ def assembleVersionMessage(remoteHost, remotePort, participatingStreams, server=
(NODE_DANDELION if state.dandelion else 0) (NODE_DANDELION if state.dandelion else 0)
) )
# = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used. # = 127.0.0.1. This will be ignored by the remote host. The actual remote connected IP will be used.
#python3 need to check
# python3 need to check payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF'.encode('raw_unicode_escape') + pack('>L', 2130706433)
payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF'.encode() + pack('>L', 2130706433)
# we have a separate extPort and incoming over clearnet # we have a separate extPort and incoming over clearnet
# or outgoing through clearnet # or outgoing through clearnet
extport = BMConfigParser().safeGetInt('bitmessagesettings', 'extport') extport = BMConfigParser().safeGetInt('bitmessagesettings', 'extport')

View File

@ -39,15 +39,20 @@ class SqliteInventory(InventoryStorage): # pylint: disable=too-many-ancestors
return True return True
def __getitem__(self, hash_): def __getitem__(self, hash_):
print('----------__getitem__------------------') if hash_ == 0:
hash_ = bytes()
with self.lock: with self.lock:
if hash_ in self._inventory: try:
return self._inventory[hash_] if hash_ in self._inventory:
rows = sqlQuery( return self._inventory[hash_]
'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', rows = sqlQuery(
sqlite3.Binary(hash_)) 'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?',
if not rows: sqlite3.Binary(hash_))
raise KeyError(hash_) if not rows:
pass
# raise KeyError(hash_)
except:
pass
return InventoryItem(*rows[0]) return InventoryItem(*rows[0])
def __setitem__(self, hash_, value): def __setitem__(self, hash_, value):

View File

@ -15,14 +15,20 @@ import knownnodes
import state import state
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from helper_msgcoding import MsgEncode, MsgDecode from helper_msgcoding import MsgEncode, MsgDecode
from helper_startup import start_proxyconfig
from network import asyncore_pollchoose as asyncore from network import asyncore_pollchoose as asyncore
from network.connectionpool import BMConnectionPool from network.connectionpool import BMConnectionPool
from network.node import Peer from network.node import Peer
from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection from network.tcp import Socks4aBMConnection, Socks5BMConnection, TCPConnection
from queues import excQueue 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') knownnodes_file = os.path.join(state.appdata, 'knownnodes.dat')
program = None
def pickle_knownnodes(): def pickle_knownnodes():
@ -170,12 +176,29 @@ class TestCore(unittest.TestCase):
self.assertIsInstance(con, connection_base) self.assertIsInstance(con, connection_base)
self.assertNotEqual(peer.host, '127.0.0.1') self.assertNotEqual(peer.host, '127.0.0.1')
return return
else: # pylint: disable=useless-else-on-loop self.fail(
self.fail( 'Failed to connect during %s sec' % (time.time() - _started))
'Failed to connect during %s sec' % (time.time() - _started))
def test_onionservicesonly(self): def test_bootstrap(self):
"""test onionservicesonly networking mode""" """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') BMConfigParser().set('bitmessagesettings', 'onionservicesonly', 'true')
self._initiate_bootstrap() self._initiate_bootstrap()
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect') BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
@ -184,26 +207,18 @@ class TestCore(unittest.TestCase):
for n, peer in enumerate(BMConnectionPool().outboundConnections): for n, peer in enumerate(BMConnectionPool().outboundConnections):
if n > 2: if n > 2:
return return
if not peer.host.endswith('.onion'): if (
not peer.host.endswith('.onion')
and not peer.host.startswith('bootstrap')
):
self.fail( self.fail(
'Found non onion hostname %s in outbound connections!' 'Found non onion hostname %s in outbound connections!'
% peer.host) % peer.host)
self.fail('Failed to connect to at least 3 nodes within 360 sec') 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')
program.start_proxyconfig(BMConfigParser())
self._check_bootstrap()
def run():
def run(prog):
"""Starts all tests defined in this module""" """Starts all tests defined in this module"""
global program # pylint: disable=global-statement
program = prog
loader = unittest.TestLoader() loader = unittest.TestLoader()
loader.sortTestMethodsUsing = None loader.sortTestMethodsUsing = None
suite = loader.loadTestsFromTestCase(TestCore) suite = loader.loadTestsFromTestCase(TestCore)