Merge branch 'foo'

This commit is contained in:
Chuck 2013-08-14 14:42:30 +07:00
commit 5f64271800
35 changed files with 999 additions and 583 deletions

101
INSTALL.md Normal file
View File

@ -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

View File

@ -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`

View File

@ -1,6 +1,6 @@
# Maintainer: Bob Mottram (4096 bits) <bob@robotics.uk.to>
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')

2
debian.sh Normal file → Executable file
View File

@ -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}

28
debian/changelog vendored
View File

@ -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) <bob@robotics.uk.to> Mon, 29 July 2013 22:11:00 +0100
pybitmessage (0.3.4-1) raring; urgency=low
* Switched addr, msg, broadcast, and getpubkey message types

0
debian/rules vendored Normal file → Executable file
View File

View File

@ -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`

View File

@ -4,4 +4,29 @@
rm -f Makefile rpmpackage/*.spec
packagemonkey -n "PyBitmessage" --version "0.3.4" --dir "." -l "mit" -e "Bob Mottram (4096 bits) <bob@robotics.uk.to>" --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) <bob@robotics.uk.to>" \
--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"

View File

@ -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`

View File

@ -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|

View File

@ -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|

4
rpm.sh
View File

@ -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`

View File

@ -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) <bob@robotics.uk.to> - 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) <bob@robotics.uk.to> - 0.3.4-1
- Switched addr, msg, broadcast, and getpubkey message types
to 8 byte time. Last remaining type is pubkey.

View File

@ -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

View File

@ -329,6 +329,21 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': '))
data += ']}'
return data
elif method == 'getAllInboxMessageIds' or 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!'
@ -363,7 +378,21 @@ 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 == 'getInboxMessagesByAddress':
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('')
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 == '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]
@ -439,7 +468,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')
@ -681,10 +727,134 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
shared.UISignalQueue.put(('rerenderInboxFromLabels', ''))
shared.UISignalQueue.put(('rerenderSubscriptions', ''))
return 'Deleted subscription if it existed.'
elif method == 'listSubscriptions':
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 == '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. 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] = (
objectType, toStreamNumber, encryptedPayload, int(time.time()))
with shared.printLock:
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex')
shared.broadcastToSendDataQueues((
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. 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, 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 == 'getMessageDataByDestinationHash':
# Method will eventually be used by a particular Android app to
# select relevant messages. Do not yet add this to the api
# doc.
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')
# 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 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
if len(data) > 25:
data += ','
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:
return 'Invalid Method: %s' % method
return 'API Error 0020: Invalid method: %s' % method
# This thread, of which there is only one, runs the API.
@ -700,11 +870,8 @@ class singleAPI(threading.Thread):
se.register_introspection_functions()
se.serve_forever()
selfInitiatedConnections = {}
# This is a list of current connections (the thread pointers at least)
selfInitiatedConnections = {}
if shared.useVeryEasyProofOfWorkForTesting:
@ -713,7 +880,8 @@ if shared.useVeryEasyProofOfWorkForTesting:
shared.networkDefaultPayloadLengthExtraBytes = int(
shared.networkDefaultPayloadLengthExtraBytes / 7000)
if __name__ == "__main__":
class Main:
def start(self, deamon=False):
# is the application already running? If yes then exit.
thisapp = singleton.singleinstance()
@ -749,9 +917,9 @@ if __name__ == "__main__":
pass
# And finally launch asyncore
asyncoreThread = asyncoreThread()
asyncoreThread.daemon = True
asyncoreThread.start()
asyncoreThreadInstance = asyncoreThread()
asyncoreThreadInstance.daemon = True
asyncoreThreadInstance.start()
shared.reloadMyAddressHashes()
shared.reloadBroadcastSendersForWhichImWatching()
@ -778,7 +946,7 @@ if __name__ == "__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:
@ -790,12 +958,35 @@ if __name__ == "__main__":
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 '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__":
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.
# 1H5XaDA6fYENLbknwZyjiYXYPQaFjjLX2u

View File

@ -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):
@ -982,29 +987,63 @@ 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?
# 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
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:
if soundFilename is not None and play is True:
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'):
soundFilename = soundFilename + '.mp3'
@ -2008,6 +2047,8 @@ class MyForm(QtGui.QMainWindow):
else:
shared.config.set('bitmessagesettings', 'keyfile', str(
self.settingsDialogInstance.sslKeyFile))
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(
@ -2838,6 +2879,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
@ -2857,8 +2903,6 @@ class MyForm(QtGui.QMainWindow):
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)
@ -3021,7 +3065,8 @@ class settingsDialog(QtGui.QDialog):
except:
self.ui.checkBoxStripMessageHeaders.setChecked(True)
self.ui.lineEditMessageHeadersToStrip.setText('User-Agent')
self.ui.checkBoxWillinglySendToMobile.setChecked(
shared.safeConfigGetBoolean('bitmessagesettings', 'willinglysendtomobile'))
if shared.appdata == '':
self.ui.checkBoxPortableMode.setChecked(True)
if 'darwin' in sys.platform:
@ -3506,7 +3551,8 @@ def run():
translator = QtCore.QTranslator()
try:
translator.load("translations/bitmessage_" + str(locale.getlocale()[0]))
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.

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'bitmessageui.ui'
#
# Created: Mon Jul 29 14:54:01 2013
# Created: Wed Aug 14 13:43:40 2013
# by: PyQt4 UI code generator 4.10.1
#
# 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(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)
@ -424,7 +424,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, 795, 27))
self.menubar.setObjectName(_fromUtf8("menubar"))
self.menuFile = QtGui.QMenu(self.menubar)
self.menuFile.setObjectName(_fromUtf8("menuFile"))
@ -442,20 +442,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)
@ -525,8 +541,8 @@ class Ui_MainWindow(object):
self.textEditMessage.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<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>", None))
"</style></head><body style=\" font-family:\'Droid Sans\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<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>", 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))
@ -601,7 +617,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))

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>795</width>
<height>561</height>
<height>580</height>
</rect>
</property>
<property name="windowTitle">
@ -257,8 +257,8 @@
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Droid Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-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';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
@ -1016,7 +1016,7 @@ p, li { white-space: pre-wrap; }
<x>0</x>
<y>0</y>
<width>795</width>
<height>18</height>
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -1066,41 +1066,87 @@ p, li { white-space: pre-wrap; }
<property name="enabled">
<bool>true</bool>
</property>
<property name="icon">
<iconset theme="dialog-password">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Manage keys</string>
</property>
</action>
<action name="actionExit">
<property name="icon">
<iconset theme="application-exit">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Quit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="actionHelp">
<property name="icon">
<iconset theme="help-contents">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Help</string>
</property>
<property name="shortcut">
<string>F1</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset theme="help-about">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>About</string>
</property>
</action>
<action name="actionSettings">
<property name="icon">
<iconset theme="document-properties">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Settings</string>
</property>
</action>
<action name="actionRegenerateDeterministicAddresses">
<property name="icon">
<iconset theme="view-refresh">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Regenerate deterministic addresses</string>
</property>
</action>
<action name="actionDeleteAllTrashedMessages">
<property name="icon">
<iconset theme="user-trash">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Delete all trashed messages</string>
</property>
</action>
<action name="actionJoinChan">
<property name="icon">
<iconset theme="contact-new">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Join / Create chan</string>
</property>

View File

@ -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", "<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>", None))
self.label_5.setText(_translate("newChanDialog", "Chan name:", None))
self.groupBoxJoinChan.setTitle(_translate("newChanDialog", "Join a chan", None))
self.label.setText(_translate("newChanDialog", "<html><head/><body><p>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.</p><p>Chans are experimental and completely unmoderatable.</p></body></html>", None))

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>530</width>
<width>553</width>
<height>422</height>
</rect>
</property>
@ -46,7 +46,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>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.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -130,6 +130,14 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>radioButtonJoinChan</tabstop>
<tabstop>radioButtonCreateChan</tabstop>
<tabstop>lineEditChanNameCreate</tabstop>
<tabstop>lineEditChanNameJoin</tabstop>
<tabstop>lineEditChanBitmessageAddress</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'settings.ui'
#
# Created: Mon Jul 29 14:53:58 2013
# Created: Wed Aug 14 13:44:07 2013
# by: PyQt4 UI code generator 4.10.1
#
# WARNING! All changes made in this file will be lost!
@ -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"))
@ -396,12 +399,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))

View File

@ -37,13 +37,29 @@
<string>User Interface</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxStartOnLogon">
<item row="7" column="0">
<widget class="QLabel" name="labelSettingsNote">
<property name="text">
<string>Start Bitmessage on user login</string>
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxStartInTray">
<property name="text">
@ -51,6 +67,13 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxShowTrayNotifications">
<property name="text">
<string>Show notification when message received</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxMinimizeToTray">
<property name="text">
@ -61,20 +84,6 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxShowTrayNotifications">
<property name="text">
<string>Show notification when message received</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxPortableMode">
<property name="text">
<string>Run in Portable Mode</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
@ -85,28 +94,26 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="labelSettingsNote">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxStartOnLogon">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
<string>Start Bitmessage on user login</string>
</property>
</widget>
</item>
<item row="7" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxPortableMode">
<property name="text">
<string>Run in Portable Mode</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="checkBoxWillinglySendToMobile">
<property name="text">
<string>Willingly include unencrypted destination address when sending to a mobile device</string>
</property>
</spacer>
</widget>
</item>
</layout>
</widget>

View File

@ -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
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)

View File

@ -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.host 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(

View File

@ -212,6 +212,14 @@ class bitmessagePOP3Connection(asyncore.dispatcher):
def handleList(self, data):
self.populateMessageIndex()
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'])

View File

@ -44,14 +44,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.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
@ -72,18 +71,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()
@ -95,16 +94,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.host]
except Exception as err:
with shared.printLock:
print 'Could not delete', self.HOST, 'from shared.connectedHostsList.', err
print 'Could not delete', self.peer.host, 'from shared.connectedHostsList.', err
try:
del shared.numberOfObjectsThatWeHaveYetToCheckAndSeeWhetherWeAlreadyHavePerPeer[
self.HOST]
self.peer]
except:
pass
shared.UISignalQueue.put(('updateNetworkStatusTab', 'no data'))
@ -139,13 +138,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])
@ -198,28 +196,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', str(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', str(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()
@ -264,22 +262,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()
@ -1545,7 +1543,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
@ -1727,11 +1725,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 = (
@ -1740,15 +1738,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')
@ -1834,14 +1829,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()
shared.knownNodes[recaddrStream][hostFromAddrMessage] = (
recaddrPort, timeSomeoneElseReceivedMessageFromThisNode)
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 = (
@ -1850,15 +1846,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')
@ -1915,35 +1908,35 @@ 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 = ''
# 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(
@ -1954,8 +1947,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(
@ -1966,8 +1959,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(
@ -2004,9 +1997,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
@ -2028,30 +2021,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.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:
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()
@ -2068,7 +2060,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:

View File

@ -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:

View File

@ -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]

View File

@ -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.
@ -271,81 +272,11 @@ 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:
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:
@ -414,7 +345,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))
@ -425,17 +357,13 @@ class singleWorker(threading.Thread):
# a 'broadcastsent' status
shared.sqlLock.acquire()
t = (inventoryHash,'broadcastsent', int(
time.time()), fromaddress, subject, body, 'broadcastqueued')
time.time()), ackdata)
shared.sqlSubmitQueue.put(
'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE fromaddress=? AND subject=? AND message=? AND status=?')
'UPDATE sent SET msgid=?, status=?, lastactiontime=? WHERE ackdata=?')
shared.sqlSubmitQueue.put(t)
queryreturn = shared.sqlReturnQueue.get()
shared.sqlSubmitQueue.put('commit')
shared.sqlLock.release()
else:
with shared.printLock:
sys.stderr.write(
'Error: In the singleWorker thread, the sendBroadcast function doesn\'t understand the address version.\n')
def sendMsg(self):
@ -590,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
@ -736,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.

View File

@ -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,6 +190,19 @@ class sqlThread(threading.Thread):
if not shared.config.has_option('bitmessagesettings', 'sockslisten'):
shared.config.set('bitmessagesettings', 'sockslisten', 'false')
# 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 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'
t = ('1234', testpayload, '12345678', 'no')

View File

@ -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 #################
@ -36,22 +37,25 @@ 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)
output.close()
return allKnownNodes
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__":

View File

@ -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:
@ -28,13 +40,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:

View File

@ -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

View File

@ -9,6 +9,7 @@ useVeryEasyProofOfWorkForTesting = False # If you set this to True while on the
# Libraries.
import collections
import ConfigParser
import os
import pickle
@ -122,10 +123,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.
@ -224,17 +223,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 ""
@ -264,8 +263,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)
@ -341,8 +339,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]
@ -367,6 +365,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. %s', filename)
present_permissions = os.stat(filename)[0]
disallowed_permissions = stat.S_IRWXG | stat.S_IRWXO
return present_permissions & disallowed_permissions == 0
@ -392,5 +403,13 @@ def fixSensitiveFilePermissions(filename, hasEnabledKeys):
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'])
helper_startup.loadConfig()
from debug import logger