From 2b17991ee4c9b33a69e3b51ff0a4abb0ae501f9e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 29 Jul 2013 22:19:15 +0100 Subject: [PATCH 01/37] -aChanged version to 0.3.5 --- .../{pybitmessage-0.3.4-1.ebuild => pybitmessage-0.3.5-1.ebuild} | 0 puppypackage/pybitmessage-0.3.4.pet.specs | 1 - puppypackage/pybitmessage-0.3.5.pet.specs | 1 + 3 files changed, 1 insertion(+), 1 deletion(-) rename ebuildpackage/{pybitmessage-0.3.4-1.ebuild => pybitmessage-0.3.5-1.ebuild} (100%) delete mode 100644 puppypackage/pybitmessage-0.3.4.pet.specs create mode 100644 puppypackage/pybitmessage-0.3.5.pet.specs diff --git a/ebuildpackage/pybitmessage-0.3.4-1.ebuild b/ebuildpackage/pybitmessage-0.3.5-1.ebuild similarity index 100% rename from ebuildpackage/pybitmessage-0.3.4-1.ebuild rename to ebuildpackage/pybitmessage-0.3.5-1.ebuild diff --git a/puppypackage/pybitmessage-0.3.4.pet.specs b/puppypackage/pybitmessage-0.3.4.pet.specs deleted file mode 100644 index aac180a9..00000000 --- a/puppypackage/pybitmessage-0.3.4.pet.specs +++ /dev/null @@ -1 +0,0 @@ -pybitmessage-0.3.4-1|PyBitmessage|0.3.4|1|Internet;mailnews;|5.8M||pybitmessage-0.3.4-1.pet|+openssl,+python-qt4,+sqlite3,+sqlite3-dev,+python-openssl,+python-sip,+gst123|Send encrypted messages|ubuntu|precise|5| diff --git a/puppypackage/pybitmessage-0.3.5.pet.specs b/puppypackage/pybitmessage-0.3.5.pet.specs new file mode 100644 index 00000000..939294e4 --- /dev/null +++ b/puppypackage/pybitmessage-0.3.5.pet.specs @@ -0,0 +1 @@ +pybitmessage-0.3.5-1|PyBitmessage|0.3.5|1|Internet;mailnews;|7.2M||pybitmessage-0.3.5-1.pet|+openssl,+python-qt4,+sqlite3,+sqlite3-dev,+python-openssl,+python-sip,+gst123|Send encrypted messages|ubuntu|precise|5| From 97df9e04f13c8450e62d97f0fe50d1264f39488b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 29 Jul 2013 22:22:17 +0100 Subject: [PATCH 02/37] Changed version to 0.3.5 --- arch.sh | 4 ++-- archpackage/PKGBUILD | 2 +- debian.sh | 2 +- debian/changelog | 28 ++++++++++++++++++++++++++++ debian/rules | 0 ebuild.sh | 4 ++-- generate.sh | 2 +- puppy.sh | 4 ++-- rpm.sh | 4 ++-- rpmpackage/pybitmessage.spec | 27 ++++++++++++++++++++++++++- slack.sh | 4 ++-- 11 files changed, 67 insertions(+), 14 deletions(-) mode change 100644 => 100755 debian.sh mode change 100644 => 100755 debian/rules diff --git a/arch.sh b/arch.sh index 77332d09..0ccc45b3 100755 --- a/arch.sh +++ b/arch.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.4 -VERSION=0.3.4 +PREV_VERSION=0.3.5 +VERSION=0.3.5 RELEASE=1 ARCH_TYPE=`uname -m` CURRDIR=`pwd` diff --git a/archpackage/PKGBUILD b/archpackage/PKGBUILD index e36c1a11..efbf33e2 100644 --- a/archpackage/PKGBUILD +++ b/archpackage/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Bob Mottram (4096 bits) pkgname=pybitmessage -pkgver=0.3.4 +pkgver=0.3.5 pkgrel=1 pkgdesc="Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide "non-content" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." arch=('i686' 'x86_64') diff --git a/debian.sh b/debian.sh old mode 100644 new mode 100755 index 2d924251..a4d6882e --- a/debian.sh +++ b/debian.sh @@ -2,7 +2,7 @@ APP=pybitmessage PREV_VERSION=0.3.4 -VERSION=0.3.4 +VERSION=0.3.5 RELEASE=1 ARCH_TYPE=`uname -m` DIR=${APP}-${VERSION} diff --git a/debian/changelog b/debian/changelog index b6e8adbf..711f6488 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,31 @@ +pybitmessage (0.3.5-1) raring; urgency=low + + * Inbox message retrieval API functions now also returns read status + * Added right-click option to mark a message as unread + * Prompt user to connect at first startup + * Install into /usr/local by default + * Add a missing rm -f to the uninstall task. + * Use system text color for enabled addresses instead of black + * Added support for Chans + * Start storing msgid in sent table + * Optionally play sounds on connection/disconnection or when messages arrive + * Adding configuration option to listen for connections when using SOCKS + * Added packaging for multiple distros (Arch, Puppy, Slack, etc.) + * Added Russian translation + * Added search support in the UI + * Added 'make uninstall' + * To improve OSX support, use PKCS5_PBKDF2_HMAC_SHA1 if PKCS5_PBKDF2_HMAC is unavailable + * Added better warnings for OSX users who are using old versions of Python + * Repaired debian packaging + * Altered Makefile to avoid needing to chase changes + * Added logger module + * Added bgWorker class for background tasks + * Added use of gevent module + * On not-Windows: Fix insecure keyfile permissions + * Fix 100% CPU usage issue + + -- Bob Mottram (4096 bits) Mon, 29 July 2013 22:11:00 +0100 + pybitmessage (0.3.4-1) raring; urgency=low * Switched addr, msg, broadcast, and getpubkey message types diff --git a/debian/rules b/debian/rules old mode 100644 new mode 100755 diff --git a/ebuild.sh b/ebuild.sh index 1a3d16e0..ed7f1903 100755 --- a/ebuild.sh +++ b/ebuild.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.4 -VERSION=0.3.4 +PREV_VERSION=0.3.5 +VERSION=0.3.5 RELEASE=1 SOURCEDIR=. ARCH_TYPE=`uname -m` diff --git a/generate.sh b/generate.sh index 173e3c79..d21f4d83 100755 --- a/generate.sh +++ b/generate.sh @@ -4,4 +4,4 @@ rm -f Makefile rpmpackage/*.spec -packagemonkey -n "PyBitmessage" --version "0.3.4" --dir "." -l "mit" -e "Bob Mottram (4096 bits) " --brief "Send encrypted messages" --desc "Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide \"non-content\" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." --homepage "https://github.com/Bitmessage/PyBitmessage" --section "mail" --categories "Office/Email" --dependsdeb "python (>= 2.7.0), openssl, python-qt4, libqt4-dev (>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123" --dependsrpm "python, PyQt4, openssl-compat-bitcoin-libs, gst123" --mainscript "bitmessagemain.py" --librarypath "/opt/openssl-compat-bitcoin/lib/" --suggestsdeb "libmessaging-menu-dev" --dependspuppy "openssl, python-qt4, sqlite3, sqlite3-dev, python-openssl, python-sip, gst123" --dependsarch "python2, qt4, python2-pyqt4, sqlite, openssl, gst123" --suggestsarch "python2-gevent" --pythonversion 2 --dependsebuild "dev-libs/openssl, dev-python/PyQt4[${PYTHON_USEDEP}]" --buildebuild "\${PYTHON_DEPS}" --pythonreq "sqlite" --repository "https://github.com/Bitmessage/PyBitmessage.git" +packagemonkey -n "PyBitmessage" --version "0.3.5" --dir "." -l "mit" -e "Bob Mottram (4096 bits) " --brief "Send encrypted messages" --desc "Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide \"non-content\" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." --homepage "https://github.com/Bitmessage/PyBitmessage" --section "mail" --categories "Office/Email" --dependsdeb "python (>= 2.7.0), openssl, python-qt4, libqt4-dev (>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123" --dependsrpm "python, PyQt4, openssl-compat-bitcoin-libs, gst123" --mainscript "bitmessagemain.py" --librarypath "/opt/openssl-compat-bitcoin/lib/" --suggestsdeb "libmessaging-menu-dev" --dependspuppy "openssl, python-qt4, sqlite3, sqlite3-dev, python-openssl, python-sip, gst123" --dependsarch "python2, qt4, python2-pyqt4, sqlite, openssl, gst123" --suggestsarch "python2-gevent" --pythonversion 2 --dependsebuild "dev-libs/openssl, dev-python/PyQt4[${PYTHON_USEDEP}]" --buildebuild "\${PYTHON_DEPS}" --pythonreq "sqlite" --repository "https://github.com/Bitmessage/PyBitmessage.git" diff --git a/puppy.sh b/puppy.sh index efe1d7da..5f2fcae0 100755 --- a/puppy.sh +++ b/puppy.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.4 -VERSION=0.3.4 +PREV_VERSION=0.3.5 +VERSION=0.3.5 RELEASE=1 BUILDDIR=~/petbuild CURRDIR=`pwd` diff --git a/rpm.sh b/rpm.sh index 8269ed9c..09064280 100755 --- a/rpm.sh +++ b/rpm.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.4 -VERSION=0.3.4 +PREV_VERSION=0.3.5 +VERSION=0.3.5 RELEASE=1 SOURCEDIR=. ARCH_TYPE=`uname -m` diff --git a/rpmpackage/pybitmessage.spec b/rpmpackage/pybitmessage.spec index 9c12661a..b15bd586 100644 --- a/rpmpackage/pybitmessage.spec +++ b/rpmpackage/pybitmessage.spec @@ -1,5 +1,5 @@ Name: pybitmessage -Version: 0.3.4 +Version: 0.3.5 Release: 1%{?dist} Summary: Send encrypted messages License: MIT @@ -68,6 +68,31 @@ make install -B DESTDIR=%{buildroot} PREFIX=/usr %attr(644,root,root) /usr/share/icons/hicolor/24x24/apps/%{name}.png %changelog +* Mon Jul 29 2013 Bob Mottram (4096 bits) - 0.3.5-1 +- Inbox message retrieval API functions now also returns read status +- Added right-click option to mark a message as unread +- Prompt user to connect at first startup +- Install into /usr/local by default +- Add a missing rm -f to the uninstall task. +- Use system text color for enabled addresses instead of black +- Added support for Chans +- Start storing msgid in sent table +- Optionally play sounds on connection/disconnection or when messages arrive +- Adding configuration option to listen for connections when using SOCKS +- Added packaging for multiple distros (Arch, Puppy, Slack, etc.) +- Added Russian translation +- Added search support in the UI +- Added 'make uninstall' +- To improve OSX support, use PKCS5_PBKDF2_HMAC_SHA1 if PKCS5_PBKDF2_HMAC is unavailable +- Added better warnings for OSX users who are using old versions of Python +- Repaired debian packaging +- Altered Makefile to avoid needing to chase changes +- Added logger module +- Added bgWorker class for background tasks +- Added use of gevent module +- On not-Windows: Fix insecure keyfile permissions +- Fix 100% CPU usage issue + * Sun Jun 30 2013 Bob Mottram (4096 bits) - 0.3.4-1 - Switched addr, msg, broadcast, and getpubkey message types to 8 byte time. Last remaining type is pubkey. diff --git a/slack.sh b/slack.sh index 5d826e0b..112488c0 100755 --- a/slack.sh +++ b/slack.sh @@ -1,8 +1,8 @@ #!/bin/bash APP=pybitmessage -PREV_VERSION=0.3.4 -VERSION=0.3.4 +PREV_VERSION=0.3.5 +VERSION=0.3.5 RELEASE=1 ARCH_TYPE=`uname -m` BUILDDIR=~/slackbuild From 29be0d55db7909af160730fff43e71e001d53b2d Mon Sep 17 00:00:00 2001 From: gnumac Date: Tue, 30 Jul 2013 00:24:04 +0000 Subject: [PATCH 03/37] Update build_osx.py --- src/build_osx.py | 86 +++++++----------------------------------------- 1 file changed, 11 insertions(+), 75 deletions(-) diff --git a/src/build_osx.py b/src/build_osx.py index dd3d7259..901d1ca6 100644 --- a/src/build_osx.py +++ b/src/build_osx.py @@ -1,80 +1,16 @@ -""" -py2app/py2exe build script for Bitmessage - -Usage (Mac OS X): - python setup.py py2app - -Usage (Windows): - python setup.py py2exe -""" - -import sys, os, shutil, re -from setuptools import setup # @UnresolvedImport - +from setuptools import setup name = "Bitmessage" -mainscript = 'bitmessagemain.py' -version = "0.3.5" - -if sys.platform == 'darwin': - extra_options = dict( - setup_requires=['py2app'], - app=[mainscript], - options=dict(py2app=dict(argv_emulation=True, - includes = ['PyQt4.QtCore','PyQt4.QtGui', 'sip', 'sqlite3'], - packages = ['bitmessageqt'], - frameworks = ['/usr/local/opt/openssl/lib/libcrypto.dylib'], - iconfile='images/bitmessage.icns', - resources=["images"])), - ) -elif sys.platform == 'win32': - extra_options = dict( - setup_requires=['py2exe'], - app=[mainscript], - ) -else: - extra_options = dict( - # Normally unix-like platforms will use "setup.py install" - # and install the main script as such - scripts=[mainscript], - ) +version = "0.3.4" +mainscript = ["bitmessagemain.py"] setup( - name = name, - version = version, - **extra_options + name = name, + version = version, + app = mainscript, + setup_requires = ["py2app"], + options = dict(py2app=dict( + resources = ["images"], + iconfile = "images/bitmessage.icns" + )) ) -from distutils import dir_util -import glob - -if sys.platform == 'darwin': - resource = "dist/" + name + ".app/Contents/Resources/" - framework = "dist/" + name + ".app/Contents/Frameworks/" - - # The pyElliptive module only works with hardcoded libcrypto paths so rename it so it can actually find it. - libs = glob.glob(framework + "libcrypto*.dylib") - for lib in libs: - os.rename(lib, framework + "libcrypto.dylib") - break - - # Try to locate qt_menu - # Let's try the port version first! - if os.path.isfile("/opt/local/lib/Resources/qt_menu.nib"): - qt_menu_location = "/opt/local/lib/Resources/qt_menu.nib" - else: - # No dice? Then let's try the brew version - qt_menu_location = os.popen("find /usr/local/Cellar -name qt_menu.nib | tail -n 1").read() - qt_menu_location = re.sub('\n','', qt_menu_location) - - if(len(qt_menu_location) == 0): - print "Sorry couldn't find your qt_menu.nib this probably won't work" - else: - print "Found your qib: " + qt_menu_location - - # Need to include a copy of qt_menu.nib - shutil.copytree(qt_menu_location, resource + "qt_menu.nib") - # Need to touch qt.conf to avoid loading 2 sets of Qt libraries - fname = resource + "qt.conf" - with file(fname, 'a'): - os.utime(fname, None) - From d606bb133392cec937e50d84c18446d0eeabc3e6 Mon Sep 17 00:00:00 2001 From: nobody Date: Tue, 30 Jul 2013 22:23:18 +0200 Subject: [PATCH 04/37] Fix: Distinguish peers by both IP address and port. Until now many parts of the code assumed that IP addresses are unique for peers. However, more than one Bitmessage instance might be running with a given IP address due to multi-user systems or firewalls. --- src/class_outgoingSynSender.py | 46 +++++++++---------- src/class_receiveDataThread.py | 82 +++++++++++++++++----------------- src/class_sendDataThread.py | 32 ++++++------- src/defaultKnownNodes.py | 17 +++---- src/helper_bootstrap.py | 4 +- src/shared.py | 5 ++- 6 files changed, 92 insertions(+), 94 deletions(-) diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index ac0b11ac..aa9cadf1 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -32,15 +32,15 @@ class outgoingSynSender(threading.Thread): break random.seed() shared.knownNodesLock.acquire() - HOST, = random.sample(shared.knownNodes[self.streamNumber], 1) + peer, = random.sample(shared.knownNodes[self.streamNumber], 1) shared.knownNodesLock.release() shared.alreadyAttemptedConnectionsListLock.acquire() - while HOST in shared.alreadyAttemptedConnectionsList or HOST in shared.connectedHostsList: + while peer in shared.alreadyAttemptedConnectionsList or peer in shared.connectedHostsList: shared.alreadyAttemptedConnectionsListLock.release() # print 'choosing new sample' random.seed() shared.knownNodesLock.acquire() - HOST, = random.sample(shared.knownNodes[self.streamNumber], 1) + peer, = random.sample(shared.knownNodes[self.streamNumber], 1) shared.knownNodesLock.release() time.sleep(1) # Clear out the shared.alreadyAttemptedConnectionsList every half @@ -51,10 +51,10 @@ class outgoingSynSender(threading.Thread): shared.alreadyAttemptedConnectionsListResetTime = int( time.time()) shared.alreadyAttemptedConnectionsListLock.acquire() - shared.alreadyAttemptedConnectionsList[HOST] = 0 + shared.alreadyAttemptedConnectionsList[peer] = 0 shared.alreadyAttemptedConnectionsListLock.release() - PORT, timeNodeLastSeen = shared.knownNodes[ - self.streamNumber][HOST] + timeNodeLastSeen = shared.knownNodes[ + self.streamNumber][peer] sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM) # This option apparently avoids the TIME_WAIT state so that we # can rebind faster @@ -62,13 +62,13 @@ class outgoingSynSender(threading.Thread): sock.settimeout(20) if shared.config.get('bitmessagesettings', 'socksproxytype') == 'none' and shared.verbose >= 2: with shared.printLock: - print 'Trying an outgoing connection to', HOST, ':', PORT + print 'Trying an outgoing connection to', peer # sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS4a': if shared.verbose >= 2: with shared.printLock: - print '(Using SOCKS4a) Trying an outgoing connection to', HOST, ':', PORT + print '(Using SOCKS4a) Trying an outgoing connection to', peer proxytype = socks.PROXY_TYPE_SOCKS4 sockshostname = shared.config.get( @@ -89,7 +89,7 @@ class outgoingSynSender(threading.Thread): elif shared.config.get('bitmessagesettings', 'socksproxytype') == 'SOCKS5': if shared.verbose >= 2: with shared.printLock: - print '(Using SOCKS5) Trying an outgoing connection to', HOST, ':', PORT + print '(Using SOCKS5) Trying an outgoing connection to', peer proxytype = socks.PROXY_TYPE_SOCKS5 sockshostname = shared.config.get( @@ -109,19 +109,19 @@ class outgoingSynSender(threading.Thread): proxytype, sockshostname, socksport, rdns) try: - sock.connect((HOST, PORT)) + sock.connect((peer.host, peer.port)) rd = receiveDataThread() rd.daemon = True # close the main program even if there are threads left someObjectsOfWhichThisRemoteNodeIsAlreadyAware = {} # This is not necessairly a complete list; we clear it from time to time to save memory. - rd.setup(sock, HOST, PORT, self.streamNumber, + rd.setup(sock, peer.host, peer.port, self.streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware, self.selfInitiatedConnections) rd.start() with shared.printLock: - print self, 'connected to', HOST, 'during an outgoing attempt.' + print self, 'connected to', peer, 'during an outgoing attempt.' sd = sendDataThread() - sd.setup(sock, HOST, PORT, self.streamNumber, + sd.setup(sock, peer.host, peer.port, self.streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware) sd.start() sd.sendVersionMessage() @@ -129,16 +129,16 @@ class outgoingSynSender(threading.Thread): except socks.GeneralProxyError as err: if shared.verbose >= 2: with shared.printLock: - print 'Could NOT connect to', HOST, 'during outgoing attempt.', err + print 'Could NOT connect to', peer, 'during outgoing attempt.', err - PORT, timeLastSeen = shared.knownNodes[ - self.streamNumber][HOST] + timeLastSeen = shared.knownNodes[ + self.streamNumber][peer] if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000: # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the shared.knownNodes data-structure. shared.knownNodesLock.acquire() - del shared.knownNodes[self.streamNumber][HOST] + del shared.knownNodes[self.streamNumber][peer] shared.knownNodesLock.release() with shared.printLock: - print 'deleting ', HOST, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.' + print 'deleting ', peer, 'from shared.knownNodes because it is more than 48 hours old and we could not connect to it.' except socks.Socks5AuthError as err: shared.UISignalQueue.put(( @@ -155,16 +155,16 @@ class outgoingSynSender(threading.Thread): else: if shared.verbose >= 1: with shared.printLock: - print 'Could NOT connect to', HOST, 'during outgoing attempt.', err + print 'Could NOT connect to', peer, 'during outgoing attempt.', err - PORT, timeLastSeen = shared.knownNodes[ - self.streamNumber][HOST] + timeLastSeen = shared.knownNodes[ + self.streamNumber][peer] if (int(time.time()) - timeLastSeen) > 172800 and len(shared.knownNodes[self.streamNumber]) > 1000: # for nodes older than 48 hours old if we have more than 1000 hosts in our list, delete from the knownNodes data-structure. shared.knownNodesLock.acquire() - del shared.knownNodes[self.streamNumber][HOST] + del shared.knownNodes[self.streamNumber][peer] shared.knownNodesLock.release() with shared.printLock: - print 'deleting ', HOST, 'from knownNodes because it is more than 48 hours old and we could not connect to it.' + print 'deleting ', peer, 'from knownNodes because it is more than 48 hours old and we could not connect to it.' except Exception as err: sys.stderr.write( diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 2693b293..40e4a97f 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -42,14 +42,13 @@ class receiveDataThread(threading.Thread): someObjectsOfWhichThisRemoteNodeIsAlreadyAware, selfInitiatedConnections): self.sock = sock - self.HOST = HOST - self.PORT = port + self.peer = shared.Peer(HOST, port) self.streamNumber = streamNumber self.payloadLength = 0 # This is the protocol payload length thus it doesn't include the 24 byte message header self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = {} self.selfInitiatedConnections = selfInitiatedConnections shared.connectedHostsList[ - self.HOST] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it. + self.peer] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it. self.connectionIsOrWasFullyEstablished = False # set to true after the remote node and I accept each other's version messages. This is needed to allow the user interface to accurately reflect the current number of connections. if self.streamNumber == -1: # This was an incoming connection. Send out a version message if we accept the other node's version message. self.initiatedConnection = False @@ -70,18 +69,18 @@ class receiveDataThread(threading.Thread): self.data += self.sock.recv(4096) except socket.timeout: with shared.printLock: - print 'Timeout occurred waiting for data from', self.HOST + '. Closing receiveData thread. (ID:', str(id(self)) + ')' + print 'Timeout occurred waiting for data from', self.peer, '. Closing receiveData thread. (ID:', str(id(self)) + ')' break except Exception as err: with shared.printLock: - print 'sock.recv error. Closing receiveData thread (HOST:', self.HOST, 'ID:', str(id(self)) + ').', err + print 'sock.recv error. Closing receiveData thread (HOST:', self.peer, 'ID:', str(id(self)) + ').', err break # print 'Received', repr(self.data) if len(self.data) == dataLen: # If self.sock.recv returned no data: with shared.printLock: - print 'Connection to', self.HOST, 'closed. Closing receiveData thread. (ID:', str(id(self)) + ')' + print 'Connection to', self.peer, 'closed. Closing receiveData thread. (ID:', str(id(self)) + ')' break else: self.processData() @@ -93,16 +92,16 @@ class receiveDataThread(threading.Thread): except: pass - shared.broadcastToSendDataQueues((0, 'shutdown', self.HOST)) + shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) try: - del shared.connectedHostsList[self.HOST] + del shared.connectedHostsList[self.peer] except Exception as err: with shared.printLock: - print 'Could not delete', self.HOST, 'from shared.connectedHostsList.', err + print 'Could not delete', self.peer, 'from shared.connectedHostsList.', err try: del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ - self.HOST] + self.peer] except: pass shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) @@ -137,13 +136,12 @@ class receiveDataThread(threading.Thread): # that other peers can be made aware of its existance. if self.initiatedConnection and self.connectionIsOrWasFullyEstablished: # The remote port is only something we should share with others if it is the remote node's incoming port (rather than some random operating-system-assigned outgoing port). shared.knownNodesLock.acquire() - shared.knownNodes[self.streamNumber][ - self.HOST] = (self.PORT, int(time.time())) + shared.knownNodes[self.streamNumber][self.peer] = int(time.time()) shared.knownNodesLock.release() if self.payloadLength <= 180000000: # If the size of the message is greater than 180MB, ignore it. (I get memory errors when processing messages much larger than this though it is concievable that this value will have to be lowered if some systems are less tolarant of large messages.) remoteCommand = self.data[4:16] with shared.printLock: - print 'remoteCommand', repr(remoteCommand.replace('\x00', '')), ' from', self.HOST + print 'remoteCommand', repr(remoteCommand.replace('\x00', '')), ' from', self.peer if remoteCommand == 'version\x00\x00\x00\x00\x00': self.recversion(self.data[24:self.payloadLength + 24]) @@ -196,28 +194,28 @@ class receiveDataThread(threading.Thread): objectHash] # It is possible that the remote node doesn't respond with the object. In that case, we'll very likely get it from someone else anyway. if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: with shared.printLock: - print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', self.peer + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) try: del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ - self.HOST] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. + self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass break if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: with shared.printLock: - print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', self.peer + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) try: del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ - self.HOST] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. + self.peer] # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. except: pass if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) > 0: with shared.printLock: - print '(concerning', self.HOST + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) - shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.HOST] = len( + shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[self.peer] = len( self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) # this data structure is maintained so that we can keep track of how many total objects, across all connections, are currently outstanding. If it goes too high it can indicate that we are under attack by multiple nodes working together. if len(self.ackDataThatWeHaveYetToSend) > 0: self.data = self.ackDataThatWeHaveYetToSend.pop() @@ -262,22 +260,22 @@ class receiveDataThread(threading.Thread): self.sock.settimeout( 600) # We'll send out a pong every 5 minutes to make sure the connection stays alive if there has been no other traffic to send lately. shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data')) - remoteNodeIncomingPort, remoteNodeSeenTime = shared.knownNodes[ - self.streamNumber][self.HOST] + remoteNodeSeenTime = shared.knownNodes[ + self.streamNumber][self.peer] with shared.printLock: - print 'Connection fully established with', self.HOST, remoteNodeIncomingPort + print 'Connection fully established with', self.peer print 'The size of the connectedHostsList is now', len(shared.connectedHostsList) print 'The length of sendDataQueues is now:', len(shared.sendDataQueues) print 'broadcasting addr from within connectionFullyEstablished function.' - self.broadcastaddr([(int(time.time()), self.streamNumber, 1, self.HOST, - remoteNodeIncomingPort)]) # This lets all of our peers know about this new node. + self.broadcastaddr([(int(time.time()), self.streamNumber, 1, self.peer.host, + self.peer.port)]) # This lets all of our peers know about this new node. self.sendaddr() # This is one large addr message to this one peer. if not self.initiatedConnection and len(shared.connectedHostsList) > 200: with shared.printLock: print 'We are connected to too many people. Closing connection.' - shared.broadcastToSendDataQueues((0, 'shutdown', self.HOST)) + shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) return self.sendBigInv() @@ -1497,7 +1495,7 @@ class receiveDataThread(threading.Thread): self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave[ data[lengthOfVarint + (32 * i):32 + lengthOfVarint + (32 * i)]] = 0 shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ - self.HOST] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + self.peer] = len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) # Send a getdata message to our peer to request the object with the given # hash @@ -1789,8 +1787,9 @@ class receiveDataThread(threading.Thread): if hostFromAddrMessage not in shared.knownNodes[recaddrStream]: if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][hostFromAddrMessage] = ( - recaddrPort, timeSomeoneElseReceivedMessageFromThisNode) + newPeer = shared.Peer(hostFromAddrMessage, recaddrPort) + shared.knownNodes[recaddrStream][newPeer] = ( + timeSomeoneElseReceivedMessageFromThisNode) shared.knownNodesLock.release() with shared.printLock: print 'added new node', hostFromAddrMessage, 'to knownNodes in stream', recaddrStream @@ -1894,8 +1893,8 @@ class receiveDataThread(threading.Thread): numberOfAddressesInAddrMessage = 0 payload = '' # print 'addrsInMyStream.items()', addrsInMyStream.items() - for HOST, value in addrsInMyStream.items(): - PORT, timeLastReceivedMessageFromThisNode = value + for (HOST, PORT), value in addrsInMyStream.items(): + timeLastReceivedMessageFromThisNode = value if timeLastReceivedMessageFromThisNode > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers): # If it is younger than 3 hours old.. numberOfAddressesInAddrMessage += 1 payload += pack( @@ -1956,9 +1955,9 @@ class receiveDataThread(threading.Thread): elif not self.verackSent: self.remoteProtocolVersion, = unpack('>L', data[:4]) if self.remoteProtocolVersion <= 1: - shared.broadcastToSendDataQueues((0, 'shutdown', self.HOST)) + shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) with shared.printLock: - print 'Closing connection to old protocol version 1 node: ', self.HOST + print 'Closing connection to old protocol version 1 node: ', self.peer return # print 'remoteProtocolVersion', self.remoteProtocolVersion @@ -1980,30 +1979,29 @@ class receiveDataThread(threading.Thread): print 'Remote node useragent:', useragent, ' stream number:', self.streamNumber if self.streamNumber != 1: - shared.broadcastToSendDataQueues((0, 'shutdown', self.HOST)) + shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) with shared.printLock: - print 'Closed connection to', self.HOST, 'because they are interested in stream', self.streamNumber, '.' + print 'Closed connection to', self.peer, 'because they are interested in stream', self.streamNumber, '.' return shared.connectedHostsList[ - self.HOST] = 1 # We use this data structure to not only keep track of what hosts we are connected to so that we don't try to connect to them again, but also to list the connections count on the Network Status tab. + self.peer] = 1 # We use this data structure to not only keep track of what hosts we are connected to so that we don't try to connect to them again, but also to list the connections count on the Network Status tab. # If this was an incoming connection, then the sendData thread # doesn't know the stream. We have to set it. if not self.initiatedConnection: shared.broadcastToSendDataQueues(( - 0, 'setStreamNumber', (self.HOST, self.streamNumber))) + 0, 'setStreamNumber', (self.peer, self.streamNumber))) if data[72:80] == shared.eightBytesOfRandomDataUsedToDetectConnectionsToSelf: - shared.broadcastToSendDataQueues((0, 'shutdown', self.HOST)) + shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) with shared.printLock: - print 'Closing connection to myself: ', self.HOST + print 'Closing connection to myself: ', self.peer return shared.broadcastToSendDataQueues((0, 'setRemoteProtocolVersion', ( - self.HOST, self.remoteProtocolVersion))) + self.peer, self.remoteProtocolVersion))) shared.knownNodesLock.acquire() - shared.knownNodes[self.streamNumber][self.HOST] = ( - self.remoteNodeIncomingPort, int(time.time())) + shared.knownNodes[self.streamNumber][self.peer] = int(time.time()) output = open(shared.appdata + 'knownnodes.dat', 'wb') pickle.dump(shared.knownNodes, output) output.close() @@ -2020,7 +2018,7 @@ class receiveDataThread(threading.Thread): try: self.sock.sendall(shared.assembleVersionMessage( - self.HOST, self.PORT, self.streamNumber)) + self.peer.host, self.peer.port, self.streamNumber)) except Exception as err: # if not 'Bad file descriptor' in err: with shared.printLock: diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index dec436e9..ded371a1 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -31,8 +31,7 @@ class sendDataThread(threading.Thread): streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware): self.sock = sock - self.HOST = HOST - self.PORT = PORT + self.peer = shared.Peer(HOST, PORT) self.streamNumber = streamNumber self.remoteProtocolVersion = - \ 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.mailbox queue. @@ -45,7 +44,7 @@ class sendDataThread(threading.Thread): def sendVersionMessage(self): datatosend = shared.assembleVersionMessage( - self.HOST, self.PORT, self.streamNumber) # the IP and port of the remote host, and my streamNumber. + self.peer.host, self.peer.port, self.streamNumber) # the IP and port of the remote host, and my streamNumber. with shared.printLock: print 'Sending version packet: ', repr(datatosend) @@ -62,15 +61,12 @@ class sendDataThread(threading.Thread): def run(self): while True: deststream, command, data = self.mailbox.get() - # with shared.printLock: - # print 'sendDataThread, destream:', deststream, ', Command:', command, ', ID:',id(self), ', HOST:', self.HOST - # if deststream == self.streamNumber or deststream == 0: if command == 'shutdown': - if data == self.HOST or data == 'all': + if data == self.peer or data == 'all': with shared.printLock: - print 'sendDataThread (associated with', self.HOST, ') ID:', id(self), 'shutting down now.' + print 'sendDataThread (associated with', self.peer, ') ID:', id(self), 'shutting down now.' try: self.sock.shutdown(socket.SHUT_RDWR) @@ -89,15 +85,15 @@ class sendDataThread(threading.Thread): # will continue on with the connection and will set the # streamNumber of this send data thread here: elif command == 'setStreamNumber': - hostInMessage, specifiedStreamNumber = data - if hostInMessage == self.HOST: + peerInMessage, specifiedStreamNumber = data + if peerInMessage == self.peer: with shared.printLock: print 'setting the stream number in the sendData thread (ID:', id(self), ') to', specifiedStreamNumber self.streamNumber = specifiedStreamNumber elif command == 'setRemoteProtocolVersion': - hostInMessage, specifiedRemoteProtocolVersion = data - if hostInMessage == self.HOST: + peerInMessage, specifiedRemoteProtocolVersion = data + if peerInMessage == self.peer: with shared.printLock: print 'setting the remote node\'s protocol version in the sendData thread (ID:', id(self), ') to', specifiedRemoteProtocolVersion @@ -113,14 +109,14 @@ class sendDataThread(threading.Thread): self.sock.sendall(data) self.lastTimeISentData = int(time.time()) except: - print 'self.sock.sendall failed' + print 'sendaddr: self.sock.sendall failed' try: self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() except: pass shared.sendDataQueues.remove(self.mailbox) - print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.HOST + print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break elif command == 'sendinv': if data not in self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware: @@ -137,21 +133,21 @@ class sendDataThread(threading.Thread): self.sock.sendall(headerData + payload) self.lastTimeISentData = int(time.time()) except: - print 'self.sock.sendall failed' + print 'sendinv: self.sock.sendall failed' try: self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() except: pass shared.sendDataQueues.remove(self.mailbox) - print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.HOST + print 'sendDataThread thread (ID:', str(id(self)) + ') ending now. Was connected to', self.peer break elif command == 'pong': self.someObjectsOfWhichThisRemoteNodeIsAlreadyAware.clear() # To save memory, let us clear this data structure from time to time. As its function is to help us keep from sending inv messages to peers which sent us the same inv message mere seconds earlier, it will be fine to clear this data structure from time to time. if self.lastTimeISentData < (int(time.time()) - 298): # Send out a pong message to keep the connection alive. with shared.printLock: - print 'Sending pong to', self.HOST, 'to keep connection alive.' + print 'Sending pong to', self.peer, 'to keep connection alive.' try: self.sock.sendall( @@ -165,7 +161,7 @@ class sendDataThread(threading.Thread): except: pass shared.sendDataQueues.remove(self.mailbox) - print 'sendDataThread thread', self, 'ending now. Was connected to', self.HOST + print 'sendDataThread thread', self, 'ending now. Was connected to', self.peer break else: with shared.printLock: diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 5c6d5b21..2308e375 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -5,19 +5,20 @@ import time import random import sys from time import strftime, localtime +import shared def createDefaultKnownNodes(appdata): ############## Stream 1 ################ stream1 = {} - stream1['85.171.174.131'] = (8444,int(time.time())) - stream1['23.28.68.159'] = (8444,int(time.time())) - stream1['66.108.210.240'] = (8080,int(time.time())) - stream1['204.236.246.212'] = (8444,int(time.time())) - stream1['78.81.56.239'] = (8444,int(time.time())) - stream1['122.60.235.157'] = (8444,int(time.time())) - stream1['204.236.246.212'] = (8444,int(time.time())) - stream1['24.98.219.109'] = (8444,int(time.time())) + stream1[shared.Peer('85.171.174.131', 8444)] = int(time.time()) + stream1[shared.Peer('23.28.68.159', 8444)] = int(time.time()) + stream1[shared.Peer('66.108.210.240', 8444)] = int(time.time()) + stream1[shared.Peer('204.236.246.212', 8444)] = int(time.time()) + stream1[shared.Peer('78.81.56.239', 8444)] = int(time.time()) + stream1[shared.Peer('122.60.235.157', 8444)] = int(time.time()) + stream1[shared.Peer('204.236.246.212', 8444)] = int(time.time()) + stream1[shared.Peer('24.98.219.109', 8444)] = int(time.time()) ############# Stream 2 ################# diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index e0056342..e3767df5 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -28,13 +28,13 @@ def dns(): try: for item in socket.getaddrinfo('bootstrap8080.bitmessage.org', 80): print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method' - shared.knownNodes[1][item[4][0]] = (8080, int(time.time())) + shared.knownNodes[1][shared.Peer(item[4][0], 8080)] = int(time.time()) except: print 'bootstrap8080.bitmessage.org DNS bootstrapping failed.' try: for item in socket.getaddrinfo('bootstrap8444.bitmessage.org', 80): print 'Adding', item[4][0], 'to knownNodes based on DNS boostrap method' - shared.knownNodes[1][item[4][0]] = (8444, int(time.time())) + shared.knownNodes[1][shared.Peer(item[4][0], 8444)] = int(time.time()) except: print 'bootstrap8444.bitmessage.org DNS bootstrapping failed.' else: diff --git a/src/shared.py b/src/shared.py index 32370524..214c124f 100644 --- a/src/shared.py +++ b/src/shared.py @@ -9,6 +9,7 @@ useVeryEasyProofOfWorkForTesting = False # If you set this to True while on the # Libraries. +import collections import ConfigParser import os import pickle @@ -370,5 +371,7 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys): logger.exception('Keyfile permissions could not be fixed.') raise +Peer = collections.namedtuple('Peer', ['host', 'port']) + helper_startup.loadConfig() -from debug import logger \ No newline at end of file +from debug import logger From fbbc65738055b7f61aab89e0715bb09ef531c593 Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Tue, 30 Jul 2013 19:41:40 -0400 Subject: [PATCH 05/37] Add listSubscriptions method to API --- src/bitmessagemain.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 51b9a82d..add370a0 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -677,6 +677,23 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.UISignalQueue.put(('rerenderInboxFromLabels', '')) shared.UISignalQueue.put(('rerenderSubscriptions', '')) return 'Deleted subscription if it existed.' + elif method == 'listSubscriptions': + if len(params) != 0: + return "API Error 0000: I don't accept parameters!" + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''SELECT label, address, enabled FROM subscriptions''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + data = '{"subscriptions":[' + for row in queryreturn: + label, address, enabled = row + label = shared.fixPotentiallyInvalidUTF8Data(label) + if len(data) > 20: + data += ',' + data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) + data += ']}' + return data elif method == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: From 8c0450ce3959cdf17461cf4dcbe2557bccc3d2c9 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 30 Jul 2013 19:53:09 -0400 Subject: [PATCH 06/37] having parameters here doesn't hurt anything --- src/bitmessagemain.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index add370a0..3fc5a3ee 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -678,8 +678,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.UISignalQueue.put(('rerenderSubscriptions', '')) return 'Deleted subscription if it existed.' elif method == 'listSubscriptions': - if len(params) != 0: - return "API Error 0000: I don't accept parameters!" shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT label, address, enabled FROM subscriptions''') shared.sqlSubmitQueue.put('') From cddfe2c44fb10cda3093e3c45be1e7dfe2323734 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 31 Jul 2013 12:08:56 -0400 Subject: [PATCH 07/37] Only return one item for certain API commands --- src/bitmessagemain.py | 8 ++++---- src/class_singleWorker.py | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index add370a0..1811141e 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -341,8 +341,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': ')) - data += ']}' - return data + data += ']}' + return data elif method == 'getAllSentMessages': shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT msgid, toaddress, fromaddress, subject, lastactiontime, message, encodingtype, status, ackdata FROM sent where folder='sent' ORDER BY lastactiontime''') @@ -395,8 +395,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): subject = shared.fixPotentiallyInvalidUTF8Data(subject) message = shared.fixPotentiallyInvalidUTF8Data(message) data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) - data += ']}' - return data + data += ']}' + return data elif method == 'getSentMessagesByAddress' or method == 'getSentMessagesBySender': if len(params) == 0: return 'API Error 0000: I need parameters!' diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 2ba34eeb..2db7b587 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -414,7 +414,8 @@ class singleWorker(threading.Thread): objectType = 'broadcast' shared.inventory[inventoryHash] = ( objectType, streamNumber, payload, int(time.time())) - print 'sending inv (within sendBroadcast function)' + with shared.printLock: + print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( streamNumber, 'sendinv', inventoryHash)) From c424885734c349ecb0ce1ee9f42a7800fb925293 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 31 Jul 2013 12:36:51 -0400 Subject: [PATCH 08/37] Update statuses of sent broadcasts seperately even if all sent data is identical for two messages --- src/class_singleWorker.py | 252 ++++++++++++++------------------------ 1 file changed, 89 insertions(+), 163 deletions(-) diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 2db7b587..56f23859 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -271,173 +271,99 @@ class singleWorker(threading.Thread): fromaddress, subject, body, ackdata = row status, addressVersionNumber, streamNumber, ripe = decodeAddress( fromaddress) - """if addressVersionNumber == 2 and int(time.time()) < shared.encryptedBroadcastSwitchoverTime: - # We need to convert our private keys to public keys in order - # to include them. - try: - privSigningKeyBase58 = shared.config.get( - fromaddress, 'privsigningkey') - privEncryptionKeyBase58 = shared.config.get( - fromaddress, 'privencryptionkey') - except: - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( - ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file.")))) - continue - - privSigningKeyHex = shared.decodeWalletImportFormat( - privSigningKeyBase58).encode('hex') - privEncryptionKeyHex = shared.decodeWalletImportFormat( - privEncryptionKeyBase58).encode('hex') - - pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode( - 'hex') # At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message. - pubEncryptionKey = highlevelcrypto.privToPub( - privEncryptionKeyHex).decode('hex') - - payload = pack('>Q', (int(time.time()) + random.randrange( - -300, 300))) # the current time plus or minus five minutes - payload += encodeVarint(1) # broadcast version - payload += encodeVarint(addressVersionNumber) - payload += encodeVarint(streamNumber) - payload += '\x00\x00\x00\x01' # behavior bitfield - payload += pubSigningKey[1:] - payload += pubEncryptionKey[1:] - payload += ripe - payload += '\x02' # message encoding type - payload += encodeVarint(len( - 'Subject:' + subject + '\n' + 'Body:' + body)) # Type 2 is simple UTF-8 message encoding. - payload += 'Subject:' + subject + '\n' + 'Body:' + body - - signature = highlevelcrypto.sign(payload, privSigningKeyHex) - payload += encodeVarint(len(signature)) - payload += signature - - target = 2 ** 64 / ((len( - payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) - print '(For broadcast message) Doing proof of work...' - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( - ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast...")))) - initialHash = hashlib.sha512(payload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) - print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce - - payload = pack('>Q', nonce) + payload - - inventoryHash = calculateInventoryHash(payload) - objectType = 'broadcast' - shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, int(time.time())) - print 'Broadcasting inv for my broadcast (within sendBroadcast function):', inventoryHash.encode('hex') - shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) - - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) - - # Update the status of the message in the 'sent' table to have - # a 'broadcastsent' status - shared.sqlLock.acquire() - t = ('broadcastsent', int( - time.time()), fromaddress, subject, body, 'broadcastqueued') - shared.sqlSubmitQueue.put( - 'UPDATE sent SET status=?, lastactiontime=? WHERE fromaddress=? AND subject=? AND message=? AND status=?') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release()""" - if addressVersionNumber == 2 or addressVersionNumber == 3: - # We need to convert our private keys to public keys in order - # to include them. - try: - privSigningKeyBase58 = shared.config.get( - fromaddress, 'privsigningkey') - privEncryptionKeyBase58 = shared.config.get( - fromaddress, 'privencryptionkey') - except: - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( - ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file.")))) - continue - - privSigningKeyHex = shared.decodeWalletImportFormat( - privSigningKeyBase58).encode('hex') - privEncryptionKeyHex = shared.decodeWalletImportFormat( - privEncryptionKeyBase58).encode('hex') - - pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode( - 'hex') # At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message. - pubEncryptionKey = highlevelcrypto.privToPub( - privEncryptionKeyHex).decode('hex') - - payload = pack('>Q', (int(time.time()) + random.randrange( - -300, 300))) # the current time plus or minus five minutes - payload += encodeVarint(2) # broadcast version - payload += encodeVarint(streamNumber) - - dataToEncrypt = encodeVarint(2) # broadcast version - dataToEncrypt += encodeVarint(addressVersionNumber) - dataToEncrypt += encodeVarint(streamNumber) - dataToEncrypt += '\x00\x00\x00\x01' # behavior bitfield - dataToEncrypt += pubSigningKey[1:] - dataToEncrypt += pubEncryptionKey[1:] - if addressVersionNumber >= 3: - dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'noncetrialsperbyte')) - dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'payloadlengthextrabytes')) - dataToEncrypt += '\x02' # message encoding type - dataToEncrypt += encodeVarint(len('Subject:' + subject + '\n' + 'Body:' + body)) #Type 2 is simple UTF-8 message encoding per the documentation on the wiki. - dataToEncrypt += 'Subject:' + subject + '\n' + 'Body:' + body - signature = highlevelcrypto.sign( - dataToEncrypt, privSigningKeyHex) - dataToEncrypt += encodeVarint(len(signature)) - dataToEncrypt += signature - - # Encrypt the broadcast with the information contained in the broadcaster's address. Anyone who knows the address can generate - # the private encryption key to decrypt the broadcast. This provides virtually no privacy; its purpose is to keep questionable - # and illegal content from flowing through the Internet connections and being stored on the disk of 3rd parties. - privEncryptionKey = hashlib.sha512(encodeVarint( - addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()[:32] - pubEncryptionKey = pointMult(privEncryptionKey) - payload += highlevelcrypto.encrypt( - dataToEncrypt, pubEncryptionKey.encode('hex')) - - target = 2 ** 64 / ((len( - payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) - print '(For broadcast message) Doing proof of work...' - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( - ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast...")))) - initialHash = hashlib.sha512(payload).digest() - trialValue, nonce = proofofwork.run(target, initialHash) - print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce - - payload = pack('>Q', nonce) + payload - - inventoryHash = calculateInventoryHash(payload) - objectType = 'broadcast' - shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, int(time.time())) - with shared.printLock: - print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') - shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) - - shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(unicode( - strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) - - # Update the status of the message in the 'sent' table to have - # a 'broadcastsent' status - shared.sqlLock.acquire() - t = (inventoryHash,'broadcastsent', int( - time.time()), fromaddress, subject, body, 'broadcastqueued') - shared.sqlSubmitQueue.put( - 'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE fromaddress=? AND subject=? AND message=? AND status=?') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlSubmitQueue.put('commit') - shared.sqlLock.release() - else: + if addressVersionNumber <= 1: with shared.printLock: sys.stderr.write( 'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n') + return + # We need to convert our private keys to public keys in order + # to include them. + try: + privSigningKeyBase58 = shared.config.get( + fromaddress, 'privsigningkey') + privEncryptionKeyBase58 = shared.config.get( + fromaddress, 'privencryptionkey') + except: + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( + ackdata, tr.translateText("MainWindow", "Error! Could not find sender address (your address) in the keys.dat file.")))) + continue + privSigningKeyHex = shared.decodeWalletImportFormat( + privSigningKeyBase58).encode('hex') + privEncryptionKeyHex = shared.decodeWalletImportFormat( + privEncryptionKeyBase58).encode('hex') + + pubSigningKey = highlevelcrypto.privToPub(privSigningKeyHex).decode( + 'hex') # At this time these pubkeys are 65 bytes long because they include the encoding byte which we won't be sending in the broadcast message. + pubEncryptionKey = highlevelcrypto.privToPub( + privEncryptionKeyHex).decode('hex') + + payload = pack('>Q', (int(time.time()) + random.randrange( + -300, 300))) # the current time plus or minus five minutes + payload += encodeVarint(2) # broadcast version + payload += encodeVarint(streamNumber) + + dataToEncrypt = encodeVarint(2) # broadcast version + dataToEncrypt += encodeVarint(addressVersionNumber) + dataToEncrypt += encodeVarint(streamNumber) + dataToEncrypt += '\x00\x00\x00\x01' # behavior bitfield + dataToEncrypt += pubSigningKey[1:] + dataToEncrypt += pubEncryptionKey[1:] + if addressVersionNumber >= 3: + dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'noncetrialsperbyte')) + dataToEncrypt += encodeVarint(shared.config.getint(fromaddress,'payloadlengthextrabytes')) + dataToEncrypt += '\x02' # message encoding type + dataToEncrypt += encodeVarint(len('Subject:' + subject + '\n' + 'Body:' + body)) #Type 2 is simple UTF-8 message encoding per the documentation on the wiki. + dataToEncrypt += 'Subject:' + subject + '\n' + 'Body:' + body + signature = highlevelcrypto.sign( + dataToEncrypt, privSigningKeyHex) + dataToEncrypt += encodeVarint(len(signature)) + dataToEncrypt += signature + + # Encrypt the broadcast with the information contained in the broadcaster's address. Anyone who knows the address can generate + # the private encryption key to decrypt the broadcast. This provides virtually no privacy; its purpose is to keep questionable + # and illegal content from flowing through the Internet connections and being stored on the disk of 3rd parties. + privEncryptionKey = hashlib.sha512(encodeVarint( + addressVersionNumber) + encodeVarint(streamNumber) + ripe).digest()[:32] + pubEncryptionKey = pointMult(privEncryptionKey) + payload += highlevelcrypto.encrypt( + dataToEncrypt, pubEncryptionKey.encode('hex')) + + target = 2 ** 64 / ((len( + payload) + shared.networkDefaultPayloadLengthExtraBytes + 8) * shared.networkDefaultProofOfWorkNonceTrialsPerByte) + print '(For broadcast message) Doing proof of work...' + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', ( + ackdata, tr.translateText("MainWindow", "Doing work necessary to send broadcast...")))) + initialHash = hashlib.sha512(payload).digest() + trialValue, nonce = proofofwork.run(target, initialHash) + print '(For broadcast message) Found proof of work', trialValue, 'Nonce:', nonce + + payload = pack('>Q', nonce) + payload + + inventoryHash = calculateInventoryHash(payload) + objectType = 'broadcast' + shared.inventory[inventoryHash] = ( + objectType, streamNumber, payload, int(time.time())) + with shared.printLock: + print 'sending inv (within sendBroadcast function) for object:', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + streamNumber, 'sendinv', inventoryHash)) + + shared.UISignalQueue.put(('updateSentItemStatusByAckdata', (ackdata, tr.translateText("MainWindow", "Broadcast sent on %1").arg(unicode( + strftime(shared.config.get('bitmessagesettings', 'timeformat'), localtime(int(time.time()))), 'utf-8'))))) + + # Update the status of the message in the 'sent' table to have + # a 'broadcastsent' status + shared.sqlLock.acquire() + t = (inventoryHash,'broadcastsent', int( + time.time()), ackdata) + shared.sqlSubmitQueue.put( + 'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=?') + shared.sqlSubmitQueue.put(t) + queryreturn = shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + def sendMsg(self): # Check to see if there are any messages queued to be sent From 46c900f027944d4c19e3aedfb72130cf74b20299 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 31 Jul 2013 15:38:01 -0400 Subject: [PATCH 09/37] show Invalid Method error in same format as other API errors --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index d3ed53fa..8168fd52 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -695,7 +695,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): elif method == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: - return 'Invalid Method: %s' % method + return 'API Error 0020: Invalid method: %s' % method # This thread, of which there is only one, runs the API. From dda530ca07788986d817e9130c206df267e250dc Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 31 Jul 2013 22:25:34 +0100 Subject: [PATCH 10/37] Set a maximum frequency for playing sounds --- src/bitmessageqt/__init__.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 96db1f92..033ce61f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -34,7 +34,7 @@ import platform import debug from debug import logger import subprocess - +import datetime try: from PyQt4 import QtCore, QtGui @@ -65,6 +65,12 @@ class MyForm(QtGui.QMainWindow): SOUND_DISCONNECTED = 4 SOUND_CONNECTION_GREEN = 5 + # the last time that a message arrival sound was played + lastSoundTime = datetime.datetime.now() - datetime.timedelta(days=1) + + # the maximum frequency of message sounds in seconds + maxSoundFrequencySec = 60 + str_broadcast_subscribers = '[Broadcast subscribers]' str_chan = '[chan]' @@ -429,7 +435,6 @@ class MyForm(QtGui.QMainWindow): self.rerenderComboBoxSendFrom() - # Show or hide the application window after clicking an item within the # tray icon or, on Windows, the try icon itself. def appIndicatorShowOrHideWindow(self): @@ -985,13 +990,26 @@ class MyForm(QtGui.QMainWindow): # play a sound def playSound(self, category, label): soundFilename = None + play = True if label is not None: - # does a sound file exist for this particular contact? + # Does a sound file exist for this particular contact? if (os.path.isfile(shared.appdata + 'sounds/' + label + '.wav') or os.path.isfile(shared.appdata + 'sounds/' + label + '.mp3')): soundFilename = shared.appdata + 'sounds/' + label + # Avoid making sounds more frequently than the threshold. + # This suppresses playing sounds repeatedly when there + # are many new messages + if (soundFilename is None and + category is not self.SOUND_CONNECTED and + category is not self.SOUND_DISCONNECTED and + category is not self.SOUND_CONNECTION_GREEN): + dt = datetime.datetime.now() - self.lastSoundTime + self.lastSoundTime = datetime.datetime.now() + if dt.total_seconds() < self.maxSoundFrequencySec: + play = False + if soundFilename is None: if category is self.SOUND_KNOWN: soundFilename = shared.appdata + 'sounds/known' @@ -1002,9 +1020,9 @@ class MyForm(QtGui.QMainWindow): elif category is self.SOUND_DISCONNECTED: soundFilename = shared.appdata + 'sounds/disconnected' elif category is self.SOUND_CONNECTION_GREEN: - soundFilename = shared.appdata + 'sounds/green' + soundFilename = shared.appdata + 'sounds/green' - if soundFilename is not None: + if soundFilename is not None and play is True: # if not wav then try mp3 format if not os.path.isfile(soundFilename + '.wav'): soundFilename = soundFilename + '.mp3' From 04cb6575bd2b6ac2b6ff8da0db618107aaba2ecd Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Wed, 31 Jul 2013 07:59:09 -0400 Subject: [PATCH 11/37] getInboxMessageIds and getSentMessageIds --- src/bitmessagemain.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 8168fd52..16a11c6f 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -325,6 +325,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) data += ']}' return data + elif method == 'getAllInboxMessageIds': + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put( + '''SELECT msgid FROM inbox where folder='inbox' ORDER BY received''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + data = '{"inboxMessageIds":[' + for row in queryreturn: + msgid = row[0] + if len(data) > 25: + data += ',' + data += json.dumps({'msgid': msgid.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data elif method == 'getInboxMessageById' or method == 'getInboxMessageByID': if len(params) == 0: return 'API Error 0000: I need parameters!' @@ -359,6 +374,20 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) data += ']}' return data + elif method == 'getAllSentMessageIds': + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() + data = '{"sentMessageIds":[' + for row in queryreturn: + msgid = row[0] + if len(data) > 25: + data += ',' + data += json.dumps({'msgid':msgid.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data elif method == 'getInboxMessagesByAddress': if len(params) == 0: return 'API Error 0000: I need parameters!' From fde0739652ed18c15d56e480e5b87d23d3a87169 Mon Sep 17 00:00:00 2001 From: Cameron Conn Date: Thu, 1 Aug 2013 00:26:46 -0500 Subject: [PATCH 12/37] made generate.sh readable, and added icons and shortcuts to the menubar --- generate.sh | 27 ++++++++++++++- src/bitmessageqt/bitmessageui.py | 30 +++++++++++++---- src/bitmessageqt/bitmessageui.ui | 58 ++++++++++++++++++++++++++++---- 3 files changed, 102 insertions(+), 13 deletions(-) diff --git a/generate.sh b/generate.sh index d21f4d83..3ab99536 100755 --- a/generate.sh +++ b/generate.sh @@ -4,4 +4,29 @@ rm -f Makefile rpmpackage/*.spec -packagemonkey -n "PyBitmessage" --version "0.3.5" --dir "." -l "mit" -e "Bob Mottram (4096 bits) " --brief "Send encrypted messages" --desc "Bitmessage is a P2P communications protocol used to send encrypted messages to another person or to many subscribers. It is decentralized and trustless, meaning that you need-not inherently trust any entities like root certificate authorities. It uses strong authentication which means that the sender of a message cannot be spoofed, and it aims to hide \"non-content\" data, like the sender and receiver of messages, from passive eavesdroppers like those running warrantless wiretapping programs." --homepage "https://github.com/Bitmessage/PyBitmessage" --section "mail" --categories "Office/Email" --dependsdeb "python (>= 2.7.0), openssl, python-qt4, libqt4-dev (>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123" --dependsrpm "python, PyQt4, openssl-compat-bitcoin-libs, gst123" --mainscript "bitmessagemain.py" --librarypath "/opt/openssl-compat-bitcoin/lib/" --suggestsdeb "libmessaging-menu-dev" --dependspuppy "openssl, python-qt4, sqlite3, sqlite3-dev, python-openssl, python-sip, gst123" --dependsarch "python2, qt4, python2-pyqt4, sqlite, openssl, gst123" --suggestsarch "python2-gevent" --pythonversion 2 --dependsebuild "dev-libs/openssl, dev-python/PyQt4[${PYTHON_USEDEP}]" --buildebuild "\${PYTHON_DEPS}" --pythonreq "sqlite" --repository "https://github.com/Bitmessage/PyBitmessage.git" +packagemonkey -n "PyBitmessage" --version "0.3.5" --dir "." -l "mit" \ + -e "Bob Mottram (4096 bits) " \ + --brief "Send encrypted messages" \ + --desc "Bitmessage is a P2P communications protocol used to send " \ + "encrypted messages to another person or to many subscribers. It is " \ + "decentralized and trustless, meaning that you need-not inherently" \ + "trust any entities like root certificate authorities. It uses strong " \ + "authentication which means that the sender of a message cannot be" \ + "spoofed, and it aims to hide \"non-content\" data, like the sender and " \ + "receiver of messages, from passive eavesdroppers like those running " \ + "warrantless wiretapping programs." \ + --homepage "https://github.com/Bitmessage/PyBitmessage" --section "mail" \ + --categories "Office/Email" \ + --dependsdeb "python (>= 2.7.0), openssl, python-qt4, libqt4-dev " \ + "(>= 4.8.0), python-qt4-dev, sqlite3, libsqlite3-dev, gst123" \ + --dependsrpm "python, PyQt4, openssl-compat-bitcoin-libs, gst123" \ + --mainscript "bitmessagemain.py" \ + --librarypath "/opt/openssl-compat-bitcoin/lib/" \ + --suggestsdeb "libmessaging-menu-dev" \ + --dependspuppy "openssl, python-qt4, sqlite3, sqlite3-dev, " \ + "python-openssl, python-sip, gst123" \ + --dependsarch "python2, qt4, python2-pyqt4, sqlite, openssl, gst123" \ + --suggestsarch "python2-gevent" --pythonversion 2 \ + --dependsebuild "dev-libs/openssl, dev-python/PyQt4[${PYTHON_USEDEP}]" \ + --buildebuild "\${PYTHON_DEPS}" --pythonreq "sqlite" \ + --repository "https://github.com/Bitmessage/PyBitmessage.git" diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 4efecb97..d5388182 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Sun Jul 21 17:50:02 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Thu Aug 1 00:22:41 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(795, 561) + MainWindow.resize(775, 598) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) @@ -422,7 +422,7 @@ class Ui_MainWindow(object): self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 18)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 775, 21)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -440,20 +440,36 @@ class Ui_MainWindow(object): self.actionManageKeys = QtGui.QAction(MainWindow) self.actionManageKeys.setCheckable(False) self.actionManageKeys.setEnabled(True) + icon = QtGui.QIcon.fromTheme(_fromUtf8("dialog-password")) + self.actionManageKeys.setIcon(icon) self.actionManageKeys.setObjectName(_fromUtf8("actionManageKeys")) self.actionExit = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("application-exit")) + self.actionExit.setIcon(icon) self.actionExit.setObjectName(_fromUtf8("actionExit")) self.actionHelp = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("help-contents")) + self.actionHelp.setIcon(icon) self.actionHelp.setObjectName(_fromUtf8("actionHelp")) self.actionAbout = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("help-about")) + self.actionAbout.setIcon(icon) self.actionAbout.setObjectName(_fromUtf8("actionAbout")) self.actionSettings = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("document-properties")) + self.actionSettings.setIcon(icon) self.actionSettings.setObjectName(_fromUtf8("actionSettings")) self.actionRegenerateDeterministicAddresses = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("view-refresh")) + self.actionRegenerateDeterministicAddresses.setIcon(icon) self.actionRegenerateDeterministicAddresses.setObjectName(_fromUtf8("actionRegenerateDeterministicAddresses")) self.actionDeleteAllTrashedMessages = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("user-trash")) + self.actionDeleteAllTrashedMessages.setIcon(icon) self.actionDeleteAllTrashedMessages.setObjectName(_fromUtf8("actionDeleteAllTrashedMessages")) self.actionJoinChan = QtGui.QAction(MainWindow) + icon = QtGui.QIcon.fromTheme(_fromUtf8("contact-new")) + self.actionJoinChan.setIcon(icon) self.actionJoinChan.setObjectName(_fromUtf8("actionJoinChan")) self.menuFile.addAction(self.actionManageKeys) self.menuFile.addAction(self.actionDeleteAllTrashedMessages) @@ -523,8 +539,8 @@ class Ui_MainWindow(object): self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" -"


", None)) +"\n" +"


", None)) self.label.setText(_translate("MainWindow", "To:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None)) @@ -597,7 +613,9 @@ class Ui_MainWindow(object): self.actionImport_keys.setText(_translate("MainWindow", "Import keys", None)) self.actionManageKeys.setText(_translate("MainWindow", "Manage keys", None)) self.actionExit.setText(_translate("MainWindow", "Quit", None)) + self.actionExit.setShortcut(_translate("MainWindow", "Ctrl+Q", None)) self.actionHelp.setText(_translate("MainWindow", "Help", None)) + self.actionHelp.setShortcut(_translate("MainWindow", "F1", None)) self.actionAbout.setText(_translate("MainWindow", "About", None)) self.actionSettings.setText(_translate("MainWindow", "Settings", None)) self.actionRegenerateDeterministicAddresses.setText(_translate("MainWindow", "Regenerate deterministic addresses", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index d4478fc8..5d616be7 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -6,8 +6,8 @@ 0 0 - 795 - 561 + 775 + 598 @@ -257,8 +257,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> +</style></head><body style=" font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> @@ -1010,8 +1010,8 @@ p, li { white-space: pre-wrap; } 0 0 - 795 - 18 + 775 + 21 @@ -1061,41 +1061,87 @@ p, li { white-space: pre-wrap; } true + + + + + Manage keys + + + + + Quit + + Ctrl+Q + + + + + + Help + + F1 + + + + + + About + + + + + Settings + + + + + Regenerate deterministic addresses + + + + + Delete all trashed messages + + + + + Join / Create chan From b06ee336adfe53b77572c045fe3ebe730e843e96 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 1 Aug 2013 09:58:30 +0100 Subject: [PATCH 13/37] Time is reset only when a sound is played #355 --- src/bitmessageqt/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 033ce61f..86d48f0f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1006,7 +1006,6 @@ class MyForm(QtGui.QMainWindow): category is not self.SOUND_DISCONNECTED and category is not self.SOUND_CONNECTION_GREEN): dt = datetime.datetime.now() - self.lastSoundTime - self.lastSoundTime = datetime.datetime.now() if dt.total_seconds() < self.maxSoundFrequencySec: play = False @@ -1023,6 +1022,9 @@ class MyForm(QtGui.QMainWindow): soundFilename = shared.appdata + 'sounds/green' if soundFilename is not None and play is True: + # record the last time that a sound was played + self.lastSoundTime = datetime.datetime.now() + # if not wav then try mp3 format if not os.path.isfile(soundFilename + '.wav'): soundFilename = soundFilename + '.mp3' From 401c95cdb6f15246267db82b3849c274c187d570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C3=B6=20Barany?= Date: Thu, 1 Aug 2013 12:31:40 +0200 Subject: [PATCH 14/37] Correct handling of old knownnodes.dat format at import. --- src/defaultKnownNodes.py | 9 +++++++-- src/helper_bootstrap.py | 14 +++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 2308e375..52e31fff 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -49,10 +49,15 @@ def readDefaultKnownNodes(appdata): pickleFile = open(appdata + 'knownnodes.dat', 'rb') knownNodes = pickle.load(pickleFile) pickleFile.close() - knownNodes for stream, storedValue in knownNodes.items(): for host,value in storedValue.items(): - port, storedtime = storedValue[host] + try: + # Old knownNodes format. + port, storedtime = value + except: + # New knownNodes format. + host, port = host + storedtime = value print host, '\t', port, '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(storedtime)),'utf-8') if __name__ == "__main__": diff --git a/src/helper_bootstrap.py b/src/helper_bootstrap.py index e3767df5..f0cae8f2 100644 --- a/src/helper_bootstrap.py +++ b/src/helper_bootstrap.py @@ -9,8 +9,20 @@ def knownNodes(): # We shouldn't have to use the shared.knownNodesLock because this had # better be the only thread accessing knownNodes right now. pickleFile = open(shared.appdata + 'knownnodes.dat', 'rb') - shared.knownNodes = pickle.load(pickleFile) + loadedKnownNodes = pickle.load(pickleFile) pickleFile.close() + # The old format of storing knownNodes was as a 'host: (port, time)' + # mapping. The new format is as 'Peer: time' pairs. If we loaded + # data in the old format, transform it to the new style. + for stream, nodes in loadedKnownNodes.items(): + shared.knownNodes[stream] = {} + for node_tuple in nodes.items(): + try: + host, (port, time) = node_tuple + peer = shared.Peer(host, port) + except: + peer, time = node_tuple + shared.knownNodes[stream][peer] = time except: shared.knownNodes = defaultKnownNodes.createDefaultKnownNodes(shared.appdata) if shared.config.getint('bitmessagesettings', 'settingsversion') > 6: From 6b01e8aa3307783334b472b07e4aa5eb70387d45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C3=B6=20Barany?= Date: Thu, 1 Aug 2013 12:32:07 +0200 Subject: [PATCH 15/37] Bug fixes in new peer handling. --- src/class_receiveDataThread.py | 70 ++++++++++++++++------------------ src/class_sendDataThread.py | 2 +- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 40e4a97f..5af33157 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -42,7 +42,7 @@ class receiveDataThread(threading.Thread): someObjectsOfWhichThisRemoteNodeIsAlreadyAware, selfInitiatedConnections): self.sock = sock - self.peer = shared.Peer(HOST, port) + self.peer = shared.Peer(HOST, port) self.streamNumber = streamNumber self.payloadLength = 0 # This is the protocol payload length thus it doesn't include the 24 byte message header self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = {} @@ -194,7 +194,7 @@ class receiveDataThread(threading.Thread): objectHash] # It is possible that the remote node doesn't respond with the object. In that case, we'll very likely get it from someone else anyway. if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: with shared.printLock: - print '(concerning', self.peer + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) try: del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ @@ -204,7 +204,7 @@ class receiveDataThread(threading.Thread): break if len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) == 0: with shared.printLock: - print '(concerning', self.peer + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) + print '(concerning', str(self.peer) + ')', 'number of objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave is now', len(self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave) try: del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ @@ -1677,11 +1677,11 @@ class receiveDataThread(threading.Thread): shared.knownNodesLock.acquire() shared.knownNodes[recaddrStream] = {} shared.knownNodesLock.release() - if hostFromAddrMessage not in shared.knownNodes[recaddrStream]: + peerFromAddrMessage = shared.Peer(hostFromAddrMessage, recaddrPort) + if peerFromAddrMessage not in shared.knownNodes[recaddrStream]: if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][hostFromAddrMessage] = ( - recaddrPort, timeSomeoneElseReceivedMessageFromThisNode) + shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode shared.knownNodesLock.release() needToWriteKnownNodesToDisk = True hostDetails = ( @@ -1690,15 +1690,12 @@ class receiveDataThread(threading.Thread): listOfAddressDetailsToBroadcastToPeers.append( hostDetails) else: - PORT, timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ - hostFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. + timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ + peerFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())): shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][hostFromAddrMessage] = ( - PORT, timeSomeoneElseReceivedMessageFromThisNode) + shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode shared.knownNodesLock.release() - if PORT != recaddrPort: - print 'Strange occurance: The port specified in an addr message', str(recaddrPort), 'does not match the port', str(PORT), 'that this program (or some other peer) used to connect to it', str(hostFromAddrMessage), '. Perhaps they changed their port or are using a strange NAT configuration.' if needToWriteKnownNodesToDisk: # Runs if any nodes were new to us. Also, share those nodes with our peers. shared.knownNodesLock.acquire() output = open(shared.appdata + 'knownnodes.dat', 'wb') @@ -1784,15 +1781,15 @@ class receiveDataThread(threading.Thread): shared.knownNodesLock.acquire() shared.knownNodes[recaddrStream] = {} shared.knownNodesLock.release() - if hostFromAddrMessage not in shared.knownNodes[recaddrStream]: + peerFromAddrMessage = shared.Peer(hostFromAddrMessage, recaddrPort) + if peerFromAddrMessage not in shared.knownNodes[recaddrStream]: if len(shared.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. shared.knownNodesLock.acquire() - newPeer = shared.Peer(hostFromAddrMessage, recaddrPort) - shared.knownNodes[recaddrStream][newPeer] = ( + shared.knownNodes[recaddrStream][peerFromAddrMessage] = ( timeSomeoneElseReceivedMessageFromThisNode) shared.knownNodesLock.release() with shared.printLock: - print 'added new node', hostFromAddrMessage, 'to knownNodes in stream', recaddrStream + print 'added new node', peerFromAddrMessage, 'to knownNodes in stream', recaddrStream needToWriteKnownNodesToDisk = True hostDetails = ( @@ -1801,15 +1798,12 @@ class receiveDataThread(threading.Thread): listOfAddressDetailsToBroadcastToPeers.append( hostDetails) else: - PORT, timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ - hostFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. + timeLastReceivedMessageFromThisNode = shared.knownNodes[recaddrStream][ + peerFromAddrMessage] # PORT in this case is either the port we used to connect to the remote node, or the port that was specified by someone else in a past addr message. if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())): shared.knownNodesLock.acquire() - shared.knownNodes[recaddrStream][hostFromAddrMessage] = ( - PORT, timeSomeoneElseReceivedMessageFromThisNode) + shared.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode shared.knownNodesLock.release() - if PORT != recaddrPort: - print 'Strange occurance: The port specified in an addr message', str(recaddrPort), 'does not match the port', str(PORT), 'that this program (or some other peer) used to connect to it', str(hostFromAddrMessage), '. Perhaps they changed their port or are using a strange NAT configuration.' if needToWriteKnownNodesToDisk: # Runs if any nodes were new to us. Also, share those nodes with our peers. shared.knownNodesLock.acquire() output = open(shared.appdata + 'knownnodes.dat', 'wb') @@ -1866,29 +1860,29 @@ class receiveDataThread(threading.Thread): if len(shared.knownNodes[self.streamNumber]) > 0: for i in range(500): random.seed() - HOST, = random.sample(shared.knownNodes[self.streamNumber], 1) - if helper_generic.isHostInPrivateIPRange(HOST): + peer, = random.sample(shared.knownNodes[self.streamNumber], 1) + if helper_generic.isHostInPrivateIPRange(peer.host): continue - addrsInMyStream[HOST] = shared.knownNodes[ - self.streamNumber][HOST] + addrsInMyStream[peer] = shared.knownNodes[ + self.streamNumber][peer] if len(shared.knownNodes[self.streamNumber * 2]) > 0: for i in range(250): random.seed() - HOST, = random.sample(shared.knownNodes[ + peer, = random.sample(shared.knownNodes[ self.streamNumber * 2], 1) - if helper_generic.isHostInPrivateIPRange(HOST): + if helper_generic.isHostInPrivateIPRange(peer.host): continue - addrsInChildStreamLeft[HOST] = shared.knownNodes[ - self.streamNumber * 2][HOST] + addrsInChildStreamLeft[peer] = shared.knownNodes[ + self.streamNumber * 2][peer] if len(shared.knownNodes[(self.streamNumber * 2) + 1]) > 0: for i in range(250): random.seed() - HOST, = random.sample(shared.knownNodes[ + peer, = random.sample(shared.knownNodes[ (self.streamNumber * 2) + 1], 1) - if helper_generic.isHostInPrivateIPRange(HOST): + if helper_generic.isHostInPrivateIPRange(peer.host): continue - addrsInChildStreamRight[HOST] = shared.knownNodes[ - (self.streamNumber * 2) + 1][HOST] + addrsInChildStreamRight[peer] = shared.knownNodes[ + (self.streamNumber * 2) + 1][peer] shared.knownNodesLock.release() numberOfAddressesInAddrMessage = 0 payload = '' @@ -1905,8 +1899,8 @@ class receiveDataThread(threading.Thread): payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ socket.inet_aton(HOST) payload += pack('>H', PORT) # remote port - for HOST, value in addrsInChildStreamLeft.items(): - PORT, timeLastReceivedMessageFromThisNode = value + for (HOST, PORT), value in addrsInChildStreamLeft.items(): + timeLastReceivedMessageFromThisNode = value if timeLastReceivedMessageFromThisNode > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers): # If it is younger than 3 hours old.. numberOfAddressesInAddrMessage += 1 payload += pack( @@ -1917,8 +1911,8 @@ class receiveDataThread(threading.Thread): payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + \ socket.inet_aton(HOST) payload += pack('>H', PORT) # remote port - for HOST, value in addrsInChildStreamRight.items(): - PORT, timeLastReceivedMessageFromThisNode = value + for (HOST, PORT), value in addrsInChildStreamRight.items(): + timeLastReceivedMessageFromThisNode = value if timeLastReceivedMessageFromThisNode > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers): # If it is younger than 3 hours old.. numberOfAddressesInAddrMessage += 1 payload += pack( diff --git a/src/class_sendDataThread.py b/src/class_sendDataThread.py index ded371a1..867d1d70 100644 --- a/src/class_sendDataThread.py +++ b/src/class_sendDataThread.py @@ -31,7 +31,7 @@ class sendDataThread(threading.Thread): streamNumber, someObjectsOfWhichThisRemoteNodeIsAlreadyAware): self.sock = sock - self.peer = shared.Peer(HOST, PORT) + self.peer = shared.Peer(HOST, PORT) self.streamNumber = streamNumber self.remoteProtocolVersion = - \ 1 # This must be set using setRemoteProtocolVersion command which is sent through the self.mailbox queue. From 7606106096d21d95b36b688f94e7b1dbdd75b104 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 1 Aug 2013 14:48:01 +0100 Subject: [PATCH 16/37] Tidying --- src/bitmessageqt/__init__.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 86d48f0f..1d115af1 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -987,11 +987,24 @@ class MyForm(QtGui.QMainWindow): # update the menu entries self.ubuntuMessagingMenuUnread(drawAttention) + # returns true if the given sound category is a connection sound + # rather than a received message sound + def isConnectionSound(self, category): + if (category is self.SOUND_CONNECTED or + category is self.SOUND_DISCONNECTED or + category is self.SOUND_CONNECTION_GREEN): + return True + return False + # play a sound def playSound(self, category, label): + # filename of the sound to be played soundFilename = None + + # whether to play a sound or not play = True + # if the address had a known label in the address book if label is not None: # Does a sound file exist for this particular contact? if (os.path.isfile(shared.appdata + 'sounds/' + label + '.wav') or @@ -1002,28 +1015,34 @@ class MyForm(QtGui.QMainWindow): # This suppresses playing sounds repeatedly when there # are many new messages if (soundFilename is None and - category is not self.SOUND_CONNECTED and - category is not self.SOUND_DISCONNECTED and - category is not self.SOUND_CONNECTION_GREEN): + not self.isConnectionSound(category)): + # elapsed time since the last sound was played dt = datetime.datetime.now() - self.lastSoundTime + # suppress sounds which are more frequent than the threshold if dt.total_seconds() < self.maxSoundFrequencySec: play = False if soundFilename is None: + # the sound is for an address which exists in the address book if category is self.SOUND_KNOWN: soundFilename = shared.appdata + 'sounds/known' + # the sound is for an unknown address elif category is self.SOUND_UNKNOWN: soundFilename = shared.appdata + 'sounds/unknown' + # initial connection sound elif category is self.SOUND_CONNECTED: soundFilename = shared.appdata + 'sounds/connected' + # disconnected sound elif category is self.SOUND_DISCONNECTED: soundFilename = shared.appdata + 'sounds/disconnected' + # sound when the connection status becomes green elif category is self.SOUND_CONNECTION_GREEN: soundFilename = shared.appdata + 'sounds/green' if soundFilename is not None and play is True: - # record the last time that a sound was played - self.lastSoundTime = datetime.datetime.now() + if not self.isConnectionSound(category): + # record the last time that a received message sound was played + self.lastSoundTime = datetime.datetime.now() # if not wav then try mp3 format if not os.path.isfile(soundFilename + '.wav'): From 5c3bc63a1f7e1d6c0ce9dc725eff9b3b8974adde Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 1 Aug 2013 12:16:31 -0400 Subject: [PATCH 17/37] Only allow 1 connection per IP --- src/bitmessagemain.py | 4 ++-- src/class_outgoingSynSender.py | 2 +- src/class_receiveDataThread.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 16a11c6f..f2ffed92 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -325,7 +325,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': ')) data += ']}' return data - elif method == 'getAllInboxMessageIds': + elif method == 'getAllInboxMessageIds' or method == 'getAllInboxMessageIDs': shared.sqlLock.acquire() shared.sqlSubmitQueue.put( '''SELECT msgid FROM inbox where folder='inbox' ORDER BY received''') @@ -374,7 +374,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) data += ']}' return data - elif method == 'getAllSentMessageIds': + elif method == 'getAllSentMessageIds' or method == 'getAllSentMessageIDs': shared.sqlLock.acquire() shared.sqlSubmitQueue.put('''SELECT msgid FROM sent where folder='sent' ORDER BY lastactiontime''') shared.sqlSubmitQueue.put('') diff --git a/src/class_outgoingSynSender.py b/src/class_outgoingSynSender.py index aa9cadf1..8a929c7d 100644 --- a/src/class_outgoingSynSender.py +++ b/src/class_outgoingSynSender.py @@ -35,7 +35,7 @@ class outgoingSynSender(threading.Thread): peer, = random.sample(shared.knownNodes[self.streamNumber], 1) shared.knownNodesLock.release() shared.alreadyAttemptedConnectionsListLock.acquire() - while peer in shared.alreadyAttemptedConnectionsList or peer in shared.connectedHostsList: + while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList: shared.alreadyAttemptedConnectionsListLock.release() # print 'choosing new sample' random.seed() diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 5af33157..6adfe490 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -48,7 +48,7 @@ class receiveDataThread(threading.Thread): self.objectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHave = {} self.selfInitiatedConnections = selfInitiatedConnections shared.connectedHostsList[ - self.peer] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it. + self.peer.host] = 0 # The very fact that this receiveData thread exists shows that we are connected to the remote host. Let's add it to this list so that an outgoingSynSender thread doesn't try to connect to it. self.connectionIsOrWasFullyEstablished = False # set to true after the remote node and I accept each other's version messages. This is needed to allow the user interface to accurately reflect the current number of connections. if self.streamNumber == -1: # This was an incoming connection. Send out a version message if we accept the other node's version message. self.initiatedConnection = False @@ -94,10 +94,10 @@ class receiveDataThread(threading.Thread): pass shared.broadcastToSendDataQueues((0, 'shutdown', self.peer)) try: - del shared.connectedHostsList[self.peer] + del shared.connectedHostsList[self.peer.host] except Exception as err: with shared.printLock: - print 'Could not delete', self.peer, 'from shared.connectedHostsList.', err + print 'Could not delete', self.peer.host, 'from shared.connectedHostsList.', err try: del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[ @@ -1979,7 +1979,7 @@ class receiveDataThread(threading.Thread): return shared.connectedHostsList[ - self.peer] = 1 # We use this data structure to not only keep track of what hosts we are connected to so that we don't try to connect to them again, but also to list the connections count on the Network Status tab. + self.peer.host] = 1 # We use this data structure to not only keep track of what hosts we are connected to so that we don't try to connect to them again, but also to list the connections count on the Network Status tab. # If this was an incoming connection, then the sendData thread # doesn't know the stream. We have to set it. if not self.initiatedConnection: From 6a44ded7fc561471e15274e5f7b99bace93796cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag=20Rob=C3=B8le?= Date: Thu, 1 Aug 2013 19:01:07 +0200 Subject: [PATCH 18/37] Fixed a problem with sticky bold fonts --- src/bitmessageqt/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1d115af1..71da6c68 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2820,6 +2820,11 @@ class MyForm(QtGui.QMainWindow): def tableWidgetInboxItemClicked(self): currentRow = self.ui.tableWidgetInbox.currentRow() if currentRow >= 0: + + font = QFont() + font.setBold(False) + self.ui.textEditInboxMessage.setCurrentFont(font) + fromAddress = str(self.ui.tableWidgetInbox.item( currentRow, 1).data(Qt.UserRole).toPyObject()) # If we have received this message from either a broadcast address @@ -2838,9 +2843,7 @@ class MyForm(QtGui.QMainWindow): else: self.ui.textEditInboxMessage.setPlainText(self.ui.tableWidgetInbox.item(currentRow, 2).data(Qt.UserRole).toPyObject()[ :30000] + '\n\nDisplay of the remainder of the message truncated because it is too long.') # Only show the first 30K characters - - font = QFont() - font.setBold(False) + self.ui.tableWidgetInbox.item(currentRow, 0).setFont(font) self.ui.tableWidgetInbox.item(currentRow, 1).setFont(font) self.ui.tableWidgetInbox.item(currentRow, 2).setFont(font) From 450f6d7509b62a722d6f19d163702a605f7c61d8 Mon Sep 17 00:00:00 2001 From: Cameron Conn Date: Thu, 1 Aug 2013 14:08:45 -0500 Subject: [PATCH 19/37] Added initial documentation for installation --- INSTALL | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 INSTALL diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..1cb06c5b --- /dev/null +++ b/INSTALL @@ -0,0 +1,101 @@ +#PyBitmessage Installation Instructions + +For an up-to-date version of these instructions, please visit the +[Bitmessage Wiki](https://bitmessage.org/wiki/Compiling_instructions). + +PyBitmessage can be run either straight from source or from an installed +packaged. + +##Dependencies +Before running PyBitmessage, make sure you have all the needed dependencies +installed on your system. + +Here's a list of dependencies needed for PyBitmessage +- python2.7 +- python2-qt4 (python-qt4 on Debian/Ubuntu) +- openssl +- (Fedora & Redhat only) openssl-compat-bitcoin-libs + +##Running PyBitmessage +PyBitmessage can be run two ways: straight from source or via a package which +is installed on your system. Since PyBitmessage is Beta, it is best to run +PyBitmessage from source, so that you may update as needed. + +####Updating +To update PyBitmessage from source (Linux/OS X), you can do these easy steps: +``` +cd PyBitmessage/src/ +git fetch --all +git reset --hard origin/master +python bitmessagemain.py +``` +Viola! Bitmessage is updated! + +####Linux +To run PyBitmessage from the command-line, you must download the source, then +run `src/bitmessagemain.py`. +``` +git clone git://github.com/Bitmessage/PyBitmessage.git +cd PyBitmessage/ && python src/bitmessagemain.py +``` + +That's it! *Honestly*! + +####Windows +In Windows you can download an executable for Bitmessage +[here](https://bitmessage.org/download/windows/Bitmessage.exe). + +However, if you would like to run PyBitmessage via Python in Windows, you can +go [here](https://bitmessage.org/wiki/Compiling_instructions#Windows) for +information on how to do so. + +####OS X +First off, install Homebrew. +``` +ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)" +``` + +Now, install the required dependencies +``` +sudo port install python27 py27-pyqt4 openssl +sudo port install git-core +svn +doc +bash_completion +gitweb +``` + +Download and run PyBitmessage: +``` +git clone git://github.com/Bitmessage/PyBitmessage.git +cd PyBitmessage && python src/bitmessagemain.py +``` + +##Creating a package for installation +If you really want, you can make a package for PyBitmessage which you may +install yourself or distribute to friends. This isn't reccomended, since +PyBitmessage is in Beta, and subject to frequent change. + +####Linux + +First off, since PyBitmessage uses something nifty called +[packagemonkey](https://github.com/fuzzgun/packagemonkey), go ahead and get +that installed. You may have to build it from source. + +Next, edit the generate.sh script. To your liking. + +Now, run the appropriate script for the type of package you'd like to make +``` +arch.sh - create a package for Arch Linux +debian.sh - create a package for Debian/Ubuntu +ebuild.sh - create a package for Gentoo +osx.sh - create a package for OS X +puppy.sh - create a package for Puppy Linux +rpm.sh - create a RPM package +slack.sh - create a package for Slackware +``` + +####OS X +Please refer to +[this page](https://bitmessage.org/forum/index.php/topic,2761.0.html) on the +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 From 5148ade238c6fdcae59db705ef5f03e10ebf55f3 Mon Sep 17 00:00:00 2001 From: Cameron Conn Date: Thu, 1 Aug 2013 14:22:53 -0500 Subject: [PATCH 20/37] Changed INSTALL to markdown --- INSTALL => INSTALL.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename INSTALL => INSTALL.md (100%) diff --git a/INSTALL b/INSTALL.md similarity index 100% rename from INSTALL rename to INSTALL.md From 03200d3bb15edaa209f0f4a8ac008c7cce07c87c Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 1 Aug 2013 18:39:45 -0400 Subject: [PATCH 21/37] Small changes to API (backwards compatible) --- src/bitmessagemain.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index f2ffed92..b156cd0d 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -388,7 +388,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'msgid':msgid.encode('hex')}, indent=4, separators=(',', ': ')) data += ']}' return data - elif method == 'getInboxMessagesByAddress': + elif method == 'getInboxMessagesByReceiver' or method == 'getInboxMessagesByAddress': #after some time getInboxMessagesByAddress should be removed if len(params) == 0: return 'API Error 0000: I need parameters!' toAddress = params[0] @@ -464,7 +464,24 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': ')) data += ']}' return data - elif (method == 'trashMessage') or (method == 'trashInboxMessage'): + elif method == 'trashMessage': + if len(params) == 0: + return 'API Error 0000: I need parameters!' + msgid = params[0].decode('hex') + + # Trash if in inbox table + helper_inbox.trash(msgid) + # Trash if in sent table + t = (msgid,) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put('''UPDATE sent SET folder='trash' WHERE msgid=?''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + shared.sqlSubmitQueue.put('commit') + shared.sqlLock.release() + # shared.UISignalQueue.put(('removeSentRowByMsgid',msgid)) This function doesn't exist yet. + return 'Trashed message (assuming message existed).' + elif method == 'trashInboxMessage': if len(params) == 0: return 'API Error 0000: I need parameters!' msgid = params[0].decode('hex') From f322696e20ab05221de46a7f11953814807048e2 Mon Sep 17 00:00:00 2001 From: akh81 Date: Thu, 1 Aug 2013 18:21:10 -0500 Subject: [PATCH 22/37] fixed the bug with getting the wrong locale --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1d115af1..4a08f5b2 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3286,7 +3286,7 @@ def run(): translator = QtCore.QTranslator() try: - translator.load("translations/bitmessage_" + str(locale.getlocale()[0])) + translator.load("translations/bitmessage_" + str(locale.getdefaultlocale()[0])) except: # The above is not compatible with all versions of OSX. translator.load("translations/bitmessage_en_US") # Default to english. From 17533237fe9420579e5a87d025ffa093a5de9478 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 2 Aug 2013 18:35:31 -0400 Subject: [PATCH 23/37] some initial work done to support particular android client --- src/bitmessagemain.py | 67 ++++++++++++++++++++++++++++++++++++ src/bitmessageqt/__init__.py | 1 + src/class_sqlThread.py | 10 +++++- src/message_data_reader.py | 41 +++++++++++++++------- 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b156cd0d..b63cc2b3 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -738,6 +738,73 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'label':label.encode('base64'), 'address': address, 'enabled': enabled == 1}, indent=4, separators=(',',': ')) data += ']}' return data + elif method == 'disseminatePreEncryptedMsg': + # The device issuing this command to PyBitmessage supplies a msg object that has + # already been encrypted and had the necessary proof of work done for it to be + # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this msg + # object and sends it out to the rest of the Bitmessage network as if it had generated the + # message itself. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + encryptedPayload, = params + inventoryHash = calculateInventoryHash(encryptedPayload) + objectType = 'msg' + shared.inventory[inventoryHash] = ( + objectType, toStreamNumber, encryptedPayload, int(time.time())) + with shared.printLock: + print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + streamNumber, 'sendinv', inventoryHash)) + elif method == 'disseminatePubkey': + # The device issuing this command to PyBitmessage supplies a pubkey object that has + # already had the necessary proof of work done for it to be disseminated to the rest of the + # Bitmessage network. PyBitmessage accepts this pubkey object and sends it out to the + # rest of the Bitmessage network, as if it had generated the pubkey object itself. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + payload, = params + inventoryHash = calculateInventoryHash(payload) + objectType = 'pubkey' + shared.inventory[inventoryHash] = ( + objectType, streamNumber, payload, int(time.time())) + with shared.printLock: + print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') + shared.broadcastToSendDataQueues(( + streamNumber, 'sendinv', inventoryHash)) + elif method == 'getMessageDataByDestinationRIPEHash': + # Method will eventually be used by a particular Android app. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + hash, = params + #if len(hash) != 40: + # return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' + print repr(hash) + hash = hash.decode('hex') + print repr(hash) + with shared.sqlLock: + shared.sqlSubmitQueue.put('''PRAGMA case_sensitive_like = true''') + shared.sqlSubmitQueue.put('') + queryreturn = shared.sqlReturnQueue.get() + + hash = string.replace(hash,'e','ee') + hash = string.replace(hash,'%','e%') + hash = string.replace(hash,'_','e_') + print 'searching for hash:', repr(hash) + parameters = ('%'+ hash + '%',) + with shared.sqlLock: + shared.sqlSubmitQueue.put('''SELECT payload FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''') + shared.sqlSubmitQueue.put(parameters) + queryreturn = shared.sqlReturnQueue.get() + + data = '{"receivedMessageDatas":[' + for row in queryreturn: + payload, = row + if len(data) > 25: + data += ',' + data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data + elif method == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5dd4c0b5..a9a7179a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3290,6 +3290,7 @@ def run(): try: translator.load("translations/bitmessage_" + str(locale.getdefaultlocale()[0])) + #translator.load("translations/bitmessage_fr_BE") # test French except: # The above is not compatible with all versions of OSX. translator.load("translations/bitmessage_en_US") # Default to english. diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index e07850e4..48be5e0d 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -189,7 +189,15 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'sockslisten'): shared.config.set('bitmessagesettings', 'sockslisten', 'false') - + + # Some prewritten code for future use whenever we need to modify the database + """item = '''SELECT value FROM settings WHERE key='version';''' + parameters = '' + self.cur.execute(item, parameters) + if self.cur.fetchall()[0][0] == 1: + do something + increment the version to 2""" + try: testpayload = '\x00\x00' t = ('1234', testpayload, '12345678', 'no') diff --git a/src/message_data_reader.py b/src/message_data_reader.py index c600935d..f6102e02 100644 --- a/src/message_data_reader.py +++ b/src/message_data_reader.py @@ -5,19 +5,10 @@ import sqlite3 from time import strftime, localtime import sys +import shared +import string -APPNAME = "PyBitmessage" -from os import path, environ -if sys.platform == 'darwin': - if "HOME" in environ: - appdata = path.join(environ["HOME"], "Library/Application support/", APPNAME) + '/' - else: - print 'Could not find home folder, please report this message and your OS X version to the BitMessage Github.' - sys.exit() -elif 'win' in sys.platform: - appdata = path.join(environ['APPDATA'], APPNAME) + '\\' -else: - appdata = path.expanduser(path.join("~", "." + APPNAME + "/")) +appdata = shared.lookupAppdataFolder() conn = sqlite3.connect( appdata + 'messages.dat' ) conn.text_factory = str @@ -71,6 +62,29 @@ def readInventory(): hash, objecttype, streamnumber, payload, receivedtime = row print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') +def readInventory2(): + searchValue = ' ' + + item = '''PRAGMA case_sensitive_like = true ''' + parameters = '' + cur.execute(item, parameters) + + searchValue = string.replace(searchValue,'e','ee') + searchValue = string.replace(searchValue,'%','e%') + searchValue = string.replace(searchValue,'_','e_') + + print 'Printing subset of inventory table:' + item = '''SELECT * FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''' + parameters = ('%'+ searchValue + '%',) + print repr(parameters), len(parameters[0]) + cur.execute(item, parameters) + output = cur.fetchall() + print 'Number of results:', len(output) + for row in output[:20]: + hash, objecttype, streamnumber, payload, receivedtime = row + print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') + print 'done' + def takeInboxMessagesOutOfTrash(): item = '''update inbox set folder='inbox' where folder='trash' ''' @@ -107,12 +121,13 @@ def vacuum(): #takeInboxMessagesOutOfTrash() #takeSentMessagesOutOfTrash() #markAllInboxMessagesAsUnread() -readInbox() +#readInbox() #readSent() #readPubkeys() #readSubscriptions() #readInventory() #vacuum() #will defragment and clean empty space from the messages.dat file. +readInventory2() From 9460712a591118bc1bb923f3ed7f6c251898cc4c Mon Sep 17 00:00:00 2001 From: Gregor Robinson Date: Mon, 5 Aug 2013 22:06:46 +0200 Subject: [PATCH 24/37] File permission special case for NTFS-3g on POSIX. Fix issue #347, "*SensitiveFilePermissions fails on ntfs-3g mounted filesystems". --- src/shared.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/shared.py b/src/shared.py index 214c124f..9b8c9325 100644 --- a/src/shared.py +++ b/src/shared.py @@ -346,6 +346,19 @@ def checkSensitiveFilePermissions(filename): # Windows systems. return True else: + try: + # Skip known problems for non-Win32 filesystems without POSIX permissions. + import subprocess + fstype = subprocess.check_output('stat -f -c "%%T" %s' % (filename), + shell=True, + stderr=subprocess.STDOUT) + if 'fuseblk' in fstype: + logger.info('Skipping file permissions check for %s. Filesystem fuseblk detected.', + filename) + return True + except: + # Swallow exception here, but we might run into trouble later! + logger.error('Could not determine filesystem type.', filename) present_permissions = os.stat(filename)[0] disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO return present_permissions & disallowed_permissions == 0 From c5442029b5515750f2bd56210c825d6cfd6e9534 Mon Sep 17 00:00:00 2001 From: merlink Date: Mon, 5 Aug 2013 22:29:06 +0200 Subject: [PATCH 25/37] Changed start code for deamon mode --- src/bitmessagemain.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b156cd0d..7a2047be 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -770,7 +770,7 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes / 7000) -if __name__ == "__main__": +def main(): # is the application already running? If yes then exit. thisapp = singleton.singleinstance() @@ -840,7 +840,9 @@ if __name__ == "__main__": while True: time.sleep(20) - +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 From 86383f0a9fcea4b6403020617e9c620836224f7f Mon Sep 17 00:00:00 2001 From: merlink Date: Tue, 6 Aug 2013 10:37:31 +0200 Subject: [PATCH 26/37] Added deamon modoe to main function --- src/bitmessagemain.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 7a2047be..04ff3071 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -770,7 +770,7 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes / 7000) -def main(): +def main(deamon=False): # is the application already running? If yes then exit. thisapp = singleton.singleinstance() @@ -823,7 +823,7 @@ def main(): singleListenerThread.daemon = True # close the main program even if there are threads left singleListenerThread.start() - if not shared.safeConfigGetBoolean('bitmessagesettings', 'daemon'): + if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: try: from PyQt4 import QtCore, QtGui except Exception as err: @@ -840,6 +840,7 @@ def main(): while True: time.sleep(20) + if __name__ == "__main__": main() From 084f67b10f83a38318effbfa4823c34f2c1b2fe2 Mon Sep 17 00:00:00 2001 From: merlink Date: Tue, 6 Aug 2013 13:23:56 +0200 Subject: [PATCH 27/37] Created Object for controlling bitmessage deamon --- src/bitmessagemain.py | 139 ++++++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 59 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 04ff3071..b39bdc96 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -770,79 +770,100 @@ if shared.useVeryEasyProofOfWorkForTesting: shared.networkDefaultPayloadLengthExtraBytes = int( shared.networkDefaultPayloadLengthExtraBytes / 7000) -def main(deamon=False): - # is the application already running? If yes then exit. - thisapp = singleton.singleinstance() +class Main: + def start(self, deamon=False): + # is the application already running? If yes then exit. + thisapp = singleton.singleinstance() - signal.signal(signal.SIGINT, helper_generic.signal_handler) - # signal.signal(signal.SIGINT, signal.SIG_DFL) + signal.signal(signal.SIGINT, helper_generic.signal_handler) + # signal.signal(signal.SIGINT, signal.SIG_DFL) - helper_bootstrap.knownNodes() - # Start the address generation thread - addressGeneratorThread = addressGenerator() - addressGeneratorThread.daemon = True # close the main program even if there are threads left - addressGeneratorThread.start() + helper_bootstrap.knownNodes() + # Start the address generation thread + addressGeneratorThread = addressGenerator() + addressGeneratorThread.daemon = True # close the main program even if there are threads left + addressGeneratorThread.start() - # Start the thread that calculates POWs - singleWorkerThread = singleWorker() - singleWorkerThread.daemon = True # close the main program even if there are threads left - singleWorkerThread.start() + # Start the thread that calculates POWs + singleWorkerThread = singleWorker() + singleWorkerThread.daemon = True # close the main program even if there are threads left + singleWorkerThread.start() - # Start the SQL thread - sqlLookup = sqlThread() - sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. - sqlLookup.start() + # Start the SQL thread + sqlLookup = sqlThread() + sqlLookup.daemon = False # DON'T close the main program even if there are threads left. The closeEvent should command this thread to exit gracefully. + sqlLookup.start() - # Start the cleanerThread - singleCleanerThread = singleCleaner() - singleCleanerThread.daemon = True # close the main program even if there are threads left - singleCleanerThread.start() + # Start the cleanerThread + singleCleanerThread = singleCleaner() + singleCleanerThread.daemon = True # close the main program even if there are threads left + singleCleanerThread.start() - shared.reloadMyAddressHashes() - shared.reloadBroadcastSendersForWhichImWatching() + shared.reloadMyAddressHashes() + shared.reloadBroadcastSendersForWhichImWatching() - if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): - try: - apiNotifyPath = shared.config.get( - 'bitmessagesettings', 'apinotifypath') - except: - apiNotifyPath = '' - if apiNotifyPath != '': - with shared.printLock: - print 'Trying to call', apiNotifyPath + if shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + try: + apiNotifyPath = shared.config.get( + 'bitmessagesettings', 'apinotifypath') + except: + apiNotifyPath = '' + if apiNotifyPath != '': + with shared.printLock: + print 'Trying to call', apiNotifyPath - call([apiNotifyPath, "startingUp"]) - singleAPIThread = singleAPI() - singleAPIThread.daemon = True # close the main program even if there are threads left - singleAPIThread.start() + call([apiNotifyPath, "startingUp"]) + singleAPIThread = singleAPI() + singleAPIThread.daemon = True # close the main program even if there are threads left + singleAPIThread.start() - connectToStream(1) + connectToStream(1) - singleListenerThread = singleListener() - singleListenerThread.setup(selfInitiatedConnections) - singleListenerThread.daemon = True # close the main program even if there are threads left - singleListenerThread.start() + singleListenerThread = singleListener() + singleListenerThread.setup(selfInitiatedConnections) + singleListenerThread.daemon = True # close the main program even if there are threads left + singleListenerThread.start() - if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: - try: - from PyQt4 import QtCore, QtGui - except Exception as err: - print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon' - print 'Error message:', err - os._exit(0) + if deamon == False and shared.safeConfigGetBoolean('bitmessagesettings', 'daemon') == False: + try: + from PyQt4 import QtCore, QtGui + except Exception as err: + print 'PyBitmessage requires PyQt unless you want to run it as a daemon and interact with it using the API. You can download PyQt from http://www.riverbankcomputing.com/software/pyqt/download or by searching Google for \'PyQt Download\'. If you want to run in daemon mode, see https://bitmessage.org/wiki/Daemon' + print 'Error message:', err + os._exit(0) - import bitmessageqt - bitmessageqt.run() - else: - shared.config.remove_option('bitmessagesettings', 'dontconnect') + import bitmessageqt + bitmessageqt.run() + else: + shared.config.remove_option('bitmessagesettings', 'dontconnect') + + if deamon: + with shared.printLock: + print 'Running as a daemon. The main program should exit this thread.' + else: + with shared.printLock: + print 'Running as a daemon. You can use Ctrl+C to exit.' + while True: + time.sleep(20) + + def stop(self): with shared.printLock: - print 'Running as a daemon. You can use Ctrl+C to exit.' - - while True: - time.sleep(20) - + print 'Stopping Bitmessage Deamon.' + shared.doCleanShutdown() + + + def getApiAddress(self): + if not shared.safeConfigGetBoolean('bitmessagesettings', 'apienabled'): + return None + + address = shared.config.get('bitmessagesettings', 'apiinterface') + port = shared.config.getint('bitmessagesettings', 'apiport') + return {'address':address,'port':port} + if __name__ == "__main__": - main() + mainprogram = Main() + mainprogram.start() + # So far, the creation of and management of the Bitmessage protocol and this # client is a one-man operation. Bitcoin tips are quite appreciated. From d51fe37a66fbce2d658a7725499bf4f214c5647d Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Tue, 6 Aug 2013 13:19:26 -0400 Subject: [PATCH 28/37] added requested API commands for mobile device --- src/bitmessagemain.py | 55 ++++++++++++++++++++++---------------- src/class_singleCleaner.py | 4 +-- src/class_sqlThread.py | 19 ++++++++----- src/message_data_reader.py | 28 ++++++++++--------- src/shared.py | 7 +++-- 5 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index b63cc2b3..82bcd3de 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -747,6 +747,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(params) != 1: return 'API Error 0000: I need 1 parameter!' encryptedPayload, = params + encryptedPayload = encryptedPayload.decode('hex') inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' shared.inventory[inventoryHash] = ( @@ -754,15 +755,16 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): with shared.printLock: print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( - streamNumber, 'sendinv', inventoryHash)) + toStreamNumber, 'sendinv', inventoryHash)) elif method == 'disseminatePubkey': # The device issuing this command to PyBitmessage supplies a pubkey object that has # already had the necessary proof of work done for it to be disseminated to the rest of the # Bitmessage network. PyBitmessage accepts this pubkey object and sends it out to the - # rest of the Bitmessage network, as if it had generated the pubkey object itself. + # rest of the Bitmessage network as if it had generated the pubkey object itself. if len(params) != 1: return 'API Error 0000: I need 1 parameter!' payload, = params + payload = payload.decode('hex') inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' shared.inventory[inventoryHash] = ( @@ -772,30 +774,40 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.broadcastToSendDataQueues(( streamNumber, 'sendinv', inventoryHash)) elif method == 'getMessageDataByDestinationRIPEHash': - # Method will eventually be used by a particular Android app. + # Method will eventually be used by a particular Android app to + # select relevant messages. + if len(params) != 1: return 'API Error 0000: I need 1 parameter!' - hash, = params - #if len(hash) != 40: - # return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' - print repr(hash) - hash = hash.decode('hex') - print repr(hash) - with shared.sqlLock: - shared.sqlSubmitQueue.put('''PRAGMA case_sensitive_like = true''') - shared.sqlSubmitQueue.put('') - queryreturn = shared.sqlReturnQueue.get() + requestedHash, = params + if len(requestedHash) != 40: + return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' + requestedHash = requestedHash.decode('hex') - hash = string.replace(hash,'e','ee') - hash = string.replace(hash,'%','e%') - hash = string.replace(hash,'_','e_') - print 'searching for hash:', repr(hash) - parameters = ('%'+ hash + '%',) + # This is not a particularly commonly used API function. Before we + # use it we'll need to fill out a field in our inventory database + # which is blank by default (first20bytesofencryptedmessage). + parameters = '' with shared.sqlLock: - shared.sqlSubmitQueue.put('''SELECT payload FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''') + shared.sqlSubmitQueue.put('''SELECT hash, payload FROM inventory WHERE first20bytesofencryptedmessage = '' and objecttype = 'msg' ; ''') shared.sqlSubmitQueue.put(parameters) queryreturn = shared.sqlReturnQueue.get() + for row in queryreturn: + hash, payload = row + readPosition = 16 # Nonce length + time length + readPosition += decodeVarint(payload[readPosition:readPosition+10])[1] # Stream Number length + t = (payload[readPosition:readPosition+20],hash) + shared.sqlSubmitQueue.put('''UPDATE inventory SET first20bytesofencryptedmessage=? WHERE hash=?; ''') + shared.sqlSubmitQueue.put(t) + shared.sqlReturnQueue.get() + + parameters = (requestedHash,) + with shared.sqlLock: + shared.sqlSubmitQueue.put('commit') + shared.sqlSubmitQueue.put('''SELECT payload FROM inventory WHERE first20bytesofencryptedmessage = ?''') + shared.sqlSubmitQueue.put(parameters) + queryreturn = shared.sqlReturnQueue.get() data = '{"receivedMessageDatas":[' for row in queryreturn: payload, = row @@ -824,11 +836,8 @@ class singleAPI(threading.Thread): se.register_introspection_functions() se.serve_forever() +# This is a list of current connections (the thread pointers at least) selfInitiatedConnections = {} - # This is a list of current connections (the thread pointers at least) - - - if shared.useVeryEasyProofOfWorkForTesting: diff --git a/src/class_singleCleaner.py b/src/class_singleCleaner.py index 6fed68a5..d92a37ec 100644 --- a/src/class_singleCleaner.py +++ b/src/class_singleCleaner.py @@ -33,9 +33,9 @@ class singleCleaner(threading.Thread): for hash, storedValue in shared.inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue if int(time.time()) - 3600 > receivedTime: - t = (hash, objectType, streamNumber, payload, receivedTime) + t = (hash, objectType, streamNumber, payload, receivedTime,'') shared.sqlSubmitQueue.put( - '''INSERT INTO inventory VALUES (?,?,?,?,?)''') + '''INSERT INTO inventory VALUES (?,?,?,?,?,?)''') shared.sqlSubmitQueue.put(t) shared.sqlReturnQueue.get() del shared.inventory[hash] diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 48be5e0d..625b2b2f 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -46,7 +46,7 @@ class sqlThread(threading.Thread): self.cur.execute( '''CREATE TABLE pubkeys (hash blob, transmitdata blob, time int, usedpersonally text, UNIQUE(hash) ON CONFLICT REPLACE)''' ) self.cur.execute( - '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, UNIQUE(hash) ON CONFLICT REPLACE)''' ) + '''CREATE TABLE inventory (hash blob, objecttype text, streamnumber int, payload blob, receivedtime integer, first20bytesofencryptedmessage blob, UNIQUE(hash) ON CONFLICT REPLACE)''' ) self.cur.execute( '''CREATE TABLE knownnodes (timelastseen int, stream int, services blob, host blob, port blob, UNIQUE(host, stream, port) ON CONFLICT REPLACE)''' ) # This table isn't used in the program yet but I @@ -55,7 +55,7 @@ class sqlThread(threading.Thread): '''INSERT INTO subscriptions VALUES('Bitmessage new releases/announcements','BM-GtovgYdgs7qXPkoYaRgrLFuFKz1SFpsw',1)''') self.cur.execute( '''CREATE TABLE settings (key blob, value blob, UNIQUE(key) ON CONFLICT REPLACE)''' ) - self.cur.execute( '''INSERT INTO settings VALUES('version','1')''') + self.cur.execute( '''INSERT INTO settings VALUES('version','2')''') self.cur.execute( '''INSERT INTO settings VALUES('lastvacuumtime',?)''', ( int(time.time()),)) self.conn.commit() @@ -190,13 +190,18 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'sockslisten'): shared.config.set('bitmessagesettings', 'sockslisten', 'false') - # Some prewritten code for future use whenever we need to modify the database - """item = '''SELECT value FROM settings WHERE key='version';''' + # Add a new column to the inventory table to store the first 20 bytes of encrypted messages to support Android app + item = '''SELECT value FROM settings WHERE key='version';''' parameters = '' self.cur.execute(item, parameters) - if self.cur.fetchall()[0][0] == 1: - do something - increment the version to 2""" + if int(self.cur.fetchall()[0][0]) == 1: + print 'upgrading database' + item = '''ALTER TABLE inventory ADD first20bytesofencryptedmessage blob DEFAULT '' ''' + parameters = '' + self.cur.execute(item, parameters) + item = '''update settings set value=? WHERE key='version';''' + parameters = (2,) + self.cur.execute(item, parameters) try: testpayload = '\x00\x00' diff --git a/src/message_data_reader.py b/src/message_data_reader.py index f6102e02..35b2441a 100644 --- a/src/message_data_reader.py +++ b/src/message_data_reader.py @@ -54,16 +54,16 @@ def readPubkeys(): def readInventory(): print 'Printing everything in inventory table:' - item = '''select hash, objecttype, streamnumber, payload, receivedtime from inventory''' + item = '''select hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage from inventory where objecttype = 'msg' ''' parameters = '' cur.execute(item, parameters) output = cur.fetchall() - for row in output: - hash, objecttype, streamnumber, payload, receivedtime = row - print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') + for row in output[:50]: + hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage = row + print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', 'first20bytesofencryptedmessage:', first20bytesofencryptedmessage.encode('hex'), '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') def readInventory2(): - searchValue = ' ' + searchValue = ' ' item = '''PRAGMA case_sensitive_like = true ''' parameters = '' @@ -74,15 +74,17 @@ def readInventory2(): searchValue = string.replace(searchValue,'_','e_') print 'Printing subset of inventory table:' - item = '''SELECT * FROM inventory WHERE hash LIKE ? ESCAPE'e'; ''' - parameters = ('%'+ searchValue + '%',) - print repr(parameters), len(parameters[0]) + item = '''SELECT substr(payload,20) FROM inventory''' + #parameters = ('%'+ searchValue + '%',) + #print repr(parameters), len(parameters[0]) + parameters = '' cur.execute(item, parameters) output = cur.fetchall() print 'Number of results:', len(output) - for row in output[:20]: - hash, objecttype, streamnumber, payload, receivedtime = row - print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') + for row in output[:100]: + print row + #hash, objecttype, streamnumber, payload, receivedtime = row + #print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') print 'done' @@ -125,9 +127,9 @@ def vacuum(): #readSent() #readPubkeys() #readSubscriptions() -#readInventory() +readInventory() #vacuum() #will defragment and clean empty space from the messages.dat file. -readInventory2() +#readInventory2() diff --git a/src/shared.py b/src/shared.py index 214c124f..925459f7 100644 --- a/src/shared.py +++ b/src/shared.py @@ -243,8 +243,7 @@ def reloadMyAddressHashes(): myAddressesByHash[hash] = addressInKeysFile else: - logger.error('Error in reloadMyAddressHashes: Can\'t handle address ' - 'versions other than 2 or 3.\n') + logger.error('Error in reloadMyAddressHashes: Can\'t handle address versions other than 2 or 3.\n') if not keyfileSecure: fixSensitiveFilePermissions(appdata + 'keys.dat', hasEnabledKeys) @@ -320,8 +319,8 @@ def flushInventory(): sqlLock.acquire() for hash, storedValue in inventory.items(): objectType, streamNumber, payload, receivedTime = storedValue - t = (hash,objectType,streamNumber,payload,receivedTime) - sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?)''') + t = (hash,objectType,streamNumber,payload,receivedTime,'') + sqlSubmitQueue.put('''INSERT INTO inventory VALUES (?,?,?,?,?,?)''') sqlSubmitQueue.put(t) sqlReturnQueue.get() del inventory[hash] From ecef8f93b5cfe8b92bf075131d40aca4e681a568 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Wed, 7 Aug 2013 15:15:49 -0400 Subject: [PATCH 29/37] minor code refactoring --- src/shared.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/shared.py b/src/shared.py index 925459f7..09a2c26c 100644 --- a/src/shared.py +++ b/src/shared.py @@ -101,10 +101,8 @@ def assembleVersionMessage(remoteHost, remotePort, myStreamNumber): random.seed() payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf - userAgent = '/PyBitmessage:' + shared.softwareVersion + \ - '/' # Length of userAgent must be less than 253. - payload += pack('>B', len( - userAgent)) # user agent string length. If the user agent is more than 252 bytes long, this code isn't going to work. + userAgent = '/PyBitmessage:' + shared.softwareVersion + '/' + payload += encodeVarint(len(userAgent)) payload += userAgent payload += encodeVarint( 1) # The number of streams about which I care. PyBitmessage currently only supports 1 per connection. From 854b7394311172afbb67405d50abeb703aaea234 Mon Sep 17 00:00:00 2001 From: Lucretiel Date: Thu, 8 Aug 2013 03:52:47 -0400 Subject: [PATCH 30/37] The createDefaultKnownNodes function now uses a with statement --- src/defaultKnownNodes.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index 52e31fff..d4611099 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -37,12 +37,10 @@ def createDefaultKnownNodes(appdata): #print stream1 #print allKnownNodes - output = open(appdata + 'knownnodes.dat', 'wb') + with open(appdata + 'knownnodes.dat', 'wb) as output: + # Pickle dictionary using protocol 0. + pickle.dump(allKnownNodes, output) - # Pickle dictionary using protocol 0. - pickle.dump(allKnownNodes, output) - - output.close() return allKnownNodes def readDefaultKnownNodes(appdata): From cdec4ad506cda3fe2d832ccda98bbb36806ccb7a Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 8 Aug 2013 15:37:48 -0400 Subject: [PATCH 31/37] Added option in settings menu to allow sending to mobile --- src/bitmessageqt/__init__.py | 4 ++ src/bitmessageqt/newchandialog.py | 13 ++++-- src/bitmessageqt/newchandialog.ui | 12 ++++- src/bitmessageqt/settings.py | 60 +++++++++++++------------ src/bitmessageqt/settings.ui | 75 +++++++++++++++++-------------- src/class_singleWorker.py | 14 ++++++ src/shared.py | 14 ++++-- 7 files changed, 120 insertions(+), 72 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index a9a7179a..396d0b6e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2000,6 +2000,8 @@ class MyForm(QtGui.QMainWindow): self.settingsDialogInstance.ui.checkBoxShowTrayNotifications.isChecked())) shared.config.set('bitmessagesettings', 'startintray', str( self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked())) + shared.config.set('bitmessagesettings', 'willinglysendtomobile', str( + self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked())) if int(shared.config.get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()): if not shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( @@ -2996,6 +2998,8 @@ class settingsDialog(QtGui.QDialog): shared.config.getboolean('bitmessagesettings', 'showtraynotifications')) self.ui.checkBoxStartInTray.setChecked( shared.config.getboolean('bitmessagesettings', 'startintray')) + self.ui.checkBoxWillinglySendToMobile.setChecked( + shared.safeConfigGetBoolean('bitmessagesettings', 'willinglysendtomobile')) if shared.appdata == '': self.ui.checkBoxPortableMode.setChecked(True) if 'darwin' in sys.platform: diff --git a/src/bitmessageqt/newchandialog.py b/src/bitmessageqt/newchandialog.py index 43235bfa..65da1f1f 100644 --- a/src/bitmessageqt/newchandialog.py +++ b/src/bitmessageqt/newchandialog.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'newchandialog.ui' # -# Created: Mon Jul 22 01:05:35 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Wed Aug 7 16:51:29 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_newChanDialog(object): def setupUi(self, newChanDialog): newChanDialog.setObjectName(_fromUtf8("newChanDialog")) - newChanDialog.resize(530, 422) + newChanDialog.resize(553, 422) newChanDialog.setMinimumSize(QtCore.QSize(0, 0)) self.formLayout = QtGui.QFormLayout(newChanDialog) self.formLayout.setObjectName(_fromUtf8("formLayout")) @@ -87,13 +87,18 @@ class Ui_newChanDialog(object): QtCore.QObject.connect(self.radioButtonJoinChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxJoinChan.setShown) QtCore.QObject.connect(self.radioButtonCreateChan, QtCore.SIGNAL(_fromUtf8("toggled(bool)")), self.groupBoxCreateChan.setShown) QtCore.QMetaObject.connectSlotsByName(newChanDialog) + newChanDialog.setTabOrder(self.radioButtonJoinChan, self.radioButtonCreateChan) + newChanDialog.setTabOrder(self.radioButtonCreateChan, self.lineEditChanNameCreate) + newChanDialog.setTabOrder(self.lineEditChanNameCreate, self.lineEditChanNameJoin) + newChanDialog.setTabOrder(self.lineEditChanNameJoin, self.lineEditChanBitmessageAddress) + newChanDialog.setTabOrder(self.lineEditChanBitmessageAddress, self.buttonBox) def retranslateUi(self, newChanDialog): newChanDialog.setWindowTitle(_translate("newChanDialog", "Dialog", None)) self.radioButtonCreateChan.setText(_translate("newChanDialog", "Create a new chan", None)) self.radioButtonJoinChan.setText(_translate("newChanDialog", "Join a chan", None)) self.groupBoxCreateChan.setTitle(_translate("newChanDialog", "Create a chan", None)) - self.label_4.setText(_translate("newChanDialog", "Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private.", None)) + self.label_4.setText(_translate("newChanDialog", "

Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.

", None)) self.label_5.setText(_translate("newChanDialog", "Chan name:", None)) self.groupBoxJoinChan.setTitle(_translate("newChanDialog", "Join a chan", None)) self.label.setText(_translate("newChanDialog", "

A chan exists when a group of people share the same decryption keys. The keys and bitmessage address used by a chan are generated from a human-friendly word or phrase (the chan name). To send a message to everyone in the chan, send a normal person-to-person message to the chan address.

Chans are experimental and completely unmoderatable.

", None)) diff --git a/src/bitmessageqt/newchandialog.ui b/src/bitmessageqt/newchandialog.ui index 3713c6a3..2d42b3db 100644 --- a/src/bitmessageqt/newchandialog.ui +++ b/src/bitmessageqt/newchandialog.ui @@ -6,7 +6,7 @@ 0 0 - 530 + 553 422 @@ -46,7 +46,7 @@ - Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. + <html><head/><body><p>Enter a name for your chan. If you choose a sufficiently complex chan name (like a strong and unique passphrase) and none of your friends share it publicly then the chan will be secure and private. If you and someone else both create a chan with the same chan name then it is currently very likely that they will be the same chan.</p></body></html> true @@ -130,6 +130,14 @@
+ + radioButtonJoinChan + radioButtonCreateChan + lineEditChanNameCreate + lineEditChanNameJoin + lineEditChanBitmessageAddress + buttonBox + diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index d7672e9d..40a57f5b 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Fri Jul 12 12:37:53 2013 -# by: PyQt4 UI code generator 4.10.1 +# Created: Wed Aug 7 16:58:45 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_settingsDialog(object): def setupUi(self, settingsDialog): settingsDialog.setObjectName(_fromUtf8("settingsDialog")) - settingsDialog.resize(445, 343) + settingsDialog.resize(462, 343) self.gridLayout = QtGui.QGridLayout(settingsDialog) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) @@ -41,33 +41,36 @@ class Ui_settingsDialog(object): self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface")) self.gridLayout_5 = QtGui.QGridLayout(self.tabUserInterface) self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) - self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) - self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) - self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) - self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 1) - self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxMinimizeToTray.setChecked(True) - self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) - self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) - self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) - self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 1) - self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) - self.gridLayout_5.addWidget(self.checkBoxPortableMode, 4, 0, 1, 1) - self.label_7 = QtGui.QLabel(self.tabUserInterface) - self.label_7.setWordWrap(True) - self.label_7.setObjectName(_fromUtf8("label_7")) - self.gridLayout_5.addWidget(self.label_7, 5, 0, 1, 1) self.labelSettingsNote = QtGui.QLabel(self.tabUserInterface) self.labelSettingsNote.setText(_fromUtf8("")) self.labelSettingsNote.setWordWrap(True) self.labelSettingsNote.setObjectName(_fromUtf8("labelSettingsNote")) - self.gridLayout_5.addWidget(self.labelSettingsNote, 6, 0, 1, 1) + self.gridLayout_5.addWidget(self.labelSettingsNote, 7, 0, 1, 1) spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_5.addItem(spacerItem, 7, 0, 1, 1) + self.gridLayout_5.addItem(spacerItem, 8, 0, 1, 1) + self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) + self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 1) + self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) + self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 1) + self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxMinimizeToTray.setChecked(True) + self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) + self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) + self.label_7 = QtGui.QLabel(self.tabUserInterface) + self.label_7.setWordWrap(True) + self.label_7.setObjectName(_fromUtf8("label_7")) + self.gridLayout_5.addWidget(self.label_7, 5, 0, 1, 1) + self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) + self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) + self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) + self.gridLayout_5.addWidget(self.checkBoxPortableMode, 4, 0, 1, 1) + self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile")) + self.gridLayout_5.addWidget(self.checkBoxWillinglySendToMobile, 6, 0, 1, 1) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) self.tabNetworkSettings = QtGui.QWidget() self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings")) @@ -252,12 +255,13 @@ class Ui_settingsDialog(object): def retranslateUi(self, settingsDialog): settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None)) - self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None)) - self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) self.checkBoxShowTrayNotifications.setText(_translate("settingsDialog", "Show notification when message received", None)) - self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) + self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) self.label_7.setText(_translate("settingsDialog", "In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.", None)) + self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) + self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) + self.checkBoxWillinglySendToMobile.setText(_translate("settingsDialog", "Willingly include unencrypted destination address when sending to a mobile device", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.groupBox.setTitle(_translate("settingsDialog", "Listening port", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index 9414e1a4..44c4f973 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -6,7 +6,7 @@ 0 0 - 445 + 462 343 @@ -37,13 +37,29 @@ User Interface - - + + - Start Bitmessage on user login + + + + true + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -51,6 +67,13 @@ + + + + Show notification when message received + + + @@ -61,20 +84,6 @@ - - - - Show notification when message received - - - - - - - Run in Portable Mode - - - @@ -85,28 +94,26 @@ - - + + - - - - true + Start Bitmessage on user login - - - - Qt::Vertical + + + + Run in Portable Mode - - - 20 - 40 - + + + + + + Willingly include unencrypted destination address when sending to a mobile device - + diff --git a/src/class_singleWorker.py b/src/class_singleWorker.py index 56f23859..b29b7f8d 100644 --- a/src/class_singleWorker.py +++ b/src/class_singleWorker.py @@ -9,6 +9,7 @@ import proofofwork import sys from class_addressGenerator import pointMult import tr +from debug import logger # This thread, of which there is only one, does the heavy lifting: # calculating POWs. @@ -517,6 +518,15 @@ class singleWorker(threading.Thread): pubkeyPayload[readPosition:readPosition + 10]) readPosition += streamNumberLength behaviorBitfield = pubkeyPayload[readPosition:readPosition + 4] + # Mobile users may ask us to include their address's RIPE hash on a message + # unencrypted. Before we actually do it the sending human must check a box + # in the settings menu to allow it. + if shared.isBitSetWithinBitfield(behaviorBitfield,30): # if receiver is a mobile device who expects that their address RIPE is included unencrypted on the front of the message.. + if not shared.safeConfigGetBoolean('bitmessagesettings','willinglysendtomobile'): # if we are Not willing to include the receiver's RIPE hash on the message.. + logger.info('The receiver is a mobile user but the sender (you) has not selected that you are willing to send to mobiles. Aborting send.') + shared.UISignalQueue.put(('updateSentItemStatusByAckdata',(ackdata,tr.translateText("MainWindow",'Problem: Destination is a mobile device who requests that the destination be included in the message but this is disallowed in your settings. %1').arg(unicode(strftime(shared.config.get('bitmessagesettings', 'timeformat'),localtime(int(time.time()))),'utf-8'))))) + # if the human changes their setting and then sends another message or restarts their client, this one will send at that time. + continue readPosition += 4 # to bypass the bitfield of behaviors # pubSigningKeyBase256 = # pubkeyPayload[readPosition:readPosition+64] #We don't use this @@ -663,6 +673,10 @@ class singleWorker(threading.Thread): with shared.printLock: print 'Not bothering to generate ackdata because we are sending to a chan.' fullAckPayload = '' + elif not shared.isBitSetWithinBitfield(behaviorBitfield,31): + with shared.printLock: + print 'Not bothering to generate ackdata because the receiver said that they won\'t relay it anyway.' + fullAckPayload = '' else: fullAckPayload = self.generateFullAckMessage( ackdata, toStreamNumber, embeddedTime) # The fullAckPayload is a normal msg protocol message with the proof of work already completed that the receiver of this message can easily send out. diff --git a/src/shared.py b/src/shared.py index 09a2c26c..db5a9810 100644 --- a/src/shared.py +++ b/src/shared.py @@ -201,17 +201,17 @@ def decodeWalletImportFormat(WIFstring): fullString = arithmetic.changebase(WIFstring,58,256) privkey = fullString[:-4] if fullString[-4:] != hashlib.sha256(hashlib.sha256(privkey).digest()).digest()[:4]: - logger.error('Major problem! When trying to decode one of your private keys, the checksum ' - 'failed. Here is the PRIVATE key: %s\n' % str(WIFstring)) + logger.critical('Major problem! When trying to decode one of your private keys, the checksum ' + 'failed. Here is the PRIVATE key: %s' % str(WIFstring)) return "" else: #checksum passed if privkey[0] == '\x80': return privkey[1:] else: - logger.error('Major problem! When trying to decode one of your private keys, the ' + logger.critical('Major problem! When trying to decode one of your private keys, the ' 'checksum passed but the key doesn\'t begin with hex 80. Here is the ' - 'PRIVATE key: %s\n' % str(WIFstring)) + 'PRIVATE key: %s' % str(WIFstring)) return "" @@ -367,6 +367,12 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys): except Exception, e: logger.exception('Keyfile permissions could not be fixed.') raise + +def isBitSetWithinBitfield(fourByteString, n): + # Uses MSB 0 bit numbering across 4 bytes of data + n = 31 - n + x, = unpack('>L', fourByteString) + return x & 2**n != 0 Peer = collections.namedtuple('Peer', ['host', 'port']) From 176340c22d3dc0f8493ea26c7d942bf4fb0110a7 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 8 Aug 2013 17:50:12 -0400 Subject: [PATCH 32/37] added api command: getPubkeyByHash --- src/bitmessagemain.py | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 4e6b327f..cae8daeb 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -743,11 +743,12 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # already been encrypted and had the necessary proof of work done for it to be # disseminated to the rest of the Bitmessage network. PyBitmessage accepts this msg # object and sends it out to the rest of the Bitmessage network as if it had generated the - # message itself. + # message itself. Please do not yet add this to the api doc. if len(params) != 1: return 'API Error 0000: I need 1 parameter!' encryptedPayload, = params encryptedPayload = encryptedPayload.decode('hex') + toStreamNumber = decodeVarint(encryptedPayload[16:26])[0] inventoryHash = calculateInventoryHash(encryptedPayload) objectType = 'msg' shared.inventory[inventoryHash] = ( @@ -760,22 +761,32 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): # The device issuing this command to PyBitmessage supplies a pubkey object that has # already had the necessary proof of work done for it to be disseminated to the rest of the # Bitmessage network. PyBitmessage accepts this pubkey object and sends it out to the - # rest of the Bitmessage network as if it had generated the pubkey object itself. + # rest of the Bitmessage network as if it had generated the pubkey object itself. Please + # do not yet add this to the api doc. if len(params) != 1: return 'API Error 0000: I need 1 parameter!' payload, = params payload = payload.decode('hex') + pubkeyReadPosition = 8 # bypass the nonce + if payload[pubkeyReadPosition:pubkeyReadPosition+4] == '\x00\x00\x00\x00': # if this pubkey uses 8 byte time + pubkeyReadPosition += 8 + else: + pubkeyReadPosition += 4 + addressVersion, addressVersionLength = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10]) + pubkeyReadPosition += addressVersionLength + pubkeyStreamNumber = decodeVarint(payload[pubkeyReadPosition:pubkeyReadPosition+10])[0] inventoryHash = calculateInventoryHash(payload) objectType = 'pubkey' shared.inventory[inventoryHash] = ( - objectType, streamNumber, payload, int(time.time())) + objectType, pubkeyStreamNumber, payload, int(time.time())) with shared.printLock: print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex') shared.broadcastToSendDataQueues(( streamNumber, 'sendinv', inventoryHash)) - elif method == 'getMessageDataByDestinationRIPEHash': + elif method == 'getMessageDataByDestinationHash': # Method will eventually be used by a particular Android app to - # select relevant messages. + # select relevant messages. Do not yet add this to the api + # doc. if len(params) != 1: return 'API Error 0000: I need 1 parameter!' @@ -816,7 +827,26 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': ')) data += ']}' return data - + elif method == 'getPubkeyByHash': + # Method will eventually be used by a particular Android app to + # retrieve pubkeys. Please do not yet add this to the api docs. + if len(params) != 1: + return 'API Error 0000: I need 1 parameter!' + requestedHash, = params + if len(requestedHash) != 40: + return 'API Error 0019: The length of hash should be 20 bytes (encoded in hex thus 40 characters).' + requestedHash = requestedHash.decode('hex') + parameters = (requestedHash,) + with shared.sqlLock: + shared.sqlSubmitQueue.put('''SELECT transmitdata FROM pubkeys WHERE hash = ? ; ''') + shared.sqlSubmitQueue.put(parameters) + queryreturn = shared.sqlReturnQueue.get() + data = '{"pubkey":[' + for row in queryreturn: + transmitdata, = row + data += json.dumps({'data':transmitdata.encode('hex')}, indent=4, separators=(',', ': ')) + data += ']}' + return data elif method == 'clientStatus': return '{ "networkConnections" : "%s" }' % str(len(shared.connectedHostsList)) else: From 035d3af61202f0c3acdd7c32bc7d6f0fa6fe0e26 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Thu, 8 Aug 2013 17:55:15 -0400 Subject: [PATCH 33/37] revert message_data_reader.py to the state it was in before I used it for debugging --- src/message_data_reader.py | 38 ++++++-------------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/src/message_data_reader.py b/src/message_data_reader.py index 35b2441a..084ef553 100644 --- a/src/message_data_reader.py +++ b/src/message_data_reader.py @@ -54,38 +54,13 @@ def readPubkeys(): def readInventory(): print 'Printing everything in inventory table:' - item = '''select hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage from inventory where objecttype = 'msg' ''' + item = '''select hash, objecttype, streamnumber, payload, receivedtime from inventory''' parameters = '' cur.execute(item, parameters) output = cur.fetchall() - for row in output[:50]: - hash, objecttype, streamnumber, payload, receivedtime, first20bytesofencryptedmessage = row - print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', 'first20bytesofencryptedmessage:', first20bytesofencryptedmessage.encode('hex'), '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') - -def readInventory2(): - searchValue = ' ' - - item = '''PRAGMA case_sensitive_like = true ''' - parameters = '' - cur.execute(item, parameters) - - searchValue = string.replace(searchValue,'e','ee') - searchValue = string.replace(searchValue,'%','e%') - searchValue = string.replace(searchValue,'_','e_') - - print 'Printing subset of inventory table:' - item = '''SELECT substr(payload,20) FROM inventory''' - #parameters = ('%'+ searchValue + '%',) - #print repr(parameters), len(parameters[0]) - parameters = '' - cur.execute(item, parameters) - output = cur.fetchall() - print 'Number of results:', len(output) - for row in output[:100]: - print row - #hash, objecttype, streamnumber, payload, receivedtime = row - #print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') - print 'done' + for row in output: + hash, objecttype, streamnumber, payload, receivedtime = row + print 'Hash:', hash.encode('hex'), objecttype, streamnumber, '\t', payload.encode('hex'), '\t', unicode(strftime('%a, %d %b %Y %I:%M %p',localtime(receivedtime)),'utf-8') def takeInboxMessagesOutOfTrash(): @@ -123,13 +98,12 @@ def vacuum(): #takeInboxMessagesOutOfTrash() #takeSentMessagesOutOfTrash() #markAllInboxMessagesAsUnread() -#readInbox() +readInbox() #readSent() #readPubkeys() #readSubscriptions() -readInventory() +#readInventory() #vacuum() #will defragment and clean empty space from the messages.dat file. -#readInventory2() From d3b9791442f800e85a7d450b24dfcdd65121ae45 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 9 Aug 2013 12:12:57 -0400 Subject: [PATCH 34/37] Fix problem with pull request #388 --- src/defaultKnownNodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/defaultKnownNodes.py b/src/defaultKnownNodes.py index d4611099..f481581c 100644 --- a/src/defaultKnownNodes.py +++ b/src/defaultKnownNodes.py @@ -37,7 +37,7 @@ def createDefaultKnownNodes(appdata): #print stream1 #print allKnownNodes - with open(appdata + 'knownnodes.dat', 'wb) as output: + with open(appdata + 'knownnodes.dat', 'wb') as output: # Pickle dictionary using protocol 0. pickle.dump(allKnownNodes, output) From 1a43402ad2cacca9a674526bf20c01e8d1f5e86c Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 9 Aug 2013 14:21:24 -0400 Subject: [PATCH 35/37] Adjusted size of main window back to the way it was --- src/bitmessageqt/bitmessageui.py | 8 ++++---- src/bitmessageqt/bitmessageui.ui | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index d5388182..54a3ee6f 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Thu Aug 1 00:22:41 2013 +# Created: Fri Aug 9 14:17:50 2013 # by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(775, 598) + MainWindow.resize(795, 580) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) @@ -422,7 +422,7 @@ class Ui_MainWindow(object): self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 775, 21)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 27)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -539,7 +539,7 @@ class Ui_MainWindow(object): self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" +"\n" "


", None)) self.label.setText(_translate("MainWindow", "To:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index 5d616be7..c2caebe0 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -6,8 +6,8 @@ 0 0 - 775 - 598 + 795 + 580 @@ -257,7 +257,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html> @@ -1010,8 +1010,8 @@ p, li { white-space: pre-wrap; } 0 0 - 775 - 21 + 795 + 27 From 0026dce551d245e6f8a9173335f199a9f33bb24a Mon Sep 17 00:00:00 2001 From: "Grant T. Olson" Date: Mon, 12 Aug 2013 20:32:13 -0400 Subject: [PATCH 36/37] Fix for traceback on OSX. Fixes Bitmessage/PyBitmessage#401 --- src/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared.py b/src/shared.py index da83f374..1d3b19bc 100644 --- a/src/shared.py +++ b/src/shared.py @@ -355,7 +355,7 @@ def checkSensitiveFilePermissions(filename): return True except: # Swallow exception here, but we might run into trouble later! - logger.error('Could not determine filesystem type.', filename) + logger.error('Could not determine filesystem type. %s', filename) present_permissions = os.stat(filename)[0] disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO return present_permissions & disallowed_permissions == 0 From a364cfb98b856b320d9c472d6732f3cbc5f28ea1 Mon Sep 17 00:00:00 2001 From: Chuck Date: Wed, 14 Aug 2013 13:31:06 +0700 Subject: [PATCH 37/37] POP3 allows for an argument in LIST command --- src/class_pop3Server.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/class_pop3Server.py b/src/class_pop3Server.py index dba573dd..77fd2e31 100644 --- a/src/class_pop3Server.py +++ b/src/class_pop3Server.py @@ -212,10 +212,18 @@ class bitmessagePOP3Connection(asyncore.dispatcher): def handleList(self, data): self.populateMessageIndex() - yield "+OK {} messages ({} octets)".format(len(self.messages), self.storage_size) - for i, msg in enumerate(self.messages): - yield "{} {}".format(i + 1, msg['size']) - yield "." + if len(data): + index = int(data.decode('ascii')) - 1 + assert index >= 0 + if index < len(self.messages): + yield "+OK {} {}".format(index, self.messages[index]['size']) + else: + yield "-ERR no such message" + else: + yield "+OK {} messages ({} octets)".format(len(self.messages), self.storage_size) + for i, msg in enumerate(self.messages): + yield "{} {}".format(i + 1, msg['size']) + yield "." #def handleTop(self, data): # cmd, num, lines = data.split()