diff --git a/buildscripts/builder.sh b/buildscripts/builder.sh
new file mode 100755
index 00000000..07949fa4
--- /dev/null
+++ b/buildscripts/builder.sh
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+# INIT
+MACHINE_TYPE=`uname -m`
+BASE_DIR=$(pwd)
+PYTHON_VERSION=2.7.15
+PYQT_VERSION=4-4.11.4-gpl-Py2.7-Qt4.8.7
+OPENSSL_VERSION=1_0_2t
+DIRECTORY32BIT=SoftwareDownloads32bit
+DIRECTORY64BIT=SoftwareDownloads64bit
+
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ if [ ! -d "$DIRECTORY64BIT" ]; then
+ mkdir SoftwareDownloads64bit
+ cd SoftwareDownloads64bit
+ else
+ echo "Directory already exists"
+ cd SoftwareDownloads64bit
+ fi
+else
+ if [ ! -d "$DIRECTORY32BIT" ]; then
+ mkdir SoftwareDownloads32bit
+ cd SoftwareDownloads32bit
+ else
+ echo "Directory 32 bit alrready exists"
+ cd SoftwareDownloads32bit
+ fi
+fi
+#Functions
+function install_wine {
+
+
+ wget -nc https://dl.winehq.org/wine-builds/Release.key --no-check-certificate
+ sudo apt-key add Release.key
+ sudo apt-add-repository 'https://dl.winehq.org/wine-builds/ubuntu/'
+ sudo apt-get -y update
+ sudo apt-get -y install wine1.8 winetricks
+ if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ sudo apt-get -y install wine64-development
+ env WINEPREFIX=$HOME/.wine64 WINEARCH=win64 winecfg
+ WINE="env WINEPREFIX=$HOME/.wine64 wine"
+ export WINEPREFIX
+
+ else
+ sudo apt-get -y install wine32-development
+ env WINEPREFIX=$HOME/.wine32 WINEARCH=win32 winecfg
+ WINE="env WINEPREFIX=$HOME/.wine32 wine"
+ export WINEPREFIX
+
+ fi
+}
+
+function install_python(){
+ echo "Download Python2.7"
+
+ if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ # For 64 bit machine
+ wget -nc wget http://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}.amd64.msi --no-check-certificate
+ echo "Install Python2.7 for 64 bit"
+ $WINE msiexec -i python-${PYTHON_VERSION}.amd64.msi /q /norestart
+
+ wget -nc https://download.microsoft.com/download/d/2/4/d242c3fb-da5a-4542-ad66-f9661d0a8d19/vcredist_x64.exe --no-check-certificate
+ $WINE vcredist_x64.exe /q /norestart
+ echo "Installed vcredist for 64 bit"
+ $WINE pip install --upgrade pip
+
+ else
+ # For 32 bit machine
+ wget -nc https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}.msi --no-check-certificate
+ echo "Install Python2.7 for 32 bit"
+ $WINE msiexec -i python-${PYTHON_VERSION}.msi /q /norestart
+
+ echo "Installing vc_redist for 32 bit "
+ wget -nc https://download.microsoft.com/download/1/1/1/1116b75a-9ec3-481a-a3c8-1777b5381140/vcredist_x86.exe --no-check-certificate
+ $WINE vcredist_x86.exe /q /norestart
+ #insatlled msvcr120.dll for 32 bit system
+ wget -nc http://www.dll-found.com/zip/m/msvcr120.dll.zip --no-check-certificate
+ unzip msvcr120.dll.zip
+ sudo cp msvcr120.dll $HOME/.wine32/drive_c/windows/system32/
+ $WINE pip install --upgrade pip
+
+ fi
+}
+
+function install_pyqt(){
+
+ echo "Download PyQT"
+ if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ # For 64 bit machine
+ wget -nc --content-disposition https://github.com/Bitmessage/ThirdPartyLibraries/blob/master/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x64.exe?raw=true --no-check-certificate
+ $WINE PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x64.exe /q /norestart /silent /verysiling /sp- /suppressmsgboxes
+ else
+ # For 32 bit machine
+ wget -nc --content-disposition https://github.com/Bitmessage/ThirdPartyLibraries/blob/master/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe?raw=true --no-check-certificate
+ $WINE PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe /q /norestart /silent /verysiling /sp- /suppressmsgboxes
+ fi
+}
+
+function install_openssl(){
+ if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ wget -nc --content-disposition https://github.com/Bitmessage/ThirdPartyLibraries/blob/master/Win64OpenSSL-${OPENSSL_VERSION}.exe?raw=true --no-check-certificate
+ $WINE Win64OpenSSL-${OPENSSL_VERSION}.exe /q /norestart /silent /verysiling /sp- /suppressmsgboxes
+
+ else
+ wget -nc --content-disposition https://github.com/Bitmessage/ThirdPartyLibraries/blob/master/Win32OpenSSL-${OPENSSL_VERSION}.exe?raw=true --no-check-certificate
+ $WINE Win32OpenSSL-${OPENSSL_VERSION}.exe /q /norestart /silent /verysiling /sp- /suppressmsgboxes
+ echo "Install PyInstaller 32 bit"
+ fi
+}
+
+function install_pyinstaller()
+{
+ $WINE pip install pyinstaller
+ echo "Install PyInstaller"
+ echo "Install Pyopencl"
+
+ if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ wget -nc https://github.com/Bitmessage/ThirdPartyLibraries/blob/master/pyopencl-2015.1-cp27-none-win_amd64.whl --no-check-certificate
+ $WINE pip install pyopencl-2015.1-cp27-none-win_amd64.whl
+ $WINE pip install msgpack-python
+
+ else
+ wget -nc --content-disposition https://github.com/Bitmessage/ThirdPartyLibraries/blob/master/pyopencl-2015.1-cp27-none-win_amd64one-win32.whl?raw=true --no-check-certificate
+ $WINE pip install msgpack-python
+ $WINE pip install pyopencl-2015.1-cp27-none-win32.whl
+ fi
+ echo "Install Message Pack"
+
+}
+
+
+function build_dll(){
+ cd $BASE_DIR
+ rm -rf master.zip
+ rm -rf PyBitmessage
+ git clone https://github.com/Bitmessage/PyBitmessage.git
+ cd PyBitmessage/src/bitmsghash
+ if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+ # Do stuff for 64 bit machine
+ echo "Install MinGW"
+ sudo apt-get -y install mingw-w64
+ echo "Create dll"
+ x86_64-w64-mingw32-g++ -D_WIN32 -Wall -O3 -march=native -I$HOME/.wine64/drive_c/OpenSSL-Win64/include -I/usr/x86_64-w64-mingw32/include -L$HOME/.wine64/drive_c/OpenSSL-Win64/lib -c bitmsghash.cpp
+ x86_64-w64-mingw32-g++ -static-libgcc -shared bitmsghash.o -D_WIN32 -O3 -march=native -I$HOME/.wine64/drive_c/OpenSSL-Win64/include -L$HOME/.wine64/drive_c/OpenSSL-Win64 -L/usr/lib/x86_64-linux-gnu/wine -fPIC -shared -lcrypt32 -leay32 -lwsock32 -o bitmsghash64.dll -Wl,--out-implib,bitmsghash.a
+ echo "DLL generated successfully "
+ cd ..
+ cp -R bitmsghash ../../../src/
+ cd ../../../
+ cd packages/pyinstaller/
+ env WINEPREFIX=$HOME/.wine64 wine pyinstaller bitmessagemain.spec
+ else
+ echo "Install MinGW for 32 bit"
+ sudo apt-get install mingw-w64
+ echo "Create dll"
+
+
+ i686-w64-mingw32-g++ -D_WIN32 -Wall -m32 -O3 -march=native -I$HOME/.wine32/drive_c/OpenSSL-Win32/include -I/usr/i686-w64-mingw32/include -L$HOME/.wine32/drive_c/OpenSSL-Win32/lib -c bitmsghash.cpp
+ i686-w64-mingw32-g++ -static-libgcc -shared bitmsghash.o -D_WIN32 -O3 -march=native -I$HOME/.wine32/drive_c/OpenSSL-Win32/include -L$HOME/.wine32/drive_c/OpenSSL-Win32/lib/MinGW -fPIC -shared -lcrypt32 -leay32 -lwsock32 -o bitmsghash32.dll -Wl,--out-implib,bitmsghash.a
+ cd ..
+ cp -R bitmsghash ../../../src/
+ cd ../../../
+ cd packages/pyinstaller/
+ env WINEPREFIX=$HOME/.wine32 wine pyinstaller bitmessagemain.spec
+ fi
+}
+
+
+install_wine
+install_python
+install_pyqt
+install_openssl
+install_pyinstaller
+build_dll
diff --git a/packages/pyinstaller/bitmessagemain.spec b/packages/pyinstaller/bitmessagemain.spec
index 06cf6e76..7f30cedf 100644
--- a/packages/pyinstaller/bitmessagemain.spec
+++ b/packages/pyinstaller/bitmessagemain.spec
@@ -1,22 +1,42 @@
import ctypes
import os
import time
+import sys
+
+if ctypes.sizeof(ctypes.c_voidp) == 4:
+ arch=32
+else:
+ arch=64
+
+sslName = 'OpenSSL-Win%s' % ("32" if arch == 32 else "64")
+site_root = os.path.abspath(HOMEPATH)
+spec_root = os.path.abspath(SPECPATH)
+cdrivePath= site_root[0:3]
+srcPath = spec_root[:-20]+"src\\"
+qtPath = site_root+"\\PyQt4\\"
+openSSLPath = cdrivePath+sslName+"\\"
+msvcrDllPath = cdrivePath+"windows\\system32\\"
+pythonDllPath = cdrivePath+"Python27\\"
+outPath = spec_root+"\\bitmessagemain"
+
+importPath = srcPath
+sys.path.insert(0,importPath)
+os.chdir(sys.path[0])
+from version import softwareVersion
-srcPath = "C:\\src\\PyBitmessage\\src\\"
-qtPath = "C:\\Qt-4.8.7\\"
-openSSLPath = "C:\\OpenSSL-1.0.2j\\bin\\"
-outPath = "C:\\src\\PyInstaller-3.2.1\\bitmessagemain"
today = time.strftime("%Y%m%d")
snapshot = False
os.rename(os.path.join(srcPath, '__init__.py'), os.path.join(srcPath, '__init__.py.backup'))
# -*- mode: python -*-
-a = Analysis([srcPath + 'bitmessagemain.py'],
+a = Analysis(
+ [srcPath + 'bitmessagemain.py'],
pathex=[outPath],
- hiddenimports=[],
+ hiddenimports=['pyopencl','numpy', 'win32com' , 'setuptools.msvc' ,'_cffi_backend'],
hookspath=None,
- runtime_hooks=None)
+ runtime_hooks=None
+ )
os.rename(os.path.join(srcPath, '__init__.py.backup'), os.path.join(srcPath, '__init__.py'))
@@ -46,20 +66,17 @@ def addUIs():
a.datas += addTranslations()
a.datas += addUIs()
-if ctypes.sizeof(ctypes.c_voidp) == 4:
- arch=32
-else:
- arch=64
+
a.binaries += [('libeay32.dll', openSSLPath + 'libeay32.dll', 'BINARY'),
- (os.path.join('bitmsghash', 'bitmsghash%i.dll' % (arch)), os.path.join(srcPath, 'bitmsghash', 'bitmsghash%i.dll' % (arch)), 'BINARY'),
- (os.path.join('bitmsghash', 'bitmsghash.cl'), os.path.join(srcPath, 'bitmsghash', 'bitmsghash.cl'), 'BINARY'),
- (os.path.join('sslkeys', 'cert.pem'), os.path.join(srcPath, 'sslkeys', 'cert.pem'), 'BINARY'),
- (os.path.join('sslkeys', 'key.pem'), os.path.join(srcPath, 'sslkeys', 'key.pem'), 'BINARY')
- ]
+ ('python27.dll', pythonDllPath + 'python27.dll', 'BINARY'),
+ ('msvcr120.dll', msvcrDllPath + 'msvcr120.dll','BINARY'),
+ (os.path.join('bitmsghash', 'bitmsghash%i.dll' % (arch)), os.path.join(srcPath, 'bitmsghash', 'bitmsghash%i.dll' % (arch)), 'BINARY'),
+ (os.path.join('bitmsghash', 'bitmsghash.cl'), os.path.join(srcPath, 'bitmsghash', 'bitmsghash.cl'), 'BINARY'),
+ (os.path.join('sslkeys', 'cert.pem'), os.path.join(srcPath, 'sslkeys', 'cert.pem'), 'BINARY'),
+ (os.path.join('sslkeys', 'key.pem'), os.path.join(srcPath, 'sslkeys', 'key.pem'), 'BINARY')
+ ]
-with open(os.path.join(srcPath, 'version.py'), 'rt') as f:
- softwareVersion = f.readline().split('\'')[1]
fname = 'Bitmessage_%s_%s.exe' % ("x86" if arch == 32 else "x64", softwareVersion)
if snapshot:
@@ -72,8 +89,18 @@ exe = EXE(pyz,
a.zipfiles,
a.datas,
a.binaries,
+ [],
name=fname,
debug=False,
strip=None,
- upx=False,
- console=False, icon= os.path.join(srcPath, 'images', 'can-icon.ico'))
+ upx=True,
+ console=True, icon= os.path.join(srcPath, 'images', 'can-icon.ico'))
+
+coll = COLLECT(exe,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ strip=False,
+ upx=True,
+ name='main')
+
diff --git a/setup.py b/setup.py
index e3f97bac..3a9c7a3c 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,7 @@ EXTRAS_REQUIRE = {
'prctl': ['python_prctl'], # Named threads
'qrcode': ['qrcode'],
'sound;platform_system=="Windows"': ['winsound'],
+ 'tor': ['stem'],
'docs': [
'sphinx', # fab build_docs
'graphviz', # fab build_docs
@@ -147,6 +148,9 @@ if __name__ == "__main__":
'libmessaging ='
'pybitmessage.plugins.indicator_libmessaging [gir]'
],
+ 'bitmessage.proxyconfig': [
+ 'stem = pybitmessage.plugins.proxyconfig_stem [tor]'
+ ],
# 'console_scripts': [
# 'pybitmessage = pybitmessage.bitmessagemain:main'
# ]
diff --git a/src/api.py b/src/api.py
index d3e87dfd..fad5d623 100644
--- a/src/api.py
+++ b/src/api.py
@@ -21,7 +21,6 @@ import json
import random # nosec
import socket
import subprocess
-import threading
import time
from binascii import hexlify, unhexlify
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
@@ -32,7 +31,6 @@ from version import softwareVersion
import defaults
import helper_inbox
import helper_sent
-import helper_threading
import network.stats
import proofofwork
import queues
@@ -44,6 +42,7 @@ from bmconfigparser import BMConfigParser
from debug import logger
from helper_ackPayload import genAckPayload
from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure
+from helper_threading import StoppableThread
from inventory import Inventory
str_chan = '[chan]'
@@ -73,11 +72,10 @@ class StoppableXMLRPCServer(SimpleXMLRPCServer):
# This thread, of which there is only one, runs the API.
-class singleAPI(threading.Thread, helper_threading.StoppableThread):
+class singleAPI(StoppableThread):
"""API thread"""
- def __init__(self):
- threading.Thread.__init__(self, name="singleAPI")
- self.initStop()
+
+ name = "singleAPI"
def stopThread(self):
super(singleAPI, self).stopThread()
diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py
index a5fe29a1..6d80ced0 100755
--- a/src/bitmessagemain.py
+++ b/src/bitmessagemain.py
@@ -88,10 +88,10 @@ def connectToStream(streamNumber):
with knownnodes.knownNodesLock:
if streamNumber not in knownnodes.knownNodes:
knownnodes.knownNodes[streamNumber] = {}
- if streamNumber*2 not in knownnodes.knownNodes:
- knownnodes.knownNodes[streamNumber*2] = {}
- if streamNumber*2+1 not in knownnodes.knownNodes:
- knownnodes.knownNodes[streamNumber*2+1] = {}
+ if streamNumber * 2 not in knownnodes.knownNodes:
+ knownnodes.knownNodes[streamNumber * 2] = {}
+ if streamNumber * 2 + 1 not in knownnodes.knownNodes:
+ knownnodes.knownNodes[streamNumber * 2 + 1] = {}
BMConnectionPool().connectToStream(streamNumber)
@@ -185,11 +185,32 @@ def signal_handler(signum, frame):
class Main:
+ @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()
+ get_plugin('proxyconfig', name=proxy_type)(config)
+ 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):
_fixSocket()
- daemon = BMConfigParser().safeGetBoolean(
- 'bitmessagesettings', 'daemon')
+ config = BMConfigParser()
+ daemon = config.safeGetBoolean('bitmessagesettings', 'daemon')
try:
opts, args = getopt.getopt(
@@ -217,7 +238,6 @@ class Main:
# Fallback: in case when no api command was issued
state.last_api_response = time.time()
# Apply special settings
- config = BMConfigParser()
config.set(
'bitmessagesettings', 'apienabled', 'true')
config.set(
@@ -261,14 +281,14 @@ class Main:
helper_threading.set_thread_name("PyBitmessage")
- state.dandelion = BMConfigParser().safeGetInt('network', 'dandelion')
+ state.dandelion = config.safeGetInt('network', 'dandelion')
# dandelion requires outbound connections, without them,
# stem objects will get stuck forever
- if state.dandelion and not BMConfigParser().safeGetBoolean(
+ if state.dandelion and not config.safeGetBoolean(
'bitmessagesettings', 'sendoutgoingconnections'):
state.dandelion = 0
- if state.testmode or BMConfigParser().safeGetBoolean(
+ if state.testmode or config.safeGetBoolean(
'bitmessagesettings', 'extralowdifficulty'):
defaults.networkDefaultProofOfWorkNonceTrialsPerByte = int(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte / 100)
@@ -302,15 +322,15 @@ class Main:
# Enable object processor and SMTP only if objproc enabled
if state.enableObjProc:
# SMTP delivery thread
- if daemon and BMConfigParser().safeGet(
- "bitmessagesettings", "smtpdeliver", '') != '':
+ if daemon and config.safeGet(
+ 'bitmessagesettings', 'smtpdeliver', '') != '':
from class_smtpDeliver import smtpDeliver
smtpDeliveryThread = smtpDeliver()
smtpDeliveryThread.start()
# SMTP daemon thread
- if daemon and BMConfigParser().safeGetBoolean(
- "bitmessagesettings", "smtpd"):
+ if daemon and config.safeGetBoolean(
+ 'bitmessagesettings', 'smtpd'):
from class_smtpServer import smtpServer
smtpServerThread = smtpServer()
smtpServerThread.start()
@@ -332,7 +352,7 @@ class Main:
shared.reloadMyAddressHashes()
shared.reloadBroadcastSendersForWhichImWatching()
# API is also objproc dependent
- if BMConfigParser().safeGetBoolean('bitmessagesettings', 'apienabled'):
+ if config.safeGetBoolean('bitmessagesettings', 'apienabled'):
import api # pylint: disable=relative-import
singleAPIThread = api.singleAPI()
# close the main program even if there are threads left
@@ -340,11 +360,12 @@ class Main:
singleAPIThread.start()
# start network components if networking is enabled
if state.enableNetwork:
+ self.start_proxyconfig(config)
BMConnectionPool()
asyncoreThread = BMNetworkThread()
asyncoreThread.daemon = True
asyncoreThread.start()
- for i in range(BMConfigParser().getint("threads", "receive")):
+ for i in range(config.getint('threads', 'receive')):
receiveQueueThread = ReceiveQueueThread(i)
receiveQueueThread.daemon = True
receiveQueueThread.start()
@@ -365,8 +386,7 @@ class Main:
state.uploadThread.start()
connectToStream(1)
- if BMConfigParser().safeGetBoolean(
- 'bitmessagesettings', 'upnp'):
+ if config.safeGetBoolean('bitmessagesettings', 'upnp'):
import upnp
upnpThread = upnp.uPnPThread()
upnpThread.start()
@@ -382,7 +402,7 @@ class Main:
bitmessagecurses.runwrapper()
elif state.kivy:
- BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
+ config.remove_option('bitmessagesettings', 'dontconnect')
from bitmessagekivy.mpybit import NavigateApp
state.kivyapp = NavigateApp()
state.kivyapp.run()
@@ -390,13 +410,13 @@ class Main:
import bitmessageqt
bitmessageqt.run()
else:
- BMConfigParser().remove_option('bitmessagesettings', 'dontconnect')
+ config.remove_option('bitmessagesettings', 'dontconnect')
if daemon:
while state.shutdown == 0:
time.sleep(1)
- if (state.testmode and
- time.time() - state.last_api_response >= 30):
+ if (
+ state.testmode and time.time() - state.last_api_response >= 30):
self.stop()
elif not state.enableGUI:
from tests import core as test_core # pylint: disable=relative-import
diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py
index 507e6ca0..2c5f1485 100644
--- a/src/bitmessageqt/__init__.py
+++ b/src/bitmessageqt/__init__.py
@@ -3270,8 +3270,8 @@ class MyForm(settingsmixin.SMainWindow):
tableWidget.model().removeRows(r.topRow(), r.bottomRow()-r.topRow()+1)
idCount = len(inventoryHashesToTrash)
sqlExecuteChunked(
- "DELETE FROM inbox" if folder == "trash" or shifted else
- "UPDATE inbox SET folder='trash'"
+ ("DELETE FROM inbox" if folder == "trash" or shifted else
+ "UPDATE inbox SET folder='trash'") +
" WHERE msgid IN ({0})", idCount, *inventoryHashesToTrash)
tableWidget.selectRow(0 if currentRow == 0 else currentRow - 1)
tableWidget.setUpdatesEnabled(True)
diff --git a/src/bitmessageqt/safehtmlparser.py b/src/bitmessageqt/safehtmlparser.py
index d1d7910c..edacd4bb 100644
--- a/src/bitmessageqt/safehtmlparser.py
+++ b/src/bitmessageqt/safehtmlparser.py
@@ -1,51 +1,73 @@
-from HTMLParser import HTMLParser
+"""Subclass of HTMLParser.HTMLParser for MessageView widget"""
+
import inspect
import re
-from urllib import quote, quote_plus
+from HTMLParser import HTMLParser
+
+from urllib import quote_plus
from urlparse import urlparse
+
class SafeHTMLParser(HTMLParser):
+ """HTML parser with sanitisation"""
# from html5lib.sanitiser
- acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area',
- 'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button',
- 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
- 'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
- 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
- 'figcaption', 'figure', 'footer', 'font', 'header', 'h1',
- 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins',
- 'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
- 'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
- 'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
- 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong',
- 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
- 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video']
- replaces_pre = [["&", "&"], ["\"", """], ["<", "<"], [">", ">"]]
- replaces_post = [["\n", "
"], ["\t", " "], [" ", " "], [" ", " "], ["
", "
"]]
- src_schemes = [ "data" ]
- #uriregex1 = re.compile(r'(?i)\b((?:(https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?]))')
- uriregex1 = re.compile(r'((https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
+ acceptable_elements = (
+ 'a', 'abbr', 'acronym', 'address', 'area',
+ 'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button',
+ 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
+ 'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
+ 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
+ 'figcaption', 'figure', 'footer', 'font', 'header', 'h1',
+ 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'ins',
+ 'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
+ 'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
+ 'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
+ 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong',
+ 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
+ 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video'
+ )
+ replaces_pre = (
+ ("&", "&"), ("\"", """), ("<", "<"), (">", ">"))
+ replaces_post = (
+ ("\n", "
"), ("\t", " "),
+ (" ", " "), (" ", " "), ("
", "
"))
+ src_schemes = ["data"]
+ # uriregex1 = re.compile(
+ # r'(?i)\b((?:(https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])'
+ # r'|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)'
+ # r'(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))'
+ # r'+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?]))')
+ uriregex1 = re.compile(
+ r'((https?|ftp|bitcoin):(?:/{1,3}|[a-z0-9%])'
+ r'(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)'
+ )
uriregex2 = re.compile(r' 1 and text[0] == " ":
text = " " + text[1:]
return text
def __init__(self, *args, **kwargs):
HTMLParser.__init__(self, *args, **kwargs)
+ self.reset()
self.reset_safe()
-
+
def reset_safe(self):
+ """Reset runtime variables specific to this class"""
self.elements = set()
self.raw = u""
self.sanitised = u""
@@ -53,8 +75,9 @@ class SafeHTMLParser(HTMLParser):
self.allow_picture = False
self.allow_external_src = False
- def add_if_acceptable(self, tag, attrs = None):
- if tag not in SafeHTMLParser.acceptable_elements:
+ def add_if_acceptable(self, tag, attrs=None):
+ """Add tag if it passes sanitisation"""
+ if tag not in self.acceptable_elements:
return
self.sanitised += "<"
if inspect.stack()[1][3] == "handle_endtag":
@@ -66,7 +89,7 @@ class SafeHTMLParser(HTMLParser):
val = ""
elif attr == "src" and not self.allow_external_src:
url = urlparse(val)
- if url.scheme not in SafeHTMLParser.src_schemes:
+ if url.scheme not in self.src_schemes:
val = ""
self.sanitised += " " + quote_plus(attr)
if not (val is None):
@@ -74,26 +97,26 @@ class SafeHTMLParser(HTMLParser):
if inspect.stack()[1][3] == "handle_startendtag":
self.sanitised += "/"
self.sanitised += ">"
-
+
def handle_starttag(self, tag, attrs):
- if tag in SafeHTMLParser.acceptable_elements:
+ if tag in self.acceptable_elements:
self.has_html = True
self.add_if_acceptable(tag, attrs)
def handle_endtag(self, tag):
self.add_if_acceptable(tag)
-
+
def handle_startendtag(self, tag, attrs):
- if tag in SafeHTMLParser.acceptable_elements:
+ if tag in self.acceptable_elements:
self.has_html = True
self.add_if_acceptable(tag, attrs)
-
+
def handle_data(self, data):
self.sanitised += data
-
+
def handle_charref(self, name):
self.sanitised += "" + name + ";"
-
+
def handle_entityref(self, name):
self.sanitised += "&" + name + ";"
@@ -104,15 +127,14 @@ class SafeHTMLParser(HTMLParser):
data = unicode(data, 'utf-8', errors='replace')
HTMLParser.feed(self, data)
tmp = SafeHTMLParser.replace_pre(data)
- tmp = SafeHTMLParser.uriregex1.sub(
- r'\1',
- tmp)
- tmp = SafeHTMLParser.uriregex2.sub(r'\1', tmp)
+ tmp = self.uriregex1.sub(r'\1', tmp)
+ tmp = self.uriregex2.sub(r'\1', tmp)
tmp = SafeHTMLParser.replace_post(tmp)
self.raw += tmp
- def is_html(self, text = None, allow_picture = False):
+ def is_html(self, text=None, allow_picture=False):
+ """Detect if string contains HTML tags"""
if text:
self.reset()
self.reset_safe()
diff --git a/src/build_osx.py b/src/build_osx.py
index 1d8f470e..7ab74dc2 100644
--- a/src/build_osx.py
+++ b/src/build_osx.py
@@ -1,3 +1,4 @@
+"""Building osx."""
from glob import glob
import os
from PyQt4 import QtCore
@@ -17,15 +18,15 @@ DATA_FILES = [
]
setup(
- name = name,
- version = version,
- app = mainscript,
- data_files = DATA_FILES,
- setup_requires = ["py2app"],
- options = dict(
- py2app = dict(
- includes = ['sip', 'PyQt4._qt'],
- iconfile = "images/bitmessage.icns"
+ name=name,
+ version=version,
+ app=mainscript,
+ data_files=DATA_FILES,
+ setup_requires=["py2app"],
+ options=dict(
+ py2app=dict(
+ includes=['sip', 'PyQt4._qt'],
+ iconfile="images/bitmessage.icns"
)
)
)
diff --git a/src/buildozer.spec b/src/buildozer.spec
index 82858554..25080ba6 100644
--- a/src/buildozer.spec
+++ b/src/buildozer.spec
@@ -284,4 +284,4 @@ warn_on_root = 1
#
# Then, invoke the command line with the "demo" profile:
#
-#buildozer --profile demo android debug
+#buildozer --profile demo android debug
\ No newline at end of file
diff --git a/src/class_addressGenerator.py b/src/class_addressGenerator.py
index ac4c53da..b5c0cbcd 100644
--- a/src/class_addressGenerator.py
+++ b/src/class_addressGenerator.py
@@ -1,6 +1,5 @@
import time
-import threading
import hashlib
from binascii import hexlify
from pyelliptic import arithmetic
@@ -18,12 +17,9 @@ from fallback import RIPEMD160Hash
from helper_threading import StoppableThread
-class addressGenerator(threading.Thread, StoppableThread):
+class addressGenerator(StoppableThread):
- def __init__(self):
- # QThread.__init__(self, parent)
- threading.Thread.__init__(self, name="addressGenerator")
- self.initStop()
+ name = "addressGenerator"
def stopThread(self):
try:
diff --git a/src/class_objectProcessor.py b/src/class_objectProcessor.py
index 720cdf02..1a9c0d81 100644
--- a/src/class_objectProcessor.py
+++ b/src/class_objectProcessor.py
@@ -35,12 +35,13 @@ class objectProcessor(threading.Thread):
objects (msg, broadcast, pubkey, getpubkey) from the receiveDataThreads.
"""
def __init__(self):
+ threading.Thread.__init__(self, name="objectProcessor")
+ random.seed()
# It may be the case that the last time Bitmessage was running,
# the user closed it before it finished processing everything in the
# objectProcessorQueue. Assuming that Bitmessage wasn't closed
# forcefully, it should have saved the data in the queue into the
# objectprocessorqueue table. Let's pull it out.
- threading.Thread.__init__(self, name="objectProcessor")
queryreturn = sqlQuery(
'''SELECT objecttype, data FROM objectprocessorqueue''')
for row in queryreturn:
diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py
index 2e9d1c3f..eb67fcef 100644
--- a/src/class_singleCleaner.py
+++ b/src/class_singleCleaner.py
@@ -21,7 +21,6 @@ resends msg messages in 5 days (then 10 days, then 20 days, etc...)
import gc
import os
import shared
-import threading
import time
import tr
@@ -36,14 +35,11 @@ import queues
import state
-class singleCleaner(threading.Thread, StoppableThread):
+class singleCleaner(StoppableThread):
+ name = "singleCleaner"
cycleLength = 300
expireDiscoveredPeers = 300
- def __init__(self):
- threading.Thread.__init__(self, name="singleCleaner")
- self.initStop()
-
def run(self):
gc.disable()
timeWeLastClearedInventoryAndPubkeysTables = 0
diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py
index 6ed7c32e..5c33af58 100644
--- a/src/class_singleWorker.py
+++ b/src/class_singleWorker.py
@@ -7,7 +7,6 @@ src/class_singleWorker.py
from __future__ import division
import hashlib
-import threading
import time
from binascii import hexlify, unhexlify
from struct import pack
@@ -45,12 +44,11 @@ def sizeof_fmt(num, suffix='h/s'):
return "%.1f%s%s" % (num, 'Yi', suffix)
-class singleWorker(threading.Thread, StoppableThread):
+class singleWorker(StoppableThread):
"""Thread for performing PoW"""
def __init__(self):
- threading.Thread.__init__(self, name="singleWorker")
- self.initStop()
+ super(singleWorker, self).__init__(name="singleWorker")
proofofwork.init()
def stopThread(self):
diff --git a/src/class_smtpDeliver.py b/src/class_smtpDeliver.py
index ef7a4363..fa607220 100644
--- a/src/class_smtpDeliver.py
+++ b/src/class_smtpDeliver.py
@@ -6,7 +6,6 @@ src/class_smtpDeliver.py
import smtplib
import sys
-import threading
import urlparse
from email.header import Header
from email.mime.text import MIMEText
@@ -20,14 +19,11 @@ from helper_threading import StoppableThread
SMTPDOMAIN = "bmaddr.lan"
-class smtpDeliver(threading.Thread, StoppableThread):
+class smtpDeliver(StoppableThread):
"""SMTP client thread for delivery"""
+ name = "smtpDeliver"
_instance = None
- def __init__(self):
- threading.Thread.__init__(self, name="smtpDeliver")
- self.initStop()
-
def stopThread(self):
try:
queues.UISignallerQueue.put(("stopThread", "data")) # pylint: disable=no-member
diff --git a/src/class_smtpServer.py b/src/class_smtpServer.py
index 216d35be..d87ab69b 100644
--- a/src/class_smtpServer.py
+++ b/src/class_smtpServer.py
@@ -154,10 +154,10 @@ class smtpServerPyBitmessage(smtpd.SMTPServer):
continue
return
-class smtpServer(threading.Thread, StoppableThread):
+
+class smtpServer(StoppableThread):
def __init__(self, parent=None):
- threading.Thread.__init__(self, name="smtpServerThread")
- self.initStop()
+ super(smtpServer, self).__init__(name="smtpServerThread")
self.server = smtpServerPyBitmessage(('127.0.0.1', LISTENPORT), None)
def stopThread(self):
diff --git a/src/helper_random.py b/src/helper_random.py
index bb173d1b..57f0ccb3 100644
--- a/src/helper_random.py
+++ b/src/helper_random.py
@@ -6,6 +6,11 @@ from pyelliptic.openssl import OpenSSL
NoneType = type(None)
+def seed():
+ """Initialize random number generator"""
+ random.seed()
+
+
def randomBytes(n):
"""Method randomBytes."""
try:
diff --git a/src/helper_threading.py b/src/helper_threading.py
index 6b6a5e25..4b0a074e 100644
--- a/src/helper_threading.py
+++ b/src/helper_threading.py
@@ -1,7 +1,9 @@
"""Helper threading perform all the threading operations."""
-from contextlib import contextmanager
import threading
+from contextlib import contextmanager
+
+import helper_random
try:
import prctl
@@ -22,7 +24,16 @@ else:
threading.Thread._Thread__bootstrap = _thread_name_hack
-class StoppableThread(object):
+class StoppableThread(threading.Thread):
+ name = None
+
+ def __init__(self, name=None):
+ if name:
+ self.name = name
+ super(StoppableThread, self).__init__(name=self.name)
+ self.initStop()
+ helper_random.seed()
+
def initStop(self):
self.stop = threading.Event()
self._stopped = False
@@ -35,6 +46,7 @@ class StoppableThread(object):
class BusyError(threading.ThreadError):
pass
+
@contextmanager
def nonBlocking(lock):
locked = lock.acquire(False)
diff --git a/src/images/default_identicon/BM-2cXewfSjCgGMPB5qYwUSCD2Q57bGLWgD3C.png b/src/images/default_identicon/BM-2cXewfSjCgGMPB5qYwUSCD2Q57bGLWgD3C.png
new file mode 100644
index 00000000..c886f8fc
Binary files /dev/null and b/src/images/default_identicon/BM-2cXewfSjCgGMPB5qYwUSCD2Q57bGLWgD3C.png differ
diff --git a/src/knownnodes.py b/src/knownnodes.py
index 5d1c003d..f4e00b90 100644
--- a/src/knownnodes.py
+++ b/src/knownnodes.py
@@ -98,7 +98,7 @@ def saveKnownNodes(dirName=None):
def addKnownNode(stream, peer, lastseen=None, is_self=False):
knownNodes[stream][peer] = {
"lastseen": lastseen or time.time(),
- "rating": 0,
+ "rating": 1 if is_self else 0,
"self": is_self,
}
diff --git a/src/network/addrthread.py b/src/network/addrthread.py
index 5b0ea638..9f516e80 100644
--- a/src/network/addrthread.py
+++ b/src/network/addrthread.py
@@ -1,18 +1,13 @@
import Queue
-import threading
-import addresses
from helper_threading import StoppableThread
from network.connectionpool import BMConnectionPool
from queues import addrQueue
-import protocol
import state
-class AddrThread(threading.Thread, StoppableThread):
- def __init__(self):
- threading.Thread.__init__(self, name="AddrBroadcaster")
- self.initStop()
- self.name = "AddrBroadcaster"
+
+class AddrThread(StoppableThread):
+ name = "AddrBroadcaster"
def run(self):
while not state.shutdown:
@@ -28,7 +23,7 @@ class AddrThread(threading.Thread, StoppableThread):
except KeyError:
continue
- #finish
+ # finish
addrQueue.iterate()
for i in range(len(chunk)):
diff --git a/src/network/announcethread.py b/src/network/announcethread.py
index a94eeb36..59fad128 100644
--- a/src/network/announcethread.py
+++ b/src/network/announcethread.py
@@ -1,4 +1,7 @@
-import threading
+"""
+src/network/announcethread.py
+=================================
+"""
import time
from bmconfigparser import BMConfigParser
@@ -9,11 +12,11 @@ from network.connectionpool import BMConnectionPool
from network.udp import UDPSocket
import state
-class AnnounceThread(threading.Thread, StoppableThread):
+
+class AnnounceThread(StoppableThread):
+ """A thread to manage regular announcing of this node"""
def __init__(self):
- threading.Thread.__init__(self, name="Announcer")
- self.initStop()
- self.name = "Announcer"
+ super(AnnounceThread, self).__init__(name="Announcer")
logger.info("init announce thread")
def run(self):
@@ -26,10 +29,15 @@ class AnnounceThread(threading.Thread, StoppableThread):
if processed == 0:
self.stop.wait(10)
- def announceSelf(self):
+ @staticmethod
+ def announceSelf():
+ """Announce our presence"""
for connection in BMConnectionPool().udpSockets.values():
if not connection.announcing:
continue
for stream in state.streamsInWhichIAmParticipating:
- addr = (stream, state.Peer('127.0.0.1', BMConfigParser().safeGetInt("bitmessagesettings", "port")), time.time())
+ addr = (
+ stream,
+ state.Peer('127.0.0.1', BMConfigParser().safeGetInt("bitmessagesettings", "port")),
+ time.time())
connection.append_write_buf(BMProto.assembleAddr([addr]))
diff --git a/src/network/bmproto.py b/src/network/bmproto.py
index c8efe91e..0a2cdc7e 100644
--- a/src/network/bmproto.py
+++ b/src/network/bmproto.py
@@ -1,3 +1,8 @@
+"""
+src/network/bmproto.py
+==================================
+"""
+# pylint: disable=attribute-defined-outside-init
import base64
import hashlib
import socket
@@ -43,6 +48,7 @@ class BMProtoExcessiveDataError(BMProtoError):
class BMProto(AdvancedDispatcher, ObjectTracker):
"""A parser for the Bitmessage Protocol"""
+ # pylint: disable=too-many-instance-attributes, too-many-public-methods
# ~1.6 MB which is the maximum possible size of an inv message.
maxMessageSize = 1600100
# 2**18 = 256kB is the maximum size of an object payload
@@ -57,7 +63,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
maxTimeOffset = 3600
timeOffsetWrongCount = 0
- def __init__(self, address=None, sock=None):
+ def __init__(self, address=None, sock=None): # pylint: disable=unused-argument, super-init-not-called
AdvancedDispatcher.__init__(self, sock)
self.isOutbound = False
# packet/connection from a local IP
@@ -97,7 +103,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
length=protocol.Header.size, expectBytes=self.payloadLength)
return True
- def state_bm_command(self):
+ def state_bm_command(self): # pylint: disable=too-many-branches
"""Process incoming command"""
self.payload = self.read_buf[:self.payloadLength]
if self.checksum != hashlib.sha512(self.payload).digest()[0:4]:
@@ -181,7 +187,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return Node(services, host, port)
- def decode_payload_content(self, pattern="v"):
+ def decode_payload_content(self, pattern="v"): # pylint: disable=too-many-branches, too-many-statements
+
"""
Decode the payload depending on pattern:
@@ -197,7 +204,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
, = end of array
"""
- def decode_simple(self, char="v"):
+ def decode_simple(self, char="v"): # pylint: disable=inconsistent-return-statements
"""Decode the payload using one char pattern"""
if char == "v":
return self.decode_payload_varint()
@@ -230,7 +237,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
while True:
i = parserStack[-1][3][parserStack[-1][4]]
if i in "0123456789" and (
- size is None or parserStack[-1][3][parserStack[-1][4] - 1]
+ size is None or parserStack[-1][3][parserStack[-1][4] - 1]
not in "lL"):
try:
size = size * 10 + int(i)
@@ -251,6 +258,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
for j in range(parserStack[-1][4], len(parserStack[-1][3])):
if parserStack[-1][3][j] not in "lL0123456789":
break
+ # pylint: disable=undefined-loop-variable
parserStack.append([
size, size, isArray,
parserStack[-1][3][parserStack[-1][4]:j + 1], 0, []
@@ -422,16 +430,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
def bm_command_addr(self):
"""Incoming addresses, process them"""
- addresses = self._decode_addr()
+ addresses = self._decode_addr() # pylint: disable=redefined-outer-name
for i in addresses:
seenTime, stream, services, ip, port = i
decodedIP = protocol.checkIPAddress(str(ip))
if stream not in state.streamsInWhichIAmParticipating:
continue
if (
- decodedIP and time.time() - seenTime > 0 and
- seenTime > time.time() - BMProto.addressAlive and
- port > 0
+ decodedIP and time.time() - seenTime > 0 and
+ seenTime > time.time() - BMProto.addressAlive and
+ port > 0
):
peer = state.Peer(decodedIP, port)
try:
@@ -462,7 +470,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.append_write_buf(protocol.CreatePacket('pong'))
return True
- def bm_command_pong(self):
+ def bm_command_pong(self): # pylint: disable=no-self-use
"""
Incoming pong.
Ignore it. PyBitmessage pings connections after about 5 minutes
@@ -530,7 +538,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
length=self.payloadLength, expectBytes=0)
return False
- def peerValidityChecks(self):
+ def peerValidityChecks(self): # pylint: disable=too-many-return-statements
"""Check the validity of the peer"""
if self.remoteProtocolVersion < 3:
self.append_write_buf(protocol.assembleErrorMessage(
@@ -584,12 +592,12 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# incoming from a peer we're connected to as outbound,
# or server full report the same error to counter deanonymisation
if (
- state.Peer(self.destination.host, self.peerNode.port) in
- connectionpool.BMConnectionPool().inboundConnections or
- len(connectionpool.BMConnectionPool().inboundConnections) +
- len(connectionpool.BMConnectionPool().outboundConnections) >
- BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") +
- BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections")
+ state.Peer(self.destination.host, self.peerNode.port) in
+ connectionpool.BMConnectionPool().inboundConnections or
+ len(connectionpool.BMConnectionPool().inboundConnections) +
+ len(connectionpool.BMConnectionPool().outboundConnections) >
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") +
+ BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections")
):
self.append_write_buf(protocol.assembleErrorMessage(
errorText="Server full, please try again later.", fatal=2))
@@ -636,8 +644,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
def stopDownloadingObject(hashId, forwardAnyway=False):
"""Stop downloading an object"""
for connection in (
- connectionpool.BMConnectionPool().inboundConnections.values() +
- connectionpool.BMConnectionPool().outboundConnections.values()
+ connectionpool.BMConnectionPool().inboundConnections.values() +
+ connectionpool.BMConnectionPool().outboundConnections.values()
):
try:
del connection.objectsNewToMe[hashId]
diff --git a/src/network/connectionpool.py b/src/network/connectionpool.py
index 077f44a5..461c2b77 100644
--- a/src/network/connectionpool.py
+++ b/src/network/connectionpool.py
@@ -1,3 +1,7 @@
+"""
+src/network/connectionpool.py
+==================================
+"""
import errno
import re
import socket
@@ -20,6 +24,7 @@ from udp import UDPSocket
@Singleton
+# pylint: disable=too-many-instance-attributes
class BMConnectionPool(object):
"""Pool of all existing connections"""
def __init__(self):
@@ -68,8 +73,8 @@ class BMConnectionPool(object):
def isAlreadyConnected(self, nodeid):
"""Check if we're already connected to this peer"""
for i in (
- self.inboundConnections.values() +
- self.outboundConnections.values()
+ self.inboundConnections.values() +
+ self.outboundConnections.values()
):
try:
if nodeid == i.nodeid:
@@ -113,7 +118,8 @@ class BMConnectionPool(object):
pass
connection.handle_close()
- def getListeningIP(self):
+ @staticmethod
+ def getListeningIP():
"""What IP are we supposed to be listening on?"""
if BMConfigParser().safeGet(
"bitmessagesettings", "onionhostname").endswith(".onion"):
@@ -123,8 +129,8 @@ class BMConnectionPool(object):
host = '127.0.0.1'
if (BMConfigParser().safeGetBoolean(
"bitmessagesettings", "sockslisten") or
- BMConfigParser().safeGet(
- "bitmessagesettings", "socksproxytype") == "none"):
+ BMConfigParser().safeGet(
+ "bitmessagesettings", "socksproxytype") == "none"):
# python doesn't like bind + INADDR_ANY?
# host = socket.INADDR_ANY
host = BMConfigParser().get("network", "bind")
@@ -154,7 +160,7 @@ class BMConnectionPool(object):
udpSocket = UDPSocket(host=bind, announcing=True)
self.udpSockets[udpSocket.listening.host] = udpSocket
- def loop(self):
+ def loop(self): # pylint: disable=too-many-branches, too-many-statements
"""Main Connectionpool's loop"""
# defaults to empty loop if outbound connections are maxed
spawnConnections = False
@@ -170,12 +176,13 @@ class BMConnectionPool(object):
onionsocksproxytype = BMConfigParser().safeGet(
'bitmessagesettings', 'onionsocksproxytype', '')
if (socksproxytype[:5] == 'SOCKS' and
- not BMConfigParser().safeGetBoolean(
- 'bitmessagesettings', 'sockslisten') and
- '.onion' not in BMConfigParser().safeGet(
- 'bitmessagesettings', 'onionhostname', '')):
+ not BMConfigParser().safeGetBoolean(
+ 'bitmessagesettings', 'sockslisten') and
+ '.onion' not in BMConfigParser().safeGet(
+ 'bitmessagesettings', 'onionhostname', '')):
acceptConnections = False
+ # pylint: disable=too-many-nested-blocks
if spawnConnections:
if not knownnodes.knownNodesActual:
helper_bootstrap.dns()
@@ -241,8 +248,8 @@ class BMConnectionPool(object):
self.lastSpawned = time.time()
else:
for i in (
- self.inboundConnections.values() +
- self.outboundConnections.values()
+ self.inboundConnections.values() +
+ self.outboundConnections.values()
):
# FIXME: rating will be increased after next connection
i.handle_close()
@@ -253,8 +260,8 @@ class BMConnectionPool(object):
self.startListening()
else:
for bind in re.sub(
- "[^\w.]+", " ",
- BMConfigParser().safeGet('network', 'bind')
+ '[^\w.]+', ' ', # pylint: disable=anomalous-backslash-in-string
+ BMConfigParser().safeGet('network', 'bind')
).split():
self.startListening(bind)
logger.info('Listening for incoming connections.')
@@ -263,8 +270,8 @@ class BMConnectionPool(object):
self.startUDPSocket()
else:
for bind in re.sub(
- "[^\w.]+", " ",
- BMConfigParser().safeGet('network', 'bind')
+ '[^\w.]+', ' ', # pylint: disable=anomalous-backslash-in-string
+ BMConfigParser().safeGet('network', 'bind')
).split():
self.startUDPSocket(bind)
self.startUDPSocket(False)
@@ -288,8 +295,8 @@ class BMConnectionPool(object):
reaper = []
for i in (
- self.inboundConnections.values() +
- self.outboundConnections.values()
+ self.inboundConnections.values() +
+ self.outboundConnections.values()
):
minTx = time.time() - 20
if i.fullyEstablished:
@@ -302,10 +309,10 @@ class BMConnectionPool(object):
time.time() - i.lastTx)
i.set_state("close")
for i in (
- self.inboundConnections.values() +
- self.outboundConnections.values() +
- self.listeningSockets.values() +
- self.udpSockets.values()
+ self.inboundConnections.values() +
+ self.outboundConnections.values() +
+ self.listeningSockets.values() +
+ self.udpSockets.values()
):
if not (i.accepting or i.connecting or i.connected):
reaper.append(i)
diff --git a/src/network/dandelion.py b/src/network/dandelion.py
index 2678ca57..fa9081cb 100644
--- a/src/network/dandelion.py
+++ b/src/network/dandelion.py
@@ -1,3 +1,7 @@
+"""
+src/network/dandelion.py
+========================
+"""
from collections import namedtuple
from random import choice, sample, expovariate
from threading import RLock
@@ -22,7 +26,7 @@ Stem = namedtuple('Stem', ['child', 'stream', 'timeout'])
@Singleton
-class Dandelion():
+class Dandelion(): # pylint: disable=old-style-class
"""Dandelion class for tracking stem/fluff stages."""
def __init__(self):
# currently assignable child stems
@@ -35,7 +39,8 @@ class Dandelion():
self.refresh = time() + REASSIGN_INTERVAL
self.lock = RLock()
- def poissonTimeout(self, start=None, average=0):
+ @staticmethod
+ def poissonTimeout(start=None, average=0):
"""Generate deadline using Poisson distribution"""
if start is None:
start = time()
@@ -97,8 +102,8 @@ class Dandelion():
for k in (k for k, v in self.nodeMap.iteritems() if v is None):
self.nodeMap[k] = connection
for k, v in {
- k: v for k, v in self.hashMap.iteritems()
- if v.child is None
+ k: v for k, v in self.hashMap.iteritems()
+ if v.child is None
}.iteritems():
self.hashMap[k] = Stem(
connection, v.stream, self.poissonTimeout())
@@ -115,12 +120,12 @@ class Dandelion():
self.stem.remove(connection)
# active mappings to pointing to the removed node
for k in (
- k for k, v in self.nodeMap.iteritems() if v == connection
+ k for k, v in self.nodeMap.iteritems() if v == connection
):
self.nodeMap[k] = None
for k, v in {
- k: v for k, v in self.hashMap.iteritems()
- if v.child == connection
+ k: v for k, v in self.hashMap.iteritems()
+ if v.child == connection
}.iteritems():
self.hashMap[k] = Stem(
None, v.stream, self.poissonTimeout())
diff --git a/src/network/downloadthread.py b/src/network/downloadthread.py
index babce5da..a4b58862 100644
--- a/src/network/downloadthread.py
+++ b/src/network/downloadthread.py
@@ -1,4 +1,7 @@
-import threading
+"""
+src/network/downloadthread.py
+=============================
+"""
import time
import addresses
@@ -12,7 +15,8 @@ from network.connectionpool import BMConnectionPool
from objectracker import missingObjects
-class DownloadThread(threading.Thread, StoppableThread):
+class DownloadThread(StoppableThread):
+ """Thread-based class for downloading from connections"""
minPending = 200
maxRequestChunk = 1000
requestTimeout = 60
@@ -20,13 +24,12 @@ class DownloadThread(threading.Thread, StoppableThread):
requestExpires = 3600
def __init__(self):
- threading.Thread.__init__(self, name="Downloader")
- self.initStop()
- self.name = "Downloader"
+ super(DownloadThread, self).__init__(name="Downloader")
logger.info("init download thread")
self.lastCleaned = time.time()
def cleanPending(self):
+ """Expire pending downloads eventually"""
deadline = time.time() - DownloadThread.requestExpires
try:
toDelete = [k for k, v in missingObjects.iteritems() if v < deadline]
@@ -41,10 +44,13 @@ class DownloadThread(threading.Thread, StoppableThread):
while not self._stopped:
requested = 0
# Choose downloading peers randomly
- connections = [x for x in BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values() if x.fullyEstablished]
+ connections = [
+ x for x in
+ BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values()
+ if x.fullyEstablished]
helper_random.randomshuffle(connections)
try:
- requestChunk = max(int(min(DownloadThread.maxRequestChunk, len(missingObjects)) / len(connections)), 1)
+ requestChunk = max(int(min(DownloadThread.maxRequestChunk, len(missingObjects)) / len(connections)), 1)
except ZeroDivisionError:
requestChunk = 1
for i in connections:
diff --git a/src/network/http-old.py b/src/network/http-old.py
index 56d24915..c97927d9 100644
--- a/src/network/http-old.py
+++ b/src/network/http-old.py
@@ -1,3 +1,7 @@
+"""
+src/network/http-old.py
+=======================
+"""
import asyncore
import socket
import time
@@ -8,6 +12,7 @@ duration = 60
class HTTPClient(asyncore.dispatcher):
+ """An asyncore dispatcher"""
port = 12345
def __init__(self, host, path, connect=True):
@@ -19,31 +24,33 @@ class HTTPClient(asyncore.dispatcher):
self.buffer = 'GET %s HTTP/1.0\r\n\r\n' % path
def handle_close(self):
+ # pylint: disable=global-statement
global requestCount
requestCount += 1
self.close()
def handle_read(self):
-# print self.recv(8192)
+ # print self.recv(8192)
self.recv(8192)
def writable(self):
- return (len(self.buffer) > 0)
+ return len(self.buffer) > 0
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
+
if __name__ == "__main__":
# initial fill
for i in range(parallel):
HTTPClient('127.0.0.1', '/')
start = time.time()
- while (time.time() - start < duration):
- if (len(asyncore.socket_map) < parallel):
+ while time.time() - start < duration:
+ if len(asyncore.socket_map) < parallel:
for i in range(parallel - len(asyncore.socket_map)):
HTTPClient('127.0.0.1', '/')
print "Active connections: %i" % (len(asyncore.socket_map))
- asyncore.loop(count=len(asyncore.socket_map)/2)
+ asyncore.loop(count=len(asyncore.socket_map) / 2)
if requestCount % 100 == 0:
print "Processed %i total messages" % (requestCount)
diff --git a/src/network/http.py b/src/network/http.py
index 55cb81a1..8bba38ac 100644
--- a/src/network/http.py
+++ b/src/network/http.py
@@ -2,15 +2,17 @@ import socket
from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore
-from proxy import Proxy, ProxyError, GeneralProxyError
-from socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error
-from socks4a import Socks4aConnection, Socks4aResolver, Socks4aError
+from proxy import ProxyError
+from socks5 import Socks5Connection, Socks5Resolver
+from socks4a import Socks4aConnection, Socks4aResolver
-class HttpError(ProxyError): pass
+
+class HttpError(ProxyError):
+ pass
class HttpConnection(AdvancedDispatcher):
- def __init__(self, host, path="/"):
+ def __init__(self, host, path="/"): # pylint: disable=redefined-outer-name
AdvancedDispatcher.__init__(self)
self.path = path
self.destination = (host, 80)
@@ -19,13 +21,15 @@ class HttpConnection(AdvancedDispatcher):
print "connecting in background to %s:%i" % (self.destination[0], self.destination[1])
def state_init(self):
- self.append_write_buf("GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (self.path, self.destination[0]))
- print "Sending %ib" % (len(self.write_buf))
+ self.append_write_buf(
+ "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" % (
+ self.path, self.destination[0]))
+ print "Sending %ib" % (len(self.write_buf))
self.set_state("http_request_sent", 0)
return False
def state_http_request_sent(self):
- if len(self.read_buf) > 0:
+ if self.read_buf:
print "Received %ib" % (len(self.read_buf))
self.read_buf = b""
if not self.connected:
@@ -34,7 +38,7 @@ class HttpConnection(AdvancedDispatcher):
class Socks5HttpConnection(Socks5Connection, HttpConnection):
- def __init__(self, host, path="/"):
+ def __init__(self, host, path="/"): # pylint: disable=super-init-not-called, redefined-outer-name
self.path = path
Socks5Connection.__init__(self, address=(host, 80))
@@ -44,7 +48,7 @@ class Socks5HttpConnection(Socks5Connection, HttpConnection):
class Socks4aHttpConnection(Socks4aConnection, HttpConnection):
- def __init__(self, host, path="/"):
+ def __init__(self, host, path="/"): # pylint: disable=super-init-not-called, redefined-outer-name
Socks4aConnection.__init__(self, address=(host, 80))
self.path = path
@@ -55,32 +59,31 @@ class Socks4aHttpConnection(Socks4aConnection, HttpConnection):
if __name__ == "__main__":
# initial fill
-
for host in ("bootstrap8080.bitmessage.org", "bootstrap8444.bitmessage.org"):
proxy = Socks5Resolver(host=host)
- while len(asyncore.socket_map) > 0:
+ while asyncore.socket_map:
print "loop %s, len %i" % (proxy.state, len(asyncore.socket_map))
asyncore.loop(timeout=1, count=1)
proxy.resolved()
proxy = Socks4aResolver(host=host)
- while len(asyncore.socket_map) > 0:
+ while asyncore.socket_map:
print "loop %s, len %i" % (proxy.state, len(asyncore.socket_map))
asyncore.loop(timeout=1, count=1)
proxy.resolved()
for host in ("bitmessage.org",):
direct = HttpConnection(host)
- while len(asyncore.socket_map) > 0:
-# print "loop, state = %s" % (direct.state)
+ while asyncore.socket_map:
+ # print "loop, state = %s" % (direct.state)
asyncore.loop(timeout=1, count=1)
proxy = Socks5HttpConnection(host)
- while len(asyncore.socket_map) > 0:
-# print "loop, state = %s" % (proxy.state)
+ while asyncore.socket_map:
+ # print "loop, state = %s" % (proxy.state)
asyncore.loop(timeout=1, count=1)
proxy = Socks4aHttpConnection(host)
- while len(asyncore.socket_map) > 0:
-# print "loop, state = %s" % (proxy.state)
+ while asyncore.socket_map:
+ # print "loop, state = %s" % (proxy.state)
asyncore.loop(timeout=1, count=1)
diff --git a/src/network/httpd.py b/src/network/httpd.py
index b8b6ba21..b69ffa99 100644
--- a/src/network/httpd.py
+++ b/src/network/httpd.py
@@ -1,28 +1,34 @@
+"""
+src/network/httpd.py
+=======================
+"""
import asyncore
import socket
from tls import TLSHandshake
+
class HTTPRequestHandler(asyncore.dispatcher):
+ """Handling HTTP request"""
response = """HTTP/1.0 200 OK\r
-Date: Sun, 23 Oct 2016 18:02:00 GMT\r
-Content-Type: text/html; charset=UTF-8\r
-Content-Encoding: UTF-8\r
-Content-Length: 136\r
-Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT\r
-Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)\r
-ETag: "3f80f-1b6-3e1cb03b"\r
-Accept-Ranges: bytes\r
-Connection: close\r
-\r
-
-