diff --git a/.gitignore b/.gitignore index 104f54c1..30a4e1e9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ dist docs/_*/* docs/autodoc/ pyan/ +build/scripts-2.7/ +desktop/icons/ diff --git a/INSTALL.md b/INSTALL.md index c140c199..ae9bd601 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -105,4 +105,14 @@ forums for instructions on how to create a package on OS X. Please note that some versions of OS X don't work. #### Windows -## TODO: Create Windows package creation instructions + +Run `python setup.py bdist_wininst` and this will create an appropriate installer executable in `dist/`. + +# Troubleshooting + +## Working in a virtualenv +If you get the error about importing PyQt4 when using a virtualenv, run these commands (adjusted to suit your venv name and site-wide PyQt4 installation) + +`ln -s /usr/lib/python2.7/dist-packages/PyQt4 ~/.virtualenvs/pybitmessage-devops/lib/python2.7/site-packages/` +`ln -s /usr/lib/python2.7/dist-packages/sip.x86_64-linux-gnu.so ~/.virtualenvs/pybitmessage-devops/lib/python2.7/site-packages/` + diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index c820c50d..9067c7e8 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -15,7 +15,28 @@ ### Tests - If there has been a change to the code, there's a good possibility there should be a corresponding change to the tests -- If you can't run `fab tests` successfully, ask for someone to run it against your branch +- If you can't run `fab tests` successfully, ask for someone to run it against your branch. If you can't (and know that this fab task is not working yet), at least make sure the following commands work: + + - Make sure the installer still works (this may be insufficient if files are deleted) and that the installed version + works. + + - `python setup.py install --record files.txt; tr '\n' '\0' < files.txt | xargs -0 rm -f --; python setup.py install` + - `pybitmessage` + + - And as root: + + - `sudo python setup.py install --record files.txt; tr '\n' '\0' < files.txt | xargs -0 sudo rm -f --; sudo python setup.py install` + - `pybitmessage` + + - Make sure the apps still work portably: + + - `./src/bitmessagemain.py` + - `./src/bitmessagecli.py` + + - Make sure the travis test still works (currently just `pybitmessage -t` but check travis.yml for changes + + - Ideally, try this in a virtualenv, as a user with no venv, system-wide as root and on windows with `python setup.py bdist_wininst +; dist/Bitmessage_x64_0.6.3.2.exe` followed by uninstalling through add/remove progragrams and re-installing (actual filename may vary). ## Translations diff --git a/checkdeps.py b/checkdeps.py index c0e10051..7665418f 100755 --- a/checkdeps.py +++ b/checkdeps.py @@ -1,4 +1,5 @@ #!/usr/bin/env python2 +# pylint: disable=no-name-in-module,import-error; PyCQA/pylint/issues/73 """ Check dependendies and give recommendations about how to satisfy them @@ -10,9 +11,15 @@ Limitations: EXTRAS_REQUIRE. This is fine because most developers do, too. """ +from __future__ import print_function + import os import sys from distutils.errors import CompileError +from importlib import import_module + +from src.depends import PACKAGE_MANAGER, PACKAGES, detectOS + try: from setuptools.dist import Distribution from setuptools.extension import Extension @@ -24,10 +31,6 @@ except ImportError: HAVE_SETUPTOOLS = False EXTRAS_REQUIRE = [] -from importlib import import_module - -from src.depends import detectOS, PACKAGES, PACKAGE_MANAGER - COMPILING = { "Debian": "build-essential libssl-dev", @@ -52,10 +55,23 @@ EXTRAS_REQUIRE_DEPS = { "Guix": [""], "Gentoo": ["dev-python/python-prctl"], }, + 'devops': { + "OpenBSD": [""], + "FreeBSD": [""], + "Debian": ["libncurses5-dev"], + "Ubuntu": [""], + "Ubuntu 12": [""], + "openSUSE": [""], + "Fedora": [""], + "Guix": [""], + "Gentoo": [""], + }, + } def detectPrereqs(missing=True): + """Detect pre-requesits of PACKAGES from src.depends""" available = [] for module in PACKAGES: try: @@ -69,6 +85,7 @@ def detectPrereqs(missing=True): def prereqToPackages(): + """Detect OS-specific package depenedncies""" if not detectPrereqs(): return print("%s %s" % ( @@ -77,6 +94,7 @@ def prereqToPackages(): def compilerToPackages(): + """Detect OS-specific compiler packages""" if not detectOS() in COMPILING: return print("%s %s" % ( @@ -84,6 +102,7 @@ def compilerToPackages(): def testCompiler(): + """Test the compiler and dependencies""" if not HAVE_SETUPTOOLS: # silent, we can't test without setuptools return True @@ -140,9 +159,9 @@ for lhs, rhs in EXTRAS_REQUIRE.items(): if OPSYS is None: break if rhs and any([ - EXTRAS_REQUIRE_DEPS[x][OPSYS] - for x in rhs - if x in EXTRAS_REQUIRE_DEPS + EXTRAS_REQUIRE_DEPS[x][OPSYS] + for x in rhs + if x in EXTRAS_REQUIRE_DEPS ]): rhs_cmd = ''.join([ CMD, diff --git a/desktop/icons/24x24/pybitmessage.png b/desktop/icons/24x24/pybitmessage.png new file mode 100644 index 00000000..30f7313e Binary files /dev/null and b/desktop/icons/24x24/pybitmessage.png differ diff --git a/desktop/icons/scalable/pybitmessage.svg b/desktop/icons/scalable/pybitmessage.svg new file mode 100644 index 00000000..7c854e34 --- /dev/null +++ b/desktop/icons/scalable/pybitmessage.svg @@ -0,0 +1,149 @@ + + + + diff --git a/fabfile/app_path.py b/fabfile/app_path.py new file mode 100644 index 00000000..af5f7db8 --- /dev/null +++ b/fabfile/app_path.py @@ -0,0 +1,14 @@ +""" +app_path.py +=========== + +Since fabfile directories are not part of the project they can't see modules such as `version` to update the +documentation versioning for example. + +""" + +import os +import sys + +app_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'src') +sys.path.insert(0, app_dir) diff --git a/fabfile/lib.py b/fabfile/lib.py index 7af40231..d7d85a47 100644 --- a/fabfile/lib.py +++ b/fabfile/lib.py @@ -5,15 +5,14 @@ A library of functions and constants for tasks to make use of. """ import os -import sys import re +import sys from functools import wraps -from fabric.api import run, hide, cd, env +from fabric.api import cd, env, hide, run from fabric.context_managers import settings, shell_env from fabvenv import virtualenv - FABRIC_ROOT = os.path.dirname(__file__) PROJECT_ROOT = os.path.dirname(FABRIC_ROOT) VENV_ROOT = os.path.expanduser(os.path.join('~', '.virtualenvs', 'pybitmessage-devops')) @@ -80,7 +79,12 @@ def filelist_from_git(rev=None): clean = re.sub('\n', '', clean) for line in clean.split('\r'): if line.endswith(".py"): - results.append(os.path.abspath(line)) + with settings(warn_only=True): + with hide('warnings'): + if run('stat {}'.format(os.path.abspath(line))).succeeded: + results.append(os.path.abspath(line)) + else: + print 'Deleted file {} skipped.'.format(line) return results @@ -133,6 +137,22 @@ def pylint(path_to_file): ) +def isort(path_to_file): + """Run isort on a file""" + with virtualenv(VENV_ROOT): + with hide('warnings', 'running', 'stdout', 'stderr'): + with settings(warn_only=True): + with shell_env(PYTHONPATH=PYTHONPATH): + returnable = run( + 'isort {0}'.format( + path_to_file, + ), + ) + # isort takes the view that a sorted file in is an error + returnable.return_code = not returnable.return_code + return returnable + + def autopep8(path_to_file): """Run autopep8 on a file""" with virtualenv(VENV_ROOT): diff --git a/fabfile/requirements.txt b/fabfile/requirements.txt deleted file mode 100644 index 0d0a3962..00000000 --- a/fabfile/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -# These requirements are for the Fabric commands that support devops tasks for PyBitmessage, not for running -# PyBitmessage itself. -# TODO: Consider moving to an extra_requires group in setup.py - -pycodestyle==2.3.1 # https://github.com/PyCQA/pycodestyle/issues/741 -flake8 -pylint --e git://github.com/hhatto/autopep8.git@ver1.2.2#egg=autopep8 # Needed for fixing E712 -pep8 # autopep8 doesn't seem to like pycodestyle diff --git a/fabfile/tasks.py b/fabfile/tasks.py index fb05937d..d57c7660 100644 --- a/fabfile/tasks.py +++ b/fabfile/tasks.py @@ -4,23 +4,23 @@ Fabric tasks for PyBitmessage devops operations. Note that where tasks declare params to be bools, they use coerce_bool() and so will accept any commandline (string) representation of true or false that coerce_bool() understands. - """ import os import sys -from fabric.api import run, task, hide, cd, settings, sudo +from fabric.api import cd, hide, run, settings, sudo, task from fabric.contrib.project import rsync_project from fabvenv import virtualenv -from fabfile.lib import ( - autopep8, PROJECT_ROOT, VENV_ROOT, coerce_bool, flatten, filelist_from_git, default_hosts, - get_filtered_pycodestyle_output, get_filtered_flake8_output, get_filtered_pylint_output, -) +import fabfile.app_path # pylint: disable=unused-import -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))) # noqa:E402 -from version import softwareVersion # pylint: disable=wrong-import-position +from version import softwareVersion + +from fabfile.lib import ( + PROJECT_ROOT, VENV_ROOT, autopep8, coerce_bool, default_hosts, filelist_from_git, flatten, + get_filtered_flake8_output, get_filtered_pycodestyle_output, get_filtered_pylint_output, isort +) def get_tool_results(file_list): @@ -69,6 +69,7 @@ def print_results(results, top, verbose, details): print line if details: + print print "pycodestyle:" for detail in flatten(item['pycodestyle_violations']): print detail @@ -107,7 +108,7 @@ def generate_file_list(filename): if filename: filename = os.path.abspath(filename) if not os.path.exists(filename): - print "Bad filename, specify a Python file" + print "Bad filename {}, specify a Python file".format(filename) sys.exit(1) else: file_list = [filename] @@ -201,8 +202,16 @@ def code_quality(verbose=True, details=False, fix=False, filename=None, top=10, results = get_tool_results(file_list) if fix: - for item in sort_and_slice(results, top): - autopep8(item['path_to_file']) + if filename: + sorted_and_sliced = [{'path_to_file': os.path.abspath(filename)}] + else: + sorted_and_sliced = sort_and_slice(results, top) + + for item in sorted_and_sliced: + path_to_file = item['path_to_file'] + print 'Applying automatic fixes to {}'.format(path_to_file) + isort(path_to_file) + autopep8(path_to_file) # Recalculate results after autopep8 to surprise the user the least results = get_tool_results(file_list) diff --git a/setup.cfg b/setup.cfg index 93efd2d5..49a56c2b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,15 +3,31 @@ [pycodestyle] max-line-length = 119 +ignore = E722,E402 [flake8] max-line-length = 119 -ignore = E722,F841 +ignore = E402,E722,F401,F841 +# E402: pylint is preferred for wrong-import-position # E722: pylint is preferred for bare-except +# F401: pylint is preferred for unused-import # F841: pylint is preferred for unused-variable # pylint honours the [MESSAGES CONTROL] section [MESSAGES CONTROL] -disable=invalid-name,bare-except,broad-except +disable=invalid-name,bare-except,broad-except,ungrouped-imports # invalid-name: needs fixing during a large, project-wide refactor # bare-except,broad-except: Need fixing once thorough testing is easier +# ungrouped-imports: Incompatible with imports-before-froms which seems to be preferred + +[isort] +# https://github.com/timothycrosley/isort/wiki/isort-Settings +combine_star = true +known_app_path = fabfile.app_path +known_pathmagic = pybitmessage.pathmagic +known_pybitmessage = version,src +known_standard_library = distutils.errors +line_length = 119 +multi_line_output = 5 +sections = FUTURE,STDLIB,THIRDPARTY,PATHMAGIC,APP_PATH,PYBITMESSAGE,FIRSTPARTY,LOCALFOLDER +wrap_length = 119 diff --git a/setup.py b/setup.py index 1081b5aa..134da562 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,21 @@ #!/usr/bin/env python2.7 +""" +setup.py +======== + +Install the pybitmessage package and dependencies. +""" + +from __future__ import print_function import os import shutil -from setuptools import setup, Extension +from setuptools import Extension, setup from setuptools.command.install import install from src.version import softwareVersion - EXTRAS_REQUIRE = { 'gir': ['pygobject'], 'notify2': ['notify2'], @@ -16,17 +23,25 @@ EXTRAS_REQUIRE = { 'prctl': ['python_prctl'], # Named threads 'qrcode': ['qrcode'], 'sound;platform_system=="Windows"': ['winsound'], - 'docs': [ - 'sphinx', # fab build_docs + 'devops': [ + 'autopep8', # fab code_quality + 'fabric==1.14.0', + 'fabric-virtualenv', + 'flake8==3.4.1', # https://github.com/PyCQA/pycodestyle/issues/741 'graphviz', # fab build_docs - 'curses', # src/depends.py - 'python2-pythondialog', # src/depends.py + 'isort', # fab code_quality 'm2r', # fab build_docs + 'pycodestyle==2.3.1', # https://github.com/PyCQA/pycodestyle/issues/741 + 'pylint', # fab code_quality + 'python2-pythondialog', # src/depends.py + 'sphinx', # fab build_docs ], } class InstallCmd(install): + """Install PyBitmessage""" + def run(self): # prepare icons directories try: @@ -46,6 +61,7 @@ class InstallCmd(install): if __name__ == "__main__": + here = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(here, 'README.md')) as f: README = f.read() @@ -63,21 +79,21 @@ if __name__ == "__main__": 'pybitmessage.bitmessagecurses', 'pybitmessage.messagetypes', 'pybitmessage.network', + 'pybitmessage.plugins', 'pybitmessage.pyelliptic', 'pybitmessage.socks', 'pybitmessage.storage', - 'pybitmessage.plugins' ] # this will silently accept alternative providers of msgpack # if they are already installed try: - import msgpack + import msgpack # pylint: disable=unused-import installRequires.append("msgpack-python") except ImportError: try: - import umsgpack + import umsgpack # pylint: disable=unused-import installRequires.append("umsgpack") except ImportError: packages += ['pybitmessage.fallback', 'pybitmessage.fallback.umsgpack'] @@ -89,12 +105,10 @@ if __name__ == "__main__": "a P2P communications protocol", long_description=README, license='MIT', - # TODO: add author info - #author='', - #author_email='', + author='The Bitmessage Team', + author_email='surda@economicsofbitcoin.com', url='https://bitmessage.org', - # TODO: add keywords - #keywords='', + keywords='bitmessage pybitmessage', install_requires=installRequires, extras_require=EXTRAS_REQUIRE, classifiers=[ @@ -114,11 +128,11 @@ if __name__ == "__main__": ]}, data_files=[ ('share/applications/', - ['desktop/pybitmessage.desktop']), + ['desktop/pybitmessage.desktop']), ('share/icons/hicolor/scalable/apps/', - ['desktop/icons/scalable/pybitmessage.svg']), + ['desktop/icons/scalable/pybitmessage.svg']), ('share/icons/hicolor/24x24/apps/', - ['desktop/icons/24x24/pybitmessage.png']) + ['desktop/icons/24x24/pybitmessage.png']) ], ext_modules=[bitmsghash], zip_safe=False, @@ -141,9 +155,6 @@ if __name__ == "__main__": 'libmessaging =' 'pybitmessage.plugins.indicator_libmessaging [gir]' ], - # 'console_scripts': [ - # 'pybitmessage = pybitmessage.bitmessagemain:main' - # ] }, scripts=['src/pybitmessage'], cmdclass={'install': InstallCmd} diff --git a/src/api.py b/src/api.py index a32519e4..ea899e02 100644 --- a/src/api.py +++ b/src/api.py @@ -1,11 +1,14 @@ +# pylint: disable=too-many-locals,too-many-lines,no-self-use,too-many-public-methods,too-many-branches +# pylint: disable=too-many-statements +""" # Copyright (c) 2012-2016 Jonathan Warren # Copyright (c) 2012-2018 The Bitmessage developers -""" This is not what you run to run the Bitmessage API. Instead, enable the API ( https://bitmessage.org/wiki/API ) and optionally enable daemon mode ( https://bitmessage.org/wiki/Daemon ) then run bitmessagemain.py. """ +from __future__ import absolute_import import base64 import hashlib @@ -15,34 +18,30 @@ from binascii import hexlify, unhexlify from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer from struct import pack -import shared -from addresses import ( - decodeAddress, addBMIfNotPresent, decodeVarint, - calculateInventoryHash, varintDecodeError) -from bmconfigparser import BMConfigParser +from version import softwareVersion + import defaults import helper_inbox import helper_sent - -import state -import queues -import shutdown import network.stats - -# Classes -from helper_sql import sqlQuery, sqlExecute, SqlBulkExecute, sqlStoredProcedure -from helper_ackPayload import genAckPayload -from debug import logger -from inventory import Inventory -from version import softwareVersion - -# Helper Functions import proofofwork +import queues +import shared +import shutdown +import state +from addresses import addBMIfNotPresent, calculateInventoryHash, decodeAddress, decodeVarint, varintDecodeError +from bmconfigparser import BMConfigParser +from debug import logger +from helper_ackPayload import genAckPayload +from helper_sql import SqlBulkExecute, sqlExecute, sqlQuery, sqlStoredProcedure +from inventory import Inventory str_chan = '[chan]' class APIError(Exception): + """APIError exception class""" + def __init__(self, error_number, error_message): super(APIError, self).__init__() self.error_number = error_number @@ -53,26 +52,34 @@ class APIError(Exception): class StoppableXMLRPCServer(SimpleXMLRPCServer): + """A SimpleXMLRPCServer that honours state.shutdown""" allow_reuse_address = True def serve_forever(self): + """Start the SimpleXMLRPCServer""" + # pylint: disable=arguments-differ while state.shutdown == 0: self.handle_request() -# This is one of several classes that constitute the API -# This class was written by Vaibhav Bhatia. -# Modified by Jonathan Warren (Atheros). -# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): + """ + This is one of several classes that constitute the API + + This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros). + http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/ + """ def do_POST(self): - # Handles the HTTP POST request. - # Attempts to interpret all HTTP POST requests as XML-RPC calls, - # which are forwarded to the server's _dispatch method for handling. + """ + Handles the HTTP POST request. - # Note: this method is the same as in SimpleXMLRPCRequestHandler, - # just hacked to handle cookies + Attempts to interpret all HTTP POST requests as XML-RPC calls, + which are forwarded to the server's _dispatch method for handling. + + Note: this method is the same as in SimpleXMLRPCRequestHandler, + just hacked to handle cookies + """ # Check that the path is legal if not self.is_rpc_path_valid(): @@ -98,10 +105,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # SimpleXMLRPCDispatcher. To maintain backwards compatibility, # check to see if a subclass implements _dispatch and dispatch # using that method if present. - response = self.server._marshaled_dispatch( + response = self.server._marshaled_dispatch( # pylint: disable=protected-access data, getattr(self, '_dispatch', None) ) - except: # This should only happen if the module is buggy + except BaseException: # This should only happen if the module is buggy # internal error, report as HTTP server error self.send_response(500) self.end_headers() @@ -125,22 +132,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): self.connection.shutdown(1) def APIAuthenticateClient(self): + """Predicate to check for valid API credentials in the request header""" + if 'Authorization' in self.headers: # handle Basic authentication - enctype, encstr = self.headers.get('Authorization').split() + _, encstr = self.headers.get('Authorization').split() emailid, password = encstr.decode('base64').split(':') return ( - emailid == - BMConfigParser().get('bitmessagesettings', 'apiusername') - and password == - BMConfigParser().get('bitmessagesettings', 'apipassword') + emailid == BMConfigParser().get('bitmessagesettings', 'apiusername') and + password == BMConfigParser().get('bitmessagesettings', 'apipassword') ) else: logger.warning( 'Authentication failed because header lacks' ' Authentication field') time.sleep(2) - return False return False @@ -155,6 +161,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 22, "Decode error - %s. Had trouble while decoding string: %r" % (e, text) ) + return None def _verifyAddress(self, address): status, addressVersionNumber, streamNumber, ripe = \ @@ -170,15 +177,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if status == 'invalidcharacters': raise APIError(9, 'Invalid characters in address: ' + address) if status == 'versiontoohigh': - raise APIError( - 10, - 'Address version number too high (or zero) in address: ' - + address - ) + raise APIError(10, 'Address version number too high (or zero) in address: ' + address) if status == 'varintmalformed': raise APIError(26, 'Malformed varint in address: ' + address) - raise APIError( - 7, 'Could not decode address: %s : %s' % (address, status)) + raise APIError(7, 'Could not decode address: %s : %s' % (address, status)) if addressVersionNumber < 2 or addressVersionNumber > 4: raise APIError( 11, 'The address version number currently must be 2, 3 or 4.' @@ -195,9 +197,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # Request Handlers def HandleListAddresses(self, method): + """Handle a request to list addresses""" + data = '{"addresses":[' for addressInKeysFile in BMConfigParser().addresses(): - status, addressVersionNumber, streamNumber, hash01 = decodeAddress( + status, addressVersionNumber, streamNumber, hash01 = decodeAddress( # pylint: disable=unused-variable addressInKeysFile) if len(data) > 20: data += ',' @@ -215,11 +219,13 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'enabled': BMConfigParser().getboolean(addressInKeysFile, 'enabled'), 'chan': chan - }, indent=4, separators=(',', ': ')) + }, indent=4, separators=(',', ': ')) data += ']}' return data def HandleListAddressBookEntries(self, params): + """Handle a request to list address book entries""" + if len(params) == 1: label, = params label = self._decode(label, "base64") @@ -243,6 +249,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleAddAddressBookEntry(self, params): + """Handle a request to add an address book entry""" + if len(params) != 2: raise APIError(0, "I need label and address") address, label = params @@ -262,6 +270,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return "Added address %s to address book" % address def HandleDeleteAddressBookEntry(self, params): + """Handle a request to delete an address book entry""" + if len(params) != 1: raise APIError(0, "I need an address") address, = params @@ -274,8 +284,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return "Deleted address book entry for %s if it existed" % address def HandleCreateRandomAddress(self, params): - if len(params) == 0: + """Handle a request to create a random address""" + + if not params: raise APIError(0, 'I need parameters!') + elif len(params) == 1: label, = params eighteenByteRipe = False @@ -292,25 +305,22 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif len(params) == 3: label, eighteenByteRipe, totalDifficulty = params nonceTrialsPerByte = int( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte - * totalDifficulty) + defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') elif len(params) == 4: label, eighteenByteRipe, totalDifficulty, \ smallMessageDifficulty = params nonceTrialsPerByte = int( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte - * totalDifficulty) + defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) payloadLengthExtraBytes = int( - defaults.networkDefaultPayloadLengthExtraBytes - * smallMessageDifficulty) + defaults.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) else: raise APIError(0, 'Too many parameters!') label = self._decode(label, "base64") try: unicode(label, 'utf-8') - except: + except BaseException: raise APIError(17, 'Label is not valid UTF-8 data.') queues.apiAddressGeneratorReturnQueue.queue.clear() streamNumberForAddress = 1 @@ -321,8 +331,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return queues.apiAddressGeneratorReturnQueue.get() def HandleCreateDeterministicAddresses(self, params): - if len(params) == 0: + """Handle a request to create a deterministic address""" + + if not params: raise APIError(0, 'I need parameters!') + elif len(params) == 1: passphrase, = params numberOfAddresses = 1 @@ -333,6 +346,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'bitmessagesettings', 'defaultnoncetrialsperbyte') payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 2: passphrase, numberOfAddresses = params addressVersionNumber = 0 @@ -342,6 +356,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'bitmessagesettings', 'defaultnoncetrialsperbyte') payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 3: passphrase, numberOfAddresses, addressVersionNumber = params streamNumber = 0 @@ -350,6 +365,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'bitmessagesettings', 'defaultnoncetrialsperbyte') payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 4: passphrase, numberOfAddresses, addressVersionNumber, \ streamNumber = params @@ -358,6 +374,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'bitmessagesettings', 'defaultnoncetrialsperbyte') payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 5: passphrase, numberOfAddresses, addressVersionNumber, \ streamNumber, eighteenByteRipe = params @@ -365,27 +382,26 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'bitmessagesettings', 'defaultnoncetrialsperbyte') payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 6: passphrase, numberOfAddresses, addressVersionNumber, \ streamNumber, eighteenByteRipe, totalDifficulty = params nonceTrialsPerByte = int( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte - * totalDifficulty) + defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) payloadLengthExtraBytes = BMConfigParser().get( 'bitmessagesettings', 'defaultpayloadlengthextrabytes') + elif len(params) == 7: passphrase, numberOfAddresses, addressVersionNumber, \ streamNumber, eighteenByteRipe, totalDifficulty, \ smallMessageDifficulty = params nonceTrialsPerByte = int( - defaults.networkDefaultProofOfWorkNonceTrialsPerByte - * totalDifficulty) + defaults.networkDefaultProofOfWorkNonceTrialsPerByte * totalDifficulty) payloadLengthExtraBytes = int( - defaults.networkDefaultPayloadLengthExtraBytes - * smallMessageDifficulty) + defaults.networkDefaultPayloadLengthExtraBytes * smallMessageDifficulty) else: raise APIError(0, 'Too many parameters!') - if len(passphrase) == 0: + if not passphrase: raise APIError(1, 'The specified passphrase is blank.') if not isinstance(eighteenByteRipe, bool): raise APIError( @@ -436,12 +452,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleGetDeterministicAddress(self, params): + """Handle a request to get a deterministic address""" + if len(params) != 3: raise APIError(0, 'I need exactly 3 parameters.') passphrase, addressVersionNumber, streamNumber = params numberOfAddresses = 1 eighteenByteRipe = False - if len(passphrase) == 0: + if not passphrase: raise APIError(1, 'The specified passphrase is blank.') passphrase = self._decode(passphrase, "base64") if addressVersionNumber != 3 and addressVersionNumber != 4: @@ -463,19 +481,23 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return queues.apiAddressGeneratorReturnQueue.get() def HandleCreateChan(self, params): - if len(params) == 0: + """Handle a request to create a chan""" + + if not params: raise APIError(0, 'I need parameters.') + elif len(params) == 1: passphrase, = params passphrase = self._decode(passphrase, "base64") - if len(passphrase) == 0: + + if not passphrase: raise APIError(1, 'The specified passphrase is blank.') # It would be nice to make the label the passphrase but it is # possible that the passphrase contains non-utf-8 characters. try: unicode(passphrase, 'utf-8') label = str_chan + ' ' + passphrase - except: + except BaseException: label = str_chan + ' ' + repr(passphrase) addressVersionNumber = 4 @@ -488,29 +510,31 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): passphrase, True )) queueReturn = queues.apiAddressGeneratorReturnQueue.get() - if len(queueReturn) == 0: + if not queueReturn: raise APIError(24, 'Chan address is already present.') address = queueReturn[0] return address def HandleJoinChan(self, params): + """Handle a request to join a chan""" + if len(params) < 2: raise APIError(0, 'I need two parameters.') elif len(params) == 2: passphrase, suppliedAddress = params passphrase = self._decode(passphrase, "base64") - if len(passphrase) == 0: + if not passphrase: raise APIError(1, 'The specified passphrase is blank.') # It would be nice to make the label the passphrase but it is # possible that the passphrase contains non-utf-8 characters. try: unicode(passphrase, 'utf-8') label = str_chan + ' ' + passphrase - except: + except BaseException: label = str_chan + ' ' + repr(passphrase) - status, addressVersionNumber, streamNumber, toRipe = \ - self._verifyAddress(suppliedAddress) + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress( # pylint: disable=unused-variable + suppliedAddress) suppliedAddress = addBMIfNotPresent(suppliedAddress) queues.apiAddressGeneratorReturnQueue.queue.clear() queues.addressGeneratorQueue.put(( @@ -522,20 +546,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if addressGeneratorReturnValue[0] == \ 'chan name does not match address': raise APIError(18, 'Chan name does not match address.') - if len(addressGeneratorReturnValue) == 0: + if not addressGeneratorReturnValue: raise APIError(24, 'Chan address is already present.') - # TODO: this variable is not used to anything - # in case we ever want it for anything. - # createdAddress = addressGeneratorReturnValue[0] return "success" def HandleLeaveChan(self, params): - if len(params) == 0: + """Handle a request to leave a chan""" + + if not params: raise APIError(0, 'I need parameters.') elif len(params) == 1: address, = params - status, addressVersionNumber, streamNumber, toRipe = \ - self._verifyAddress(address) + # pylint: disable=unused-variable + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) address = addBMIfNotPresent(address) if not BMConfigParser().has_section(address): raise APIError( @@ -550,12 +573,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return 'success' def HandleDeleteAddress(self, params): - if len(params) == 0: + """Handle a request to delete an address""" + + if not params: raise APIError(0, 'I need parameters.') elif len(params) == 1: address, = params - status, addressVersionNumber, streamNumber, toRipe = \ - self._verifyAddress(address) + # pylint: disable=unused-variable + status, addressVersionNumber, streamNumber, toRipe = self._verifyAddress(address) address = addBMIfNotPresent(address) if not BMConfigParser().has_section(address): raise APIError( @@ -568,7 +593,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.reloadMyAddressHashes() return 'success' - def HandleGetAllInboxMessages(self, params): + def HandleGetAllInboxMessages(self, params): # pylint: disable=unused-argument + """Handle a request to get all inbox messages""" + queryreturn = sqlQuery( "SELECT msgid, toaddress, fromaddress, subject, received, message," " encodingtype, read FROM inbox where folder='inbox'" @@ -594,7 +621,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data - def HandleGetAllInboxMessageIds(self, params): + def HandleGetAllInboxMessageIds(self, params): # pylint: disable=unused-argument + """Handle a request to get all inbox message IDs""" + queryreturn = sqlQuery( "SELECT msgid FROM inbox where folder='inbox' ORDER BY received") data = '{"inboxMessageIds":[' @@ -608,7 +637,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleGetInboxMessageById(self, params): - if len(params) == 0: + """Handle a request to get an inbox messsage by ID""" + + if not params: raise APIError(0, 'I need parameters!') elif len(params) == 1: msgid = self._decode(params[0], "hex") @@ -649,7 +680,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data - def HandleGetAllSentMessages(self, params): + def HandleGetAllSentMessages(self, params): # pylint: disable=unused-argument + """Handle a request to get all sent messages""" + queryreturn = sqlQuery( "SELECT msgid, toaddress, fromaddress, subject, lastactiontime," " message, encodingtype, status, ackdata FROM sent" @@ -676,7 +709,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data - def HandleGetAllSentMessageIds(self, params): + def HandleGetAllSentMessageIds(self, params): # pylint: disable=unused-argument + """Handle a request to get all sent message IDs""" + queryreturn = sqlQuery( "SELECT msgid FROM sent where folder='sent'" " ORDER BY lastactiontime" @@ -692,7 +727,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleInboxMessagesByReceiver(self, params): - if len(params) == 0: + """Handle a request to get inbox messages by receiver""" + + if not params: raise APIError(0, 'I need parameters!') toAddress = params[0] queryreturn = sqlQuery( @@ -719,7 +756,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleGetSentMessageById(self, params): - if len(params) == 0: + """Handle a request to get a sent message by ID""" + + if not params: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") queryreturn = sqlQuery( @@ -747,7 +786,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleGetSentMessagesByAddress(self, params): - if len(params) == 0: + """Handle a request to get sent messages by address""" + + if not params: raise APIError(0, 'I need parameters!') fromAddress = params[0] queryreturn = sqlQuery( @@ -759,7 +800,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data = '{"sentMessages":[' for row in queryreturn: msgid, toAddress, fromAddress, subject, lastactiontime, message, \ - encodingtype, status, ackdata = row + encodingtype, status, ackdata = row # pylint: disable=unused-variable subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) if len(data) > 25: @@ -778,7 +819,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleGetSentMessagesByAckData(self, params): - if len(params) == 0: + """Handle a request to get sent messages by ack data""" + + if not params: raise APIError(0, 'I need parameters!') ackData = self._decode(params[0], "hex") queryreturn = sqlQuery( @@ -806,7 +849,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return data def HandleTrashMessage(self, params): - if len(params) == 0: + """Handle a request to trash a message by ID""" + + if not params: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") @@ -817,32 +862,42 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return 'Trashed message (assuming message existed).' def HandleTrashInboxMessage(self, params): - if len(params) == 0: + """Handle a request to trash an inbox message by ID""" + + if not params: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") helper_inbox.trash(msgid) return 'Trashed inbox message (assuming message existed).' def HandleTrashSentMessage(self, params): - if len(params) == 0: + """Handle a request to trash a sent message by ID""" + + if not params: raise APIError(0, 'I need parameters!') msgid = self._decode(params[0], "hex") sqlExecute('''UPDATE sent SET folder='trash' WHERE msgid=?''', msgid) return 'Trashed sent message (assuming message existed).' def HandleSendMessage(self, params): - if len(params) == 0: + """Handle a request to send a message""" + + if not params: raise APIError(0, 'I need parameters!') + elif len(params) == 4: toAddress, fromAddress, subject, message = params encodingType = 2 TTL = 4 * 24 * 60 * 60 + elif len(params) == 5: toAddress, fromAddress, subject, message, encodingType = params TTL = 4 * 24 * 60 * 60 + elif len(params) == 6: toAddress, fromAddress, subject, message, encodingType, TTL = \ params + if encodingType not in [2, 3]: raise APIError(6, 'The encoding type must be 2 or 3.') subject = self._decode(subject, "base64") @@ -855,13 +910,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): TTL = 28 * 24 * 60 * 60 toAddress = addBMIfNotPresent(toAddress) fromAddress = addBMIfNotPresent(fromAddress) + # pylint: disable=unused-variable status, addressVersionNumber, streamNumber, toRipe = \ self._verifyAddress(toAddress) self._verifyAddress(fromAddress) try: fromAddressEnabled = BMConfigParser().getboolean( fromAddress, 'enabled') - except: + except BaseException: raise APIError( 13, 'Could not find your fromAddress in the keys.dat file.') if not fromAddressEnabled: @@ -894,7 +950,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if queryreturn != []: for row in queryreturn: toLabel, = row - # apiSignalQueue.put(('displayNewSentMessage',(toAddress,toLabel,fromAddress,subject,message,ackdata))) queues.UISignalQueue.put(('displayNewSentMessage', ( toAddress, toLabel, fromAddress, subject, message, ackdata))) @@ -903,19 +958,25 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return hexlify(ackdata) def HandleSendBroadcast(self, params): - if len(params) == 0: + """Handle a request to send a broadcast message""" + + if not params: raise APIError(0, 'I need parameters!') + if len(params) == 3: fromAddress, subject, message = params encodingType = 2 TTL = 4 * 24 * 60 * 60 + elif len(params) == 4: fromAddress, subject, message, encodingType = params TTL = 4 * 24 * 60 * 60 elif len(params) == 5: fromAddress, subject, message, encodingType, TTL = params + if encodingType not in [2, 3]: raise APIError(6, 'The encoding type must be 2 or 3.') + subject = self._decode(subject, "base64") message = self._decode(message, "base64") if len(subject + message) > (2 ** 18 - 500): @@ -928,7 +989,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): self._verifyAddress(fromAddress) try: BMConfigParser().getboolean(fromAddress, 'enabled') - except: + except BaseException: raise APIError( 13, 'could not find your fromAddress in the keys.dat file.') streamNumber = decodeAddress(fromAddress)[2] @@ -961,6 +1022,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return hexlify(ackdata) def HandleGetStatus(self, params): + """Handle a request to get the status of a sent message""" + if len(params) != 1: raise APIError(0, 'I need one parameter!') ackdata, = params @@ -977,7 +1040,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return status def HandleAddSubscription(self, params): - if len(params) == 0: + """Handle a request to add a subscription""" + + if not params: raise APIError(0, 'I need parameters!') if len(params) == 1: address, = params @@ -987,7 +1052,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): label = self._decode(label, "base64") try: unicode(label, 'utf-8') - except: + except BaseException: raise APIError(17, 'Label is not valid UTF-8 data.') if len(params) > 2: raise APIError(0, 'I need either 1 or 2 parameters!') @@ -1007,6 +1072,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return 'Added subscription.' def HandleDeleteSubscription(self, params): + """Handle a request to delete a subscription""" + if len(params) != 1: raise APIError(0, 'I need 1 parameter!') address, = params @@ -1017,7 +1084,10 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): queues.UISignalQueue.put(('rerenderSubscriptions', '')) return 'Deleted subscription if it existed.' - def ListSubscriptions(self, params): + def ListSubscriptions(self, params): # pylint: disable=unused-argument + """Handle a request to list susbcriptions""" + + # pylint: disable=unused-variable queryreturn = sqlQuery( "SELECT label, address, enabled FROM subscriptions") data = {'subscriptions': []} @@ -1032,6 +1102,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return json.dumps(data, indent=4, separators=(',', ': ')) def HandleDisseminatePreEncryptedMsg(self, params): + """Handle a request to disseminate an encrypted message""" + # The device issuing this command to PyBitmessage supplies a msg # object that has already been encrypted but which still needs the POW # to be done. PyBitmessage accepts this msg object and sends it out @@ -1044,18 +1116,30 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): encryptedPayload = self._decode(encryptedPayload, "hex") # Let us do the POW and attach it to the front target = 2**64 / ( - (len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8) - * requiredAverageProofOfWorkNonceTrialsPerByte) + ( + len(encryptedPayload) + requiredPayloadLengthExtraBytes + 8 + ) * requiredAverageProofOfWorkNonceTrialsPerByte + ) with shared.printLock: - print '(For msg message via API) Doing proof of work. Total required difficulty:', float(requiredAverageProofOfWorkNonceTrialsPerByte) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte, 'Required small message difficulty:', float(requiredPayloadLengthExtraBytes) / defaults.networkDefaultPayloadLengthExtraBytes + print( + '(For msg message via API) Doing proof of work. Total required difficulty:', + float( + requiredAverageProofOfWorkNonceTrialsPerByte + ) / defaults.networkDefaultProofOfWorkNonceTrialsPerByte, + 'Required small message difficulty:', + float(requiredPayloadLengthExtraBytes) / defaults.networkDefaultPayloadLengthExtraBytes, + ) powStartTime = time.time() initialHash = hashlib.sha512(encryptedPayload).digest() trialValue, nonce = proofofwork.run(target, initialHash) with shared.printLock: print '(For msg message via API) Found proof of work', trialValue, 'Nonce:', nonce try: - print 'POW took', int(time.time() - powStartTime), 'seconds.', nonce / (time.time() - powStartTime), 'nonce trials per second.' - except: + print( + 'POW took', int(time.time() - powStartTime), 'seconds.', + nonce / (time.time() - powStartTime), 'nonce trials per second.', + ) + except BaseException: pass encryptedPayload = pack('>Q', nonce) + encryptedPayload toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] @@ -1071,14 +1155,18 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): queues.invQueue.put((toStreamNumber, inventoryHash)) def HandleTrashSentMessageByAckDAta(self, params): + """Handle a request to trash a sent message by ackdata""" + # This API method should only be used when msgid is not available - if len(params) == 0: + if not params: raise APIError(0, 'I need parameters!') ackdata = self._decode(params[0], "hex") sqlExecute("UPDATE sent SET folder='trash' WHERE ackdata=?", ackdata) return 'Trashed sent message (assuming message existed).' - def HandleDissimatePubKey(self, params): + def HandleDissimatePubKey(self, params): # pylint: disable=unused-argument + """Handle a request to disseminate a public key""" + # The device issuing this command to PyBitmessage supplies a pubkey # object to be disseminated to the rest of the Bitmessage network. # PyBitmessage accepts this pubkey object and sends it out to the rest @@ -1090,9 +1178,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): payload = self._decode(payload, "hex") # Let us do the POW - target = 2 ** 64 / ( - (len(payload) + defaults.networkDefaultPayloadLengthExtraBytes - + 8) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte) + target = 2 ** 64 / (( + len(payload) + defaults.networkDefaultPayloadLengthExtraBytes + 8 + ) * defaults.networkDefaultProofOfWorkNonceTrialsPerByte) print '(For pubkey message via API) Doing proof of work...' initialHash = hashlib.sha512(payload).digest() trialValue, nonce = proofofwork.run(target, initialHash) @@ -1100,18 +1188,19 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): payload = pack('>Q', nonce) + payload pubkeyReadPosition = 8 # bypass the nonce - if payload[pubkeyReadPosition:pubkeyReadPosition+4] == \ + if payload[pubkeyReadPosition:pubkeyReadPosition + 4] == \ '\x00\x00\x00\x00': # if this pubkey uses 8 byte time pubkeyReadPosition += 8 else: pubkeyReadPosition += 4 + # pylint: disable=unused-variable addressVersion, addressVersionLength = decodeVarint( - payload[pubkeyReadPosition:pubkeyReadPosition+10]) + payload[pubkeyReadPosition:pubkeyReadPosition + 10]) pubkeyReadPosition += addressVersionLength pubkeyStreamNumber = decodeVarint( - payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] + payload[pubkeyReadPosition:pubkeyReadPosition + 10])[0] inventoryHash = calculateInventoryHash(payload) - objectType = 1 # TODO: support v4 pubkeys + objectType = 1 # .. todo::: support v4 pubkeys TTL = 28 * 24 * 60 * 60 Inventory()[inventoryHash] = ( objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL, '' @@ -1121,6 +1210,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): queues.invQueue.put((pubkeyStreamNumber, inventoryHash)) def HandleGetMessageDataByDestinationHash(self, params): + """Handle a request to get message data by destination hash""" + # Method will eventually be used by a particular Android app to # select relevant messages. Do not yet add this to the api # doc. @@ -1145,8 +1236,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): readPosition = 16 # Nonce length + time length # Stream Number length readPosition += decodeVarint( - payload[readPosition:readPosition+10])[1] - t = (payload[readPosition:readPosition+32], hash01) + payload[readPosition:readPosition + 10])[1] + t = (payload[readPosition:readPosition + 32], hash01) sql.execute("UPDATE inventory SET tag=? WHERE hash=?", *t) queryreturn = sqlQuery( @@ -1161,10 +1252,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += ']}' return data - def HandleClientStatus(self, params): - if len(network.stats.connectedHostsList()) == 0: + def HandleClientStatus(self, params): # pylint: disable=unused-argument + """Handle a request to get the status of the client""" + + if network.stats.connectedHostsList(): networkStatus = 'notConnected' - elif len(network.stats.connectedHostsList()) > 0 \ + elif not network.stats.connectedHostsList() \ and not shared.clientHasReceivedIncomingConnections: networkStatus = 'connectedButHaveNotReceivedIncomingConnections' else: @@ -1177,9 +1270,11 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'networkStatus': networkStatus, 'softwareName': 'PyBitmessage', 'softwareVersion': softwareVersion - }, indent=4, separators=(',', ': ')) + }, indent=4, separators=(',', ': ')) def HandleDecodeAddress(self, params): + """Handle a request to decode an address""" + # Return a meaningful decoding of an address. if len(params) != 1: raise APIError(0, 'I need 1 parameter!') @@ -1190,29 +1285,41 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'addressVersion': addressVersion, 'streamNumber': streamNumber, 'ripe': base64.b64encode(ripe) - }, indent=4, separators=(',', ': ')) + }, indent=4, separators=(',', ': ')) def HandleHelloWorld(self, params): + """Test two string params""" + a, b = params return a + '-' + b def HandleAdd(self, params): + """Test two numeric params""" + a, b = params return a + b def HandleStatusBar(self, params): + """Handle a request to update the status bar""" + message, = params queues.UISignalQueue.put(('updateStatusBar', message)) def HandleDeleteAndVacuum(self, params): + """Handle a request to run the deleteandvacuum stored procedure""" + if not params: sqlStoredProcedure('deleteandvacuume') return 'done' + return None def HandleShutdown(self, params): + """Handle a request to huutdown the client""" + if not params: shutdown.doCleanShutdown() return 'done' + return None handlers = {} handlers['helloWorld'] = HandleHelloWorld @@ -1279,6 +1386,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): return self.handlers[method](self, params) def _dispatch(self, method, params): + # pylint: disable=attribute-defined-outside-init self.cookies = [] validuser = self.APIAuthenticateClient() diff --git a/src/bitmessagecli.py b/src/bitmessagecli.py old mode 100644 new mode 100755 index 02fed7e9..8e9d88e8 --- a/src/bitmessagecli.py +++ b/src/bitmessagecli.py @@ -12,20 +12,21 @@ This is an example of a daemon client for PyBitmessage 0.6.2, by .dok (Version 0 TODO: fix the following (currently ignored) violations: """ +from __future__ import absolute_import -import xmlrpclib import datetime import imghdr -import ntpath import json -import socket -import time -import sys +import ntpath import os +import socket +import sys +import time +import xmlrpclib +import pathmagic from bmconfigparser import BMConfigParser - api = '' keysName = 'keys.dat' keysPath = 'keys.dat' diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index fb3cccb9..a4b316fb 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -1,75 +1,69 @@ #!/usr/bin/python2.7 -# Copyright (c) 2012-2016 Jonathan Warren -# Copyright (c) 2012-2018 The Bitmessage developers -# Distributed under the MIT/X11 software license. See the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# pylint: disable=no-self-use,too-many-branches,too-many-statements,too-many-locals +""" +bitmessagemain.py +================= -# Right now, PyBitmessage only support connecting to stream 1. It doesn't -# yet contain logic to expand into further streams. +Copyright (c) 2012-2016 Jonathan Warren +Copyright (c) 2012-2018 The Bitmessage developers +Distributed under the MIT/X11 software license. See the accompanying +file COPYING or http://www.opensource.org/licenses/mit-license.php. -# The software version variable is now held in shared.py +Right now, PyBitmessage only support connecting to stream 1. It doesn't +yet contain logic to expand into further streams. -import os -import sys +The software version variable is now held in shared.py -app_dir = os.path.dirname(os.path.abspath(__file__)) -os.chdir(app_dir) -sys.path.insert(0, app_dir) +""" +from __future__ import absolute_import -import depends -depends.check_dependencies() - -# Used to capture a Ctrl-C keypress so that Bitmessage can shutdown gracefully. -import signal -# The next 3 are used for the API -from singleinstance import singleinstance -import errno -import socket import ctypes +import errno +import getopt +import os +import signal +import socket +import sys +import threading +from random import randint from struct import pack from subprocess import call from time import sleep -from random import randint -import getopt - -from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer -from helper_startup import ( - isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections -) import defaults -import shared -import knownnodes -import state -import shutdown -import threading - -# Classes -from class_sqlThread import sqlThread -from class_singleCleaner import singleCleaner -from class_objectProcessor import objectProcessor -from class_singleWorker import singleWorker -from class_addressGenerator import addressGenerator -from bmconfigparser import BMConfigParser - -from inventory import Inventory - -from network.connectionpool import BMConnectionPool -from network.dandelion import Dandelion -from network.networkthread import BMNetworkThread -from network.receivequeuethread import ReceiveQueueThread -from network.announcethread import AnnounceThread -from network.invthread import InvThread -from network.addrthread import AddrThread -from network.downloadthread import DownloadThread - -# Helper Functions -import helper_bootstrap +import depends import helper_generic import helper_threading +import knownnodes +import shared +import shutdown +import state +from api import MySimpleXMLRPCRequestHandler, StoppableXMLRPCServer +from bmconfigparser import BMConfigParser +from class_addressGenerator import addressGenerator +from class_objectProcessor import objectProcessor +from class_singleCleaner import singleCleaner +from class_singleWorker import singleWorker +from class_sqlThread import sqlThread +from helper_startup import isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections +from inventory import Inventory +from network.addrthread import AddrThread +from network.announcethread import AnnounceThread +from network.connectionpool import BMConnectionPool +from network.dandelion import Dandelion +from network.downloadthread import DownloadThread +from network.invthread import InvThread +from network.networkthread import BMNetworkThread +from network.receivequeuethread import ReceiveQueueThread +from singleinstance import singleinstance + +depends.check_dependencies() + def connectToStream(streamNumber): + """Connect to a stream""" + state.streamsInWhichIAmParticipating.append(streamNumber) selfInitiatedConnections[streamNumber] = {} @@ -84,16 +78,16 @@ def connectToStream(streamNumber): if BMConfigParser().get( 'bitmessagesettings', 'socksproxytype') != 'none': state.maximumNumberOfHalfOpenConnections = 4 - except: + except BaseException: pass 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) @@ -111,6 +105,8 @@ def _fixSocket(): addressToString = ctypes.windll.ws2_32.WSAAddressToStringA def inet_ntop(family, host): + """Convert IPv4 and IPv6 addresses from binary to text form""" + if family == socket.AF_INET: if len(host) != 4: raise ValueError("invalid IPv4 host") @@ -132,6 +128,8 @@ def _fixSocket(): stringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA def inet_pton(family, host): + """Convert IPv4 and IPv6 addresses from text to binary form""" + buf = "\0" * 28 lengthBuf = pack("I", len(buf)) if stringToAddress(str(host), @@ -155,13 +153,15 @@ def _fixSocket(): socket.IPV6_V6ONLY = 27 -# This thread, of which there is only one, runs the API. class singleAPI(threading.Thread, helper_threading.StoppableThread): + """This thread, of which there is only one, runs the API.""" + def __init__(self): threading.Thread.__init__(self, name="singleAPI") self.initStop() def stopThread(self): + """Stop the API thread""" super(singleAPI, self).stopThread() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: @@ -171,13 +171,14 @@ class singleAPI(threading.Thread, helper_threading.StoppableThread): )) s.shutdown(socket.SHUT_RDWR) s.close() - except: + except BaseException: pass def run(self): + """Run the API thread""" port = BMConfigParser().getint('bitmessagesettings', 'apiport') try: - from errno import WSAEADDRINUSE + from errno import WSAEADDRINUSE # pylint: disable=unused-variable except (ImportError, AttributeError): errno.WSAEADDRINUSE = errno.EADDRINUSE for attempt in range(50): @@ -212,15 +213,18 @@ if shared.useVeryEasyProofOfWorkForTesting: defaults.networkDefaultPayloadLengthExtraBytes / 100) -class Main: +class Main(object): + """The main app""" + def start(self): + """Start the main app""" _fixSocket() daemon = BMConfigParser().safeGetBoolean( 'bitmessagesettings', 'daemon') try: - opts, args = getopt.getopt( + opts, _ = getopt.getopt( sys.argv[1:], "hcdt", ["help", "curses", "daemon", "test"]) @@ -228,7 +232,7 @@ class Main: self.usage() sys.exit(2) - for opt, arg in opts: + for opt, _ in opts: if opt in ("-h", "--help"): self.usage() sys.exit() @@ -259,7 +263,7 @@ class Main: if daemon and not state.testmode: with shared.printLock: - print('Running as a daemon. Send TERM signal to end.') + print 'Running as a daemon. Send TERM signal to end.' self.daemonize() self.setSignalHandler() @@ -343,11 +347,11 @@ class Main: try: apiNotifyPath = BMConfigParser().get( 'bitmessagesettings', 'apinotifypath') - except: + except BaseException: apiNotifyPath = '' if apiNotifyPath != '': with shared.printLock: - print('Trying to call', apiNotifyPath) + print 'Trying to call', apiNotifyPath call([apiNotifyPath, "startingUp"]) singleAPIThread = singleAPI() @@ -393,7 +397,7 @@ class Main: if state.curses: if not depends.check_curses(): sys.exit() - print('Running with curses') + print 'Running with curses' import bitmessagecurses bitmessagecurses.runwrapper() elif state.kivy: @@ -415,6 +419,7 @@ class Main: sleep(1) def daemonize(self): + """Daemonise""" grandfatherPid = os.getpid() parentPid = None try: @@ -424,7 +429,7 @@ class Main: # wait until grandchild ready while True: sleep(1) - os._exit(0) + sys.exit(0) except AttributeError: # fork not implemented pass @@ -445,7 +450,7 @@ class Main: # wait until child ready while True: sleep(1) - os._exit(0) + sys.exit(0) except AttributeError: # fork not implemented pass @@ -467,11 +472,13 @@ class Main: os.kill(grandfatherPid, signal.SIGTERM) def setSignalHandler(self): + """Register signal handlers""" signal.signal(signal.SIGINT, helper_generic.signal_handler) signal.signal(signal.SIGTERM, helper_generic.signal_handler) - # signal.signal(signal.SIGINT, signal.SIG_DFL) def usage(self): + """Print usage message""" + print 'Usage: ' + sys.argv[0] + ' [OPTIONS]' print ''' Options: @@ -484,12 +491,18 @@ All parameters are optional. ''' def stop(self): + """Stop the daemon""" with shared.printLock: - print('Stopping Bitmessage Deamon.') + print 'Stopping Bitmessage Daemon.' shutdown.doCleanShutdown() - # TODO: nice function but no one is using this def getApiAddress(self): + """ + Return the address and port the API is configured to use + + .. todo:: nice function but no one is using this + """ + if not BMConfigParser().safeGetBoolean( 'bitmessagesettings', 'apienabled'): return None @@ -499,14 +512,10 @@ All parameters are optional. def main(): + """Create and start the main app""" mainprogram = Main() mainprogram.start() if __name__ == "__main__": main() - - -# So far, the creation of and management of the Bitmessage protocol and this -# client is a one-man operation. Bitcoin tips are quite appreciated. -# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 50ea3548..8b1a94fa 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -7,8 +7,6 @@ Account related functions. """ -from __future__ import absolute_import - import inspect import re import sys diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index cb3578c0..13d6ed7b 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -1,20 +1,23 @@ # -*- coding: utf-8 -*- +# pylint: disable=too-many-locals,c-extension-no-member +""" +bitmessageui.py +=============== -# Form implementation generated from reading ui file 'bitmessageui.ui' -# -# Created: Mon Mar 23 22:18:07 2015 -# by: PyQt4 UI code generator 4.10.4 -# -# WARNING! All changes made in this file will be lost! +Originally generated from reading ui file 'bitmessageui.ui'. Since then maintained manually. +""" + +import sys from PyQt4 import QtCore, QtGui + +from bitmessageqt import settingsmixin +from bitmessageqt.blacklist import Blacklist +from bitmessageqt.foldertree import AddressBookCompleter +from bitmessageqt.messagecompose import MessageCompose +from bitmessageqt.messageview import MessageView +from bitmessageqt.networkstatus import NetworkStatus from bmconfigparser import BMConfigParser -from foldertree import AddressBookCompleter -from messageview import MessageView -from messagecompose import MessageCompose -import settingsmixin -from networkstatus import NetworkStatus -from blacklist import Blacklist try: _fromUtf8 = QtCore.QString.fromUtf8 @@ -24,24 +27,38 @@ except AttributeError: try: _encoding = QtGui.QApplication.UnicodeUTF8 - def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None): + + def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None): + # pylint: disable=unused-argument if n is None: return QtGui.QApplication.translate(context, text, disambig, _encoding) - else: - return QtGui.QApplication.translate(context, text, disambig, _encoding, n) + return QtGui.QApplication.translate(context, text, disambig, _encoding, n) + except AttributeError: - def _translate(context, text, disambig, encoding = QtCore.QCoreApplication.CodecForTr, n = None): + + def _translate(context, text, disambig, encoding=QtCore.QCoreApplication.CodecForTr, n=None): + # pylint: disable=unused-argument if n is None: return QtGui.QApplication.translate(context, text, disambig) - else: - return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n) + return QtGui.QApplication.translate(context, text, disambig, QtCore.QCoreApplication.CodecForTr, n) + class Ui_MainWindow(object): + """Encapsulate the main UI""" + # pylint: disable=too-many-instance-attributes,too-many-statements + def setupUi(self, MainWindow): + """Set up the UI""" + # pylint: disable=attribute-defined-outside-init + MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(885, 580) icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/can-icon-24px.png")), + QtGui.QIcon.Normal, + QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) MainWindow.setTabShape(QtGui.QTabWidget.Rounded) self.centralwidget = QtGui.QWidget(MainWindow) @@ -75,7 +92,11 @@ class Ui_MainWindow(object): self.treeWidgetYourIdentities.setObjectName(_fromUtf8("treeWidgetYourIdentities")) self.treeWidgetYourIdentities.resize(200, self.treeWidgetYourIdentities.height()) icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/identities.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + icon1.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/identities.png")), + QtGui.QIcon.Selected, + QtGui.QIcon.Off) self.treeWidgetYourIdentities.headerItem().setIcon(0, icon1) self.verticalSplitter_12.addWidget(self.treeWidgetYourIdentities) self.pushButtonNewAddress = QtGui.QPushButton(self.inbox) @@ -175,7 +196,11 @@ class Ui_MainWindow(object): self.tableWidgetAddressBook.resize(200, self.tableWidgetAddressBook.height()) item = QtGui.QTableWidgetItem() icon3 = QtGui.QIcon() - icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/addressbook.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + icon3.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/addressbook.png")), + QtGui.QIcon.Selected, + QtGui.QIcon.Off) item.setIcon(icon3) self.tableWidgetAddressBook.setHorizontalHeaderItem(0, item) item = QtGui.QTableWidgetItem() @@ -376,7 +401,11 @@ class Ui_MainWindow(object): self.treeWidgetSubscriptions.setObjectName(_fromUtf8("treeWidgetSubscriptions")) self.treeWidgetSubscriptions.resize(200, self.treeWidgetSubscriptions.height()) icon5 = QtGui.QIcon() - icon5.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + icon5.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/subscriptions.png")), + QtGui.QIcon.Selected, + QtGui.QIcon.Off) self.treeWidgetSubscriptions.headerItem().setIcon(0, icon5) self.verticalSplitter_3.addWidget(self.treeWidgetSubscriptions) self.pushButtonAddSubscription = QtGui.QPushButton(self.subscriptions) @@ -455,7 +484,11 @@ class Ui_MainWindow(object): self.horizontalSplitter_4.setCollapsible(1, False) self.gridLayout_3.addWidget(self.horizontalSplitter_4, 0, 0, 1, 1) icon6 = QtGui.QIcon() - icon6.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/subscriptions.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon6.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/subscriptions.png")), + QtGui.QIcon.Normal, + QtGui.QIcon.Off) self.tabWidget.addTab(self.subscriptions, icon6, _fromUtf8("")) self.chans = QtGui.QWidget() self.chans.setObjectName(_fromUtf8("chans")) @@ -475,7 +508,11 @@ class Ui_MainWindow(object): self.treeWidgetChans.setObjectName(_fromUtf8("treeWidgetChans")) self.treeWidgetChans.resize(200, self.treeWidgetChans.height()) icon7 = QtGui.QIcon() - icon7.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Selected, QtGui.QIcon.Off) + icon7.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/can-icon-16px.png")), + QtGui.QIcon.Selected, + QtGui.QIcon.Off) self.treeWidgetChans.headerItem().setIcon(0, icon7) self.verticalSplitter_17.addWidget(self.treeWidgetChans) self.pushButtonAddChan = QtGui.QPushButton(self.chans) @@ -554,7 +591,11 @@ class Ui_MainWindow(object): self.horizontalSplitter_7.setCollapsible(1, False) self.gridLayout_4.addWidget(self.horizontalSplitter_7, 0, 0, 1, 1) icon8 = QtGui.QIcon() - icon8.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-16px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + icon8.addPixmap( + QtGui.QPixmap( + _fromUtf8(":/newPrefix/images/can-icon-16px.png")), + QtGui.QIcon.Normal, + QtGui.QIcon.Off) self.tabWidget.addTab(self.chans, icon8, _fromUtf8("")) self.blackwhitelist = Blacklist() self.tabWidget.addTab(self.blackwhitelist, QtGui.QIcon(":/newPrefix/images/blacklist.png"), "") @@ -652,6 +693,8 @@ class Ui_MainWindow(object): MainWindow.setTabOrder(self.textEditMessage, self.pushButtonAddSubscription) def updateNetworkSwitchMenuLabel(self, dontconnect=None): + """Restore last online/offline setting""" + if dontconnect is None: dontconnect = BMConfigParser().safeGetBoolean( 'bitmessagesettings', 'dontconnect') @@ -662,6 +705,8 @@ class Ui_MainWindow(object): ) def retranslateUi(self, MainWindow): + """Re-translate the UI""" + MainWindow.setWindowTitle(_translate("MainWindow", "Bitmessage", None)) self.treeWidgetYourIdentities.headerItem().setText(0, _translate("MainWindow", "Identities", None)) self.pushButtonNewAddress.setText(_translate("MainWindow", "New Identity", None)) @@ -691,19 +736,33 @@ class Ui_MainWindow(object): self.label_3.setText(_translate("MainWindow", "Subject:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) self.label.setText(_translate("MainWindow", "To:", None)) - #self.textEditMessage.setHtml("") - self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendDirect), _translate("MainWindow", "Send ordinary Message", None)) + # self.textEditMessage.setHtml("") + self.tabWidgetSend.setTabText( + self.tabWidgetSend.indexOf( + self.sendDirect), + _translate( + "MainWindow", "Send ordinary Message", None)) self.label_8.setText(_translate("MainWindow", "From:", None)) self.label_7.setText(_translate("MainWindow", "Subject:", None)) - #self.textEditMessageBroadcast.setHtml("") - self.tabWidgetSend.setTabText(self.tabWidgetSend.indexOf(self.sendBroadcast), _translate("MainWindow", "Send Message to your Subscribers", None)) + # self.textEditMessageBroadcast.setHtml("") + self.tabWidgetSend.setTabText( + self.tabWidgetSend.indexOf( + self.sendBroadcast), + _translate( + "MainWindow", "Send Message to your Subscribers", None)) self.pushButtonTTL.setText(_translate("MainWindow", "TTL:", None)) hours = 48 try: - hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl')/60/60) - except: + hours = int(BMConfigParser().getint('bitmessagesettings', 'ttl') / 60 / 60) + except BaseException: pass - self.labelHumanFriendlyTTLDescription.setText(_translate("MainWindow", "%n hour(s)", None, QtCore.QCoreApplication.CodecForTr, hours)) + self.labelHumanFriendlyTTLDescription.setText( + _translate( + "MainWindow", + "%n hour(s)", + None, + QtCore.QCoreApplication.CodecForTr, + hours)) self.pushButtonClear.setText(_translate("MainWindow", "Clear", None)) self.pushButtonSend.setText(_translate("MainWindow", "Send", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.send), _translate("MainWindow", "Send", None)) @@ -724,7 +783,11 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Subject", None)) item = self.tableWidgetInboxSubscriptions.horizontalHeaderItem(3) item.setText(_translate("MainWindow", "Received", None)) - self.tabWidget.setTabText(self.tabWidget.indexOf(self.subscriptions), _translate("MainWindow", "Subscriptions", None)) + self.tabWidget.setTabText( + self.tabWidget.indexOf( + self.subscriptions), + _translate( + "MainWindow", "Subscriptions", None)) self.treeWidgetChans.headerItem().setText(0, _translate("MainWindow", "Chans", None)) self.pushButtonAddChan.setText(_translate("MainWindow", "Add Chan", None)) self.inboxSearchLineEditChans.setPlaceholderText(_translate("MainWindow", "Search", None)) @@ -744,9 +807,17 @@ class Ui_MainWindow(object): item.setText(_translate("MainWindow", "Received", None)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.chans), _translate("MainWindow", "Chans", None)) self.blackwhitelist.retranslateUi() - self.tabWidget.setTabText(self.tabWidget.indexOf(self.blackwhitelist), _translate("blacklist", "Blacklist", None)) + self.tabWidget.setTabText( + self.tabWidget.indexOf( + self.blackwhitelist), + _translate( + "blacklist", "Blacklist", None)) self.networkstatus.retranslateUi() - self.tabWidget.setTabText(self.tabWidget.indexOf(self.networkstatus), _translate("networkstatus", "Network Status", None)) + self.tabWidget.setTabText( + self.tabWidget.indexOf( + self.networkstatus), + _translate( + "networkstatus", "Network Status", None)) self.menuFile.setTitle(_translate("MainWindow", "File", None)) self.menuSettings.setTitle(_translate("MainWindow", "Settings", None)) self.menuHelp.setTitle(_translate("MainWindow", "Help", None)) @@ -759,19 +830,17 @@ class Ui_MainWindow(object): self.actionSupport.setText(_translate("MainWindow", "Contact support", None)) self.actionAbout.setText(_translate("MainWindow", "About", None)) self.actionSettings.setText(_translate("MainWindow", "Settings", None)) - self.actionRegenerateDeterministicAddresses.setText(_translate("MainWindow", "Regenerate deterministic addresses", None)) + self.actionRegenerateDeterministicAddresses.setText( + _translate("MainWindow", "Regenerate deterministic addresses", None)) self.actionDeleteAllTrashedMessages.setText(_translate("MainWindow", "Delete all trashed messages", None)) self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None)) -import bitmessage_icons_rc if __name__ == "__main__": - import sys - - app = QtGui.QApplication(sys.argv) - MainWindow = settingsmixin.SMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - sys.exit(app.exec_()) + app = QtGui.QApplication(sys.argv) + ThisMainWindow = settingsmixin.SMainWindow() + ui = Ui_MainWindow() + ui.setupUi(ThisMainWindow) + ThisMainWindow.show() + sys.exit(app.exec_()) diff --git a/src/bitmessageqt/newaddresswizard.py b/src/bitmessageqt/newaddresswizard.py deleted file mode 100644 index 2311239c..00000000 --- a/src/bitmessageqt/newaddresswizard.py +++ /dev/null @@ -1,354 +0,0 @@ -#!/usr/bin/env python2.7 -from PyQt4 import QtCore, QtGui - -class NewAddressWizardIntroPage(QtGui.QWizardPage): - def __init__(self): - super(QtGui.QWizardPage, self).__init__() - self.setTitle("Creating a new address") - - label = QtGui.QLabel("This wizard will help you create as many addresses as you like. Indeed, creating and abandoning addresses is encouraged.\n\n" - "What type of address would you like? Would you like to send emails or not?\n" - "You can still change your mind later, and register/unregister with an email service provider.\n\n") - label.setWordWrap(True) - - self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage address") - self.onlyBM = QtGui.QRadioButton("Bitmessage-only address (no email)") - self.emailAsWell.setChecked(True) - self.registerField("emailAsWell", self.emailAsWell) - self.registerField("onlyBM", self.onlyBM) - - layout = QtGui.QVBoxLayout() - layout.addWidget(label) - layout.addWidget(self.emailAsWell) - layout.addWidget(self.onlyBM) - self.setLayout(layout) - - def nextId(self): - if self.emailAsWell.isChecked(): - return 4 - else: - return 1 - - -class NewAddressWizardRngPassphrasePage(QtGui.QWizardPage): - def __init__(self): - super(QtGui.QWizardPage, self).__init__() - self.setTitle("Random or Passphrase") - - label = QtGui.QLabel("
You may generate addresses by using either random numbers or by using a passphrase. " - "If you use a passphrase, the address is called a "deterministic" address. " - "The \'Random Number\' option is selected by default but deterministic addresses have several pros and cons:
" - "Pros: | Cons: |
You can recreate your addresses on any computer from memory. " - "You need-not worry about backing up your keys.dat file as long as you can remember your passphrase. | " - "You must remember (or write down) your passphrase if you expect to be able "
- "to recreate your keys if they are lost. "
-# "You must remember the address version number and the stream number along with your passphrase. "
- "If you choose a weak passphrase and someone on the Internet can brute-force it, they can read your messages and send messages as you."
- "")
- label.setWordWrap(True)
-
- self.randomAddress = QtGui.QRadioButton("Use a random number generator to make an address")
- self.deterministicAddress = QtGui.QRadioButton("Use a passphrase to make an address")
- self.randomAddress.setChecked(True)
-
- layout = QtGui.QVBoxLayout()
- layout.addWidget(label)
- layout.addWidget(self.randomAddress)
- layout.addWidget(self.deterministicAddress)
- self.setLayout(layout)
-
- def nextId(self):
- if self.randomAddress.isChecked():
- return 2
- else:
- return 3
-
-class NewAddressWizardRandomPage(QtGui.QWizardPage):
- def __init__(self, addresses):
- super(QtGui.QWizardPage, self).__init__()
- self.setTitle("Random")
-
- label = QtGui.QLabel("Random address.")
- label.setWordWrap(True)
-
- labelLabel = QtGui.QLabel("Label (not shown to anyone except you):")
- self.labelLineEdit = QtGui.QLineEdit()
-
- self.radioButtonMostAvailable = QtGui.QRadioButton("Use the most available stream\n"
- "(best if this is the first of many addresses you will create)")
- self.radioButtonExisting = QtGui.QRadioButton("Use the same stream as an existing address\n"
- "(saves you some bandwidth and processing power)")
- self.radioButtonMostAvailable.setChecked(True)
- self.comboBoxExisting = QtGui.QComboBox()
- self.comboBoxExisting.setEnabled(False)
- self.comboBoxExisting.setEditable(True)
-
- for address in addresses:
- self.comboBoxExisting.addItem(address)
-
-# self.comboBoxExisting.setObjectName(_fromUtf8("comboBoxExisting"))
- self.checkBoxEighteenByteRipe = QtGui.QCheckBox("Spend several minutes of extra computing time to make the address(es) 1 or 2 characters shorter")
-
- layout = QtGui.QGridLayout()
- layout.addWidget(label, 0, 0)
- layout.addWidget(labelLabel, 1, 0)
- layout.addWidget(self.labelLineEdit, 2, 0)
- layout.addWidget(self.radioButtonMostAvailable, 3, 0)
- layout.addWidget(self.radioButtonExisting, 4, 0)
- layout.addWidget(self.comboBoxExisting, 5, 0)
- layout.addWidget(self.checkBoxEighteenByteRipe, 6, 0)
- self.setLayout(layout)
-
- QtCore.QObject.connect(self.radioButtonExisting, QtCore.SIGNAL("toggled(bool)"), self.comboBoxExisting.setEnabled)
-
- self.registerField("label", self.labelLineEdit)
- self.registerField("radioButtonMostAvailable", self.radioButtonMostAvailable)
- self.registerField("radioButtonExisting", self.radioButtonExisting)
- self.registerField("comboBoxExisting", self.comboBoxExisting)
-
-# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account")
-# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)")
-# self.emailAsWell.setChecked(True)
-
- def nextId(self):
- return 6
-
-
-class NewAddressWizardPassphrasePage(QtGui.QWizardPage):
- def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
- self.setTitle("Passphrase")
-
- label = QtGui.QLabel("Deterministric address.")
- label.setWordWrap(True)
-
- passphraseLabel = QtGui.QLabel("Passphrase")
- self.lineEditPassphrase = QtGui.QLineEdit()
- self.lineEditPassphrase.setEchoMode(QtGui.QLineEdit.Password)
- self.lineEditPassphrase.setInputMethodHints(QtCore.Qt.ImhHiddenText|QtCore.Qt.ImhNoAutoUppercase|QtCore.Qt.ImhNoPredictiveText)
- retypePassphraseLabel = QtGui.QLabel("Retype passphrase")
- self.lineEditPassphraseAgain = QtGui.QLineEdit()
- self.lineEditPassphraseAgain.setEchoMode(QtGui.QLineEdit.Password)
-
- numberLabel = QtGui.QLabel("Number of addresses to make based on your passphrase:")
- self.spinBoxNumberOfAddressesToMake = QtGui.QSpinBox()
- self.spinBoxNumberOfAddressesToMake.setMinimum(1)
- self.spinBoxNumberOfAddressesToMake.setProperty("value", 8)
-# self.spinBoxNumberOfAddressesToMake.setObjectName(_fromUtf8("spinBoxNumberOfAddressesToMake"))
- label2 = QtGui.QLabel("In addition to your passphrase, you must remember these numbers:")
- label3 = QtGui.QLabel("Address version number: 4")
- label4 = QtGui.QLabel("Stream number: 1")
-
- layout = QtGui.QGridLayout()
- layout.addWidget(label, 0, 0, 1, 4)
- layout.addWidget(passphraseLabel, 1, 0, 1, 4)
- layout.addWidget(self.lineEditPassphrase, 2, 0, 1, 4)
- layout.addWidget(retypePassphraseLabel, 3, 0, 1, 4)
- layout.addWidget(self.lineEditPassphraseAgain, 4, 0, 1, 4)
- layout.addWidget(numberLabel, 5, 0, 1, 3)
- layout.addWidget(self.spinBoxNumberOfAddressesToMake, 5, 3)
- layout.setColumnMinimumWidth(3, 1)
- layout.addWidget(label2, 6, 0, 1, 4)
- layout.addWidget(label3, 7, 0, 1, 2)
- layout.addWidget(label4, 7, 2, 1, 2)
- self.setLayout(layout)
-
- def nextId(self):
- return 6
-
-
-class NewAddressWizardEmailProviderPage(QtGui.QWizardPage):
- def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
- self.setTitle("Choose email provider")
-
- label = QtGui.QLabel("Currently only Mailchuck email gateway is available "
- "(@mailchuck.com email address). In the future, maybe other gateways will be available. "
- "Press Next.")
- label.setWordWrap(True)
-
-# self.mailchuck = QtGui.QRadioButton("Mailchuck email gateway (@mailchuck.com)")
-# self.mailchuck.setChecked(True)
-
- layout = QtGui.QVBoxLayout()
- layout.addWidget(label)
-# layout.addWidget(self.mailchuck)
- self.setLayout(layout)
-
- def nextId(self):
- return 5
-
-
-class NewAddressWizardEmailAddressPage(QtGui.QWizardPage):
- def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
- self.setTitle("Email address")
-
- label = QtGui.QLabel("Choosing an email address. Address must end with @mailchuck.com")
- label.setWordWrap(True)
-
- self.specificEmail = QtGui.QRadioButton("Pick your own email address:")
- self.specificEmail.setChecked(True)
- self.emailLineEdit = QtGui.QLineEdit()
- self.randomEmail = QtGui.QRadioButton("Generate a random email address")
-
- QtCore.QObject.connect(self.specificEmail, QtCore.SIGNAL("toggled(bool)"), self.emailLineEdit.setEnabled)
-
- layout = QtGui.QVBoxLayout()
- layout.addWidget(label)
- layout.addWidget(self.specificEmail)
- layout.addWidget(self.emailLineEdit)
- layout.addWidget(self.randomEmail)
- self.setLayout(layout)
-
- def nextId(self):
- return 6
-
-
-class NewAddressWizardWaitPage(QtGui.QWizardPage):
- def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
- self.setTitle("Wait")
-
- self.label = QtGui.QLabel("Wait!")
- self.label.setWordWrap(True)
- self.progressBar = QtGui.QProgressBar()
- self.progressBar.setMinimum(0)
- self.progressBar.setMaximum(100)
- self.progressBar.setValue(0)
-
-# self.emailAsWell = QtGui.QRadioButton("Combined email and bitmessage account")
-# self.onlyBM = QtGui.QRadioButton("Bitmessage-only account (no email)")
-# self.emailAsWell.setChecked(True)
-
- layout = QtGui.QVBoxLayout()
- layout.addWidget(self.label)
- layout.addWidget(self.progressBar)
-# layout.addWidget(self.emailAsWell)
-# layout.addWidget(self.onlyBM)
- self.setLayout(layout)
-
- def update(self, i):
- if i == 101 and self.wizard().currentId() == 6:
- self.wizard().button(QtGui.QWizard.NextButton).click()
- return
- elif i == 101:
- print "haha"
- return
- self.progressBar.setValue(i)
- if i == 50:
- self.emit(QtCore.SIGNAL('completeChanged()'))
-
- def isComplete(self):
-# print "val = " + str(self.progressBar.value())
- if self.progressBar.value() >= 50:
- return True
- else:
- return False
-
- def initializePage(self):
- if self.field("emailAsWell").toBool():
- val = "yes/"
- else:
- val = "no/"
- if self.field("onlyBM").toBool():
- val += "yes"
- else:
- val += "no"
-
- self.label.setText("Wait! " + val)
-# self.wizard().button(QtGui.QWizard.NextButton).setEnabled(False)
- self.progressBar.setValue(0)
- self.thread = NewAddressThread()
- self.connect(self.thread, self.thread.signal, self.update)
- self.thread.start()
-
- def nextId(self):
- return 10
-
-
-class NewAddressWizardConclusionPage(QtGui.QWizardPage):
- def __init__(self):
- super(QtGui.QWizardPage, self).__init__()
- self.setTitle("All done!")
-
- label = QtGui.QLabel("You successfully created a new address.")
- label.setWordWrap(True)
-
- layout = QtGui.QVBoxLayout()
- layout.addWidget(label)
- self.setLayout(layout)
-
-class Ui_NewAddressWizard(QtGui.QWizard):
- def __init__(self, addresses):
- super(QtGui.QWizard, self).__init__()
-
- self.pages = {}
-
- page = NewAddressWizardIntroPage()
- self.setPage(0, page)
- self.setStartId(0)
- page = NewAddressWizardRngPassphrasePage()
- self.setPage(1, page)
- page = NewAddressWizardRandomPage(addresses)
- self.setPage(2, page)
- page = NewAddressWizardPassphrasePage()
- self.setPage(3, page)
- page = NewAddressWizardEmailProviderPage()
- self.setPage(4, page)
- page = NewAddressWizardEmailAddressPage()
- self.setPage(5, page)
- page = NewAddressWizardWaitPage()
- self.setPage(6, page)
- page = NewAddressWizardConclusionPage()
- self.setPage(10, page)
-
- self.setWindowTitle("New address wizard")
- self.adjustSize()
- self.show()
-
-class NewAddressThread(QtCore.QThread):
- def __init__(self):
- QtCore.QThread.__init__(self)
- self.signal = QtCore.SIGNAL("signal")
-
- def __del__(self):
- self.wait()
-
- def createDeterministic(self):
- pass
-
- def createPassphrase(self):
- pass
-
- def broadcastAddress(self):
- pass
-
- def registerMailchuck(self):
- pass
-
- def waitRegistration(self):
- pass
-
- def run(self):
- import time
- for i in range(1, 101):
- time.sleep(0.1) # artificial time delay
- self.emit(self.signal, i)
- self.emit(self.signal, 101)
-# self.terminate()
-
-if __name__ == '__main__':
-
- import sys
-
- app = QtGui.QApplication(sys.argv)
-
- wizard = Ui_NewAddressWizard(["a", "b", "c", "d"])
- if (wizard.exec_()):
- print "Email: " + ("yes" if wizard.field("emailAsWell").toBool() else "no")
- print "BM: " + ("yes" if wizard.field("onlyBM").toBool() else "no")
- else:
- print "Wizard cancelled"
- sys.exit()
diff --git a/src/network/asyncore_pollchoose.py b/src/network/asyncore_pollchoose.py
index e50be61b..bbe89119 100644
--- a/src/network/asyncore_pollchoose.py
+++ b/src/network/asyncore_pollchoose.py
@@ -1,7 +1,9 @@
# -*- Mode: Python -*-
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
# Author: Sam Rushing |