Compare commits

...

11 Commits

15 changed files with 343 additions and 92 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "packages/flatpak/shared-modules"]
path = packages/flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git

View File

@ -0,0 +1,57 @@
{
"id": "org.bitmessage.BaseApp",
"branch": "19.08",
"runtime": "org.freedesktop.Platform",
"sdk": "org.freedesktop.Sdk",
"runtime-version": "19.08",
"separate-locales": false,
"modules": [
"shared-modules/python2.7/python-2.7.json",
"shared-modules/qt4/qt4-4.8.7-minimal.json",
{
"name": "python-sip",
"sources": [
{
"type": "archive",
"url": "https://www.riverbankcomputing.com/static/Downloads/sip/4.19.25/sip-4.19.25.tar.gz",
"sha256": "b39d93e937647807bac23579edbff25fe46d16213f708370072574ab1f1b4211"
}
],
"buildsystem": "simple",
"build-commands": [
"python configure.py --sip-module PyQt4.sip --no-dist-info",
"make",
"make install"
]
},
{
"name": "python-qt4",
"sources": [
{
"type": "archive",
"url": "http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12.3/PyQt4_gpl_x11-4.12.3.tar.gz",
"sha256": "a00f5abef240a7b5852b7924fa5fdf5174569525dc076cd368a566619e56d472"
}
],
"buildsystem": "simple",
"build-commands": [
"python configure.py -w --confirm-license",
"make",
"make install"
]
},
{
"name" : "PyBitmessage-dependencies",
"buildsystem" : "simple",
"build-options": {
"build-args": [
"--share=network"
]
},
"build-commands": [
"pip --version",
"pip install setuptools msgpack"
]
}
]
}

View File

@ -0,0 +1,48 @@
{
"app-id": "org.bitmessage.PyBitmessage",
"runtime": "org.freedesktop.Platform",
"runtime-version": "19.08",
"branch": "stable",
"sdk": "org.freedesktop.Sdk",
"base": "org.bitmessage.BaseApp",
"command": "pybitmessage",
"base-version":"stable",
"finish-args" : [
"--share=network",
"--socket=x11",
"--share=ipc",
"--filesystem=xdg-config/PyBitmessage:create"
],
"modules": [
{
"name" : "PyBitmessage",
"buildsystem" : "simple",
"build-options": {
"build-args": [
"--share=network"
]
},
"build-commands": [
"python --version",
"pwd",
"ls",
"python checkdeps.py",
"python setup.py install --prefix=/app --exec-prefix=/app",
"sed -i 's~/usr/bin/~/app/bin/~' /app/bin/pybitmessage",
"cat /app/bin/pybitmessage",
"mv /app/share/applications/pybitmessage.desktop /app/share/applications/org.bitmessage.PyBitmessage.desktop",
"sed -i 's~Icon=pybitmessage~Icon=org.bitmessage.PyBitmessage~' /app/share/applications/org.bitmessage.PyBitmessage.desktop",
"mv /app/share/icons/hicolor/scalable/apps/pybitmessage.svg /app/share/icons/hicolor/scalable/apps/org.bitmessage.PyBitmessage.svg",
"mv /app/share/icons/hicolor/24x24/apps/pybitmessage.png /app/share/icons/hicolor/24x24/apps/org.bitmessage.PyBitmessage.png",
"which pybitmessage"
],
"sources" : [
{
"type" : "dir",
"path" : "../../"
}
]
}
]
}

@ -0,0 +1 @@
Subproject commit fd4d38328ccb078b88ad4a891807e593ae8de806

View File

@ -321,9 +321,10 @@ class Main(object):
receiveQueueThread = ReceiveQueueThread(i)
receiveQueueThread.daemon = True
receiveQueueThread.start()
announceThread = AnnounceThread()
announceThread.daemon = True
announceThread.start()
if config.safeGetBoolean('bitmessagesettings', 'udp'):
state.announceThread = AnnounceThread()
state.announceThread.daemon = True
state.announceThread.start()
state.invThread = InvThread()
state.invThread.daemon = True
state.invThread.start()

View File

@ -19,7 +19,7 @@ import widgets
from bmconfigparser import BMConfigParser
from helper_sql import sqlExecute, sqlStoredProcedure
from helper_startup import start_proxyconfig
from network import knownnodes
from network import knownnodes, AnnounceThread
from network.asyncore_pollchoose import set_rates
from tr import _translate
@ -138,6 +138,8 @@ class SettingsDialog(QtGui.QDialog):
config.get('bitmessagesettings', 'port')))
self.checkBoxUPnP.setChecked(
config.safeGetBoolean('bitmessagesettings', 'upnp'))
self.checkBoxUDP.setChecked(
config.safeGetBoolean('bitmessagesettings', 'udp'))
self.checkBoxAuthentication.setChecked(
config.getboolean('bitmessagesettings', 'socksauthentication'))
self.checkBoxSocksListen.setChecked(
@ -326,7 +328,8 @@ class SettingsDialog(QtGui.QDialog):
self.lineEditTCPPort.text()):
self.config.set(
'bitmessagesettings', 'port', str(self.lineEditTCPPort.text()))
if not self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
if not self.config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
self.net_restart_needed = True
if self.checkBoxUPnP.isChecked() != self.config.safeGetBoolean(
@ -339,11 +342,26 @@ class SettingsDialog(QtGui.QDialog):
upnpThread = upnp.uPnPThread()
upnpThread.start()
udp_enabled = self.checkBoxUDP.isChecked()
if udp_enabled != self.config.safeGetBoolean(
'bitmessagesettings', 'udp'):
self.config.set('bitmessagesettings', 'udp', str(udp_enabled))
if udp_enabled:
announceThread = AnnounceThread()
announceThread.daemon = True
announceThread.start()
else:
try:
state.announceThread.stopThread()
except AttributeError:
pass
proxytype_index = self.comboBoxProxyType.currentIndex()
if proxytype_index == 0:
if self._proxy_type and state.statusIconColor != 'red':
self.net_restart_needed = True
elif state.statusIconColor == 'red' and self.config.safeGetBoolean('bitmessagesettings', 'dontconnect'):
elif state.statusIconColor == 'red' and self.config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
self.net_restart_needed = False
elif self.comboBoxProxyType.currentText() != self._proxy_type:
self.net_restart_needed = True
@ -369,8 +387,11 @@ class SettingsDialog(QtGui.QDialog):
self.lineEditSocksPassword.text()))
self.config.set('bitmessagesettings', 'sockslisten', str(
self.checkBoxSocksListen.isChecked()))
if self.checkBoxOnionOnly.isChecked() \
and not self.config.safeGetBoolean('bitmessagesettings', 'onionservicesonly'):
if (
self.checkBoxOnionOnly.isChecked()
and not self.config.safeGetBoolean(
'bitmessagesettings', 'onionservicesonly')
):
self.net_restart_needed = True
self.config.set('bitmessagesettings', 'onionservicesonly', str(
self.checkBoxOnionOnly.isChecked()))
@ -432,8 +453,8 @@ class SettingsDialog(QtGui.QDialog):
acceptableDifficultyChanged = False
if (
float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
float(self.lineEditMaxAcceptableTotalDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableTotalDifficulty.text()) == 0
):
if self.config.get(
'bitmessagesettings', 'maxacceptablenoncetrialsperbyte'
@ -449,8 +470,8 @@ class SettingsDialog(QtGui.QDialog):
* defaults.networkDefaultProofOfWorkNonceTrialsPerByte))
)
if (
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) >= 1
or float(self.lineEditMaxAcceptableSmallMessageDifficulty.text()) == 0
):
if self.config.get(
'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes'
@ -541,8 +562,8 @@ class SettingsDialog(QtGui.QDialog):
self.parent.updateStartOnLogon()
if (
state.appdata != paths.lookupExeFolder()
and self.checkBoxPortableMode.isChecked()
state.appdata != paths.lookupExeFolder()
and self.checkBoxPortableMode.isChecked()
):
# If we are NOT using portable mode now but the user selected
# that we should...
@ -564,8 +585,8 @@ class SettingsDialog(QtGui.QDialog):
pass
if (
state.appdata == paths.lookupExeFolder()
and not self.checkBoxPortableMode.isChecked()
state.appdata == paths.lookupExeFolder()
and not self.checkBoxPortableMode.isChecked()
):
# If we ARE using portable mode now but the user selected
# that we shouldn't...

View File

@ -231,7 +231,7 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Bandwidth limit</string>
@ -322,7 +322,7 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Proxy server / Tor</string>
@ -432,7 +432,14 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxUDP">
<property name="text">
<string>Announce self by UDP</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>

View File

@ -2,6 +2,10 @@
from addressbook import TestAddressbook
from main import TestMain, TestUISignaler
from settings import TestSettings
from support import TestSupport
__all__ = ["TestAddressbook", "TestMain", "TestSupport", "TestUISignaler"]
__all__ = [
"TestAddressbook", "TestMain", "TestSettings", "TestSupport",
"TestUISignaler"
]

View File

@ -0,0 +1,34 @@
import threading
import time
from main import TestBase
from bmconfigparser import BMConfigParser
from bitmessageqt import settings
class TestSettings(TestBase):
"""A test case for the "Settings" dialog"""
def setUp(self):
super(TestSettings, self).setUp()
self.dialog = settings.SettingsDialog(self.window)
def test_udp(self):
"""Test the effect of checkBoxUDP"""
udp_setting = BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'udp')
self.assertEqual(udp_setting, self.dialog.checkBoxUDP.isChecked())
self.dialog.checkBoxUDP.setChecked(not udp_setting)
self.dialog.accept()
self.assertEqual(
not udp_setting,
BMConfigParser().safeGetBoolean('bitmessagesettings', 'udp'))
time.sleep(5)
for thread in threading.enumerate():
if thread.name == 'Announcer': # find Announcer thread
if udp_setting:
self.fail(
'Announcer thread is running while udp set to False')
break
else:
if not udp_setting:
self.fail('No Announcer thread found while udp set to True')

View File

@ -2,13 +2,22 @@
BMConfigParser class definition and default configuration settings
"""
import ConfigParser
import sys
if sys.version_info[0] == 3:
# python 3
import configparser as ConfigParser
SafeConfigParser = ConfigParser.ConfigParser
else:
# python 2
import ConfigParser
SafeConfigParser = ConfigParser.SafeConfigParser
import state
from singleton import Singleton
import os
import shutil
from datetime import datetime
import state
from singleton import Singleton
BMConfigDefaults = {
"bitmessagesettings": {
@ -19,30 +28,32 @@ BMConfigDefaults = {
"maxtotalconnections": 200,
"maxuploadrate": 0,
"apiinterface": "127.0.0.1",
"apiport": 8442
"apiport": 8442,
"udp": "True"
},
"threads": {
"receive": 3,
},
"network": {
"bind": '',
"bind": "",
"dandelion": 90,
},
"inventory": {
"storage": "sqlite",
"acceptmismatch": False,
"acceptmismatch": "False",
},
"knownnodes": {
"maxnodes": 20000,
},
"zlib": {
'maxsize': 1048576
"maxsize": 1048576
}
}
@Singleton
class BMConfigParser(ConfigParser.SafeConfigParser):
class BMConfigParser(SafeConfigParser):
"""
Singleton class inherited from :class:`ConfigParser.SafeConfigParser`
with additional methods specific to bitmessage config.
@ -59,26 +70,47 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
raise ValueError("Invalid value %s" % value)
return ConfigParser.ConfigParser.set(self, section, option, value)
def get(self, section, option, raw=False, variables=None):
# pylint: disable=arguments-differ
try:
if section == "bitmessagesettings" and option == "timeformat":
def get(self, section, option, raw=False, vars=None):
if sys.version_info[0] == 3:
# pylint: disable=arguments-differ
try:
if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get(
self, section, option)
try:
return self._temp[section][option]
except KeyError:
pass
return ConfigParser.ConfigParser.get(
self, section, option, raw, variables)
self, section, option)
except ConfigParser.InterpolationError:
return ConfigParser.ConfigParser.get(
self, section, option)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
try:
return BMConfigDefaults[section][option]
except (KeyError, ValueError, AttributeError):
raise e
else:
# pylint: disable=arguments-differ
try:
return self._temp[section][option]
except KeyError:
pass
return ConfigParser.ConfigParser.get(
self, section, option, True, variables)
except ConfigParser.InterpolationError:
return ConfigParser.ConfigParser.get(
self, section, option, True, variables)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
try:
return BMConfigDefaults[section][option]
except (KeyError, ValueError, AttributeError):
raise e
if section == "bitmessagesettings" and option == "timeformat":
return ConfigParser.ConfigParser.get(
self, section, option, raw, vars)
try:
return self._temp[section][option]
except KeyError:
pass
return ConfigParser.ConfigParser.get(
self, section, option, True, vars)
except ConfigParser.InterpolationError:
return ConfigParser.ConfigParser.get(
self, section, option, True, vars)
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) as e:
try:
return BMConfigDefaults[section][option]
except (KeyError, ValueError, AttributeError):
raise e
def setTemp(self, section, option, value=None):
"""Temporary set option to value, not saving."""
@ -190,3 +222,4 @@ class BMConfigParser(ConfigParser.SafeConfigParser):
if value < 0 or value > 8:
return False
return True

View File

@ -7,7 +7,6 @@ import state
from bmconfigparser import BMConfigParser
from network.assemble import assemble_addr
from network.connectionpool import BMConnectionPool
from network.udp import UDPSocket
from node import Peer
from threads import StoppableThread
@ -15,12 +14,13 @@ from threads import StoppableThread
class AnnounceThread(StoppableThread):
"""A thread to manage regular announcing of this node"""
name = "Announcer"
announceInterval = 60
def run(self):
lastSelfAnnounced = 0
while not self._stopped and state.shutdown == 0:
processed = 0
if lastSelfAnnounced < time.time() - UDPSocket.announceInterval:
if lastSelfAnnounced < time.time() - self.announceInterval:
self.announceSelf()
lastSelfAnnounced = time.time()
if processed == 0:

View File

@ -8,6 +8,7 @@ import time
import protocol
import state
from bmproto import BMProto
from constants import MAX_TIME_OFFSET
from node import Peer
from objectracker import ObjectTracker
from queues import receiveDataQueue
@ -18,7 +19,6 @@ logger = logging.getLogger('default')
class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
"""Bitmessage protocol over UDP (class)"""
port = 8444
announceInterval = 60
def __init__(self, host=None, sock=None, announcing=False):
# pylint: disable=bad-super-call
@ -82,8 +82,8 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating:
continue
if (seenTime < time.time() - self.maxTimeOffset
or seenTime > time.time() + self.maxTimeOffset):
if (seenTime < time.time() - MAX_TIME_OFFSET
or seenTime > time.time() + MAX_TIME_OFFSET):
continue
if decodedIP is False:
# if the address isn't local, interpret it as
@ -94,9 +94,8 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
logger.debug(
"received peer discovery from %s:%i (port %i):",
self.destination.host, self.destination.port, remoteport)
if self.local:
state.discoveredPeers[Peer(self.destination.host, remoteport)] = \
time.time()
state.discoveredPeers[Peer(self.destination.host, remoteport)] = \
time.time()
return True
def bm_command_portcheck(self):
@ -125,9 +124,9 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
def handle_read(self):
try:
(recdata, addr) = self.socket.recvfrom(self._buf_len)
except socket.error as e:
logger.error("socket error: %s", e)
recdata, addr = self.socket.recvfrom(self._buf_len)
except socket.error:
logger.error("socket error on recvfrom:", exc_info=True)
return
self.destination = Peer(*addr)
@ -143,7 +142,7 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
try:
retval = self.socket.sendto(
self.write_buf, ('<broadcast>', self.port))
except socket.error as e:
logger.error("socket error on sendto: %s", e)
except socket.error:
logger.error("socket error on sendto:", exc_info=True)
retval = len(self.write_buf)
self.slice_write_buf(retval)

View File

@ -11,6 +11,7 @@ import shutil
import socket
import string
import sys
import threading
import time
import unittest
@ -61,6 +62,13 @@ class TestCore(unittest.TestCase):
"""Test case, which runs in main pybitmessage thread"""
addr = 'BM-2cVvkzJuQDsQHLqxRXc6HZGPLZnkBLzEZY'
def tearDown(self):
"""Reset possible unexpected settings after test"""
knownnodes.addKnownNode(1, Peer('127.0.0.1', 8444), is_self=True)
BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
BMConfigParser().remove_option('bitmessagesettings', 'onionservicesonly')
BMConfigParser().set('bitmessagesettings', 'socksproxytype', 'none')
def test_msgcoding(self):
"""test encoding and decoding (originally from helper_msgcoding)"""
msg_data = {
@ -270,6 +278,36 @@ class TestCore(unittest.TestCase):
return
self.fail('Failed to connect to at least 3 nodes within 360 sec')
def test_udp(self):
"""check default udp setting and presence of Announcer thread"""
self.assertTrue(
BMConfigParser().safeGetBoolean('bitmessagesettings', 'udp'))
for thread in threading.enumerate():
if thread.name == 'Announcer': # find Announcer thread
break
else:
return self.fail('No Announcer thread found')
for _ in range(20): # wait for UDP socket
for sock in BMConnectionPool().udpSockets.values():
thread.announceSelf()
break
else:
time.sleep(1)
continue
break
else:
self.fail('UDP socket is not started')
for _ in range(20):
if state.discoveredPeers:
peer = state.discoveredPeers.keys()[0]
self.assertEqual(peer.port, 8444)
break
time.sleep(1)
else:
self.fail('No self in discovered peers')
@staticmethod
def _decode_msg(data, pattern):
proto = BMProto()

View File

@ -2,11 +2,7 @@
Various tests for config
"""
import os
import unittest
import tempfile
from .test_process import TestProcessProto
from pybitmessage.bmconfigparser import BMConfigParser
@ -38,32 +34,3 @@ class TestConfig(unittest.TestCase):
BMConfigParser().safeGetInt('nonexistent', 'nonexistent'), 0)
self.assertEqual(
BMConfigParser().safeGetInt('nonexistent', 'nonexistent', 42), 42)
class TestProcessConfig(TestProcessProto):
"""A test case for keys.dat"""
home = tempfile.mkdtemp()
def test_config_defaults(self):
"""Test settings in the generated config"""
self._stop_process()
self._kill_process()
config = BMConfigParser()
config.read(os.path.join(self.home, 'keys.dat'))
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'settingsversion'), 10)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'port'), 8444)
# don't connect
self.assertTrue(config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'))
# API disabled
self.assertFalse(config.safeGetBoolean(
'bitmessagesettings', 'apienabled'))
# extralowdifficulty is false
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)

View File

@ -0,0 +1,38 @@
"""
Various tests for config
"""
import os
import tempfile
from pybitmessage.bmconfigparser import BMConfigParser
from .test_process import TestProcessProto
class TestProcessConfig(TestProcessProto):
"""A test case for keys.dat"""
home = tempfile.mkdtemp()
def test_config_defaults(self):
"""Test settings in the generated config"""
config = BMConfigParser()
self._stop_process()
self._kill_process()
config.read(os.path.join(self.home, 'keys.dat'))
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'settingsversion'), 10)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'port'), 8444)
# don't connect
self.assertTrue(config.safeGetBoolean(
'bitmessagesettings', 'dontconnect'))
# API disabled
self.assertFalse(config.safeGetBoolean(
'bitmessagesettings', 'apienabled'))
# extralowdifficulty is false
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultnoncetrialsperbyte'), 1000)
self.assertEqual(config.safeGetInt(
'bitmessagesettings', 'defaultpayloadlengthextrabytes'), 1000)