Merge pull request #852 from Bitmessage/v0.6

V0.6
This commit is contained in:
Jonathan Warren 2016-05-02 19:58:38 -04:00
commit af0dfdcf93
133 changed files with 42847 additions and 15664 deletions

3
.gitignore vendored
View File

@ -5,4 +5,5 @@ src/build
src/dist
src/.project
src/.pydevproject
src/.settings/
src/.settings/
*.dll

View File

@ -1,5 +1,5 @@
Copyright (c) 2012-2015 Jonathan Warren
Copyright (c) 2013-2015 The Bitmessage Developers
Copyright (c) 2012-2016 Jonathan Warren
Copyright (c) 2013-2016 The Bitmessage Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2012-2014 Jonathan Warren
Copyright (c) 2013-2014 The Bitmessage Developers
Copyright (c) 2012-2016 Jonathan Warren
Copyright (c) 2013-2016 The Bitmessage Developers
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@ -1,5 +1,6 @@
APP=pybitmessage
VERSION=0.4.4
APPDIR=`basename "\`pwd\`"`
VERSION=0.6.0
RELEASE=1
ARCH_TYPE=`uname -m`
PREFIX?=/usr/local
@ -8,7 +9,7 @@ LIBDIR=lib
all:
debug:
source:
tar -cvf ../${APP}_${VERSION}.orig.tar ../${APP}-${VERSION} --exclude-vcs
tar -cvf ../${APP}_${VERSION}.orig.tar ../${APPDIR} --exclude-vcs
gzip -f9n ../${APP}_${VERSION}.orig.tar
install:
mkdir -p ${DESTDIR}/usr
@ -57,5 +58,5 @@ clean:
rm -f puppypackage/*.gz puppypackage/*.pet slackpackage/*.txz
sourcedeb:
tar -cvf ../${APP}_${VERSION}.orig.tar ../${APP}-${VERSION} --exclude-vcs --exclude 'debian'
tar -cvf ../${APP}_${VERSION}.orig.tar ../${APPDIR} --exclude-vcs --exclude 'debian'
gzip -f9n ../${APP}_${VERSION}.orig.tar

View File

@ -2,7 +2,7 @@
APP=pybitmessage
PREV_VERSION=0.4.4
VERSION=0.4.4
VERSION=0.6.0
RELEASE=1
ARCH_TYPE=any
CURRDIR=`pwd`

View File

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

20
compiletest.py Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/python2.7
import ctypes
import fnmatch
import os
import sys
import traceback
matches = []
for root, dirnames, filenames in os.walk('src'):
for filename in fnmatch.filter(filenames, '*.py'):
matches.append(os.path.join(root, filename))
for filename in matches:
source = open(filename, 'r').read() + '\n'
try:
compile(source, filename, 'exec')
except Exception as e:
ctypes.windll.user32.MessageBoxA(0, traceback.format_exc(), "Exception in " + filename, 1)
sys.exit(1)

View File

@ -1,11 +1,13 @@
#!/bin/bash
APP=pybitmessage
PREV_VERSION=0.4.2
VERSION=0.4.4
PREV_VERSION=0.4.4
VERSION=0.6.0
RELEASE=1
ARCH_TYPE=all
DIR=${APP}-${VERSION}
CURDIR=`pwd`
SHORTDIR=`basename ${CURDIR}`
if [ $ARCH_TYPE == "x86_64" ]; then
ARCH_TYPE="amd64"
@ -30,17 +32,17 @@ make clean
make
# Change the parent directory name to Debian format
mv ../${APP} ../${DIR}
mv ../${SHORTDIR} ../${DIR}
# Create a source archive
make sourcedeb
# Build the package
dpkg-buildpackage -F
dpkg-buildpackage -F -us -uc
# Sign files
gpg -ba ../${APP}_${VERSION}-1_${ARCH_TYPE}.deb
gpg -ba ../${APP}_${VERSION}.orig.tar.gz
# Restore the parent directory name
mv ../${DIR} ../${APP}
mv ../${DIR} ../${SHORTDIR}

9
debian/changelog vendored
View File

@ -1,3 +1,12 @@
pybitmessage (0.6.0-1) trusty; urgency=low
* Bugfixes
* UI improvements
* performance and security improvements
* integration with email gateway (mailchuck.com)
-- Peter Surda <dev@mailchuck.com> Mon, 2 May 2016 16:25:00 +0200
pybitmessage (0.4.4-1) utopic; urgency=low
* Added ability to limit network transfer rate

4
debian/copyright vendored
View File

@ -3,11 +3,11 @@ Upstream-Name:
Source:
Files: *
Copyright: Copyright 2014 Bob Mottram (4096 bits) <bob@robotics.uk.to>
Copyright: Copyright 2016 Bob Mottram (4096 bits) <bob@robotics.uk.to>
License: MIT
Files: debian/*
Copyright: Copyright 2014 Bob Mottram (4096 bits) <bob@robotics.uk.to>
Copyright: Copyright 2016 Bob Mottram (4096 bits) <bob@robotics.uk.to>
License: MIT
License: MIT

View File

@ -2,7 +2,7 @@
APP=pybitmessage
PREV_VERSION=0.4.4
VERSION=0.4.4
VERSION=0.6.0
RELEASE=1
SOURCEDIR=.
ARCH_TYPE=`uname -m`

View File

@ -1,32 +0,0 @@
# $Header: $
EAPI=5
inherit git-2 python-r1
PYTHON_COMPAT=( python2_7 )
PYTHON_REQ_USE="sqlite"
REQUIRED_USE="${PYTHON_REQUIRED_USE}"
DESCRIPTION="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"
EGIT_REPO_URI="https://github.com/Bitmessage/PyBitmessage.git"
LICENSE="MIT"
SLOT="0"
KEYWORDS="x86"
DEPEND="dev-libs/popt
${PYTHON_DEPS}"
RDEPEND="${DEPEND}
dev-libs/openssl
dev-python/PyQt4[]"
src_configure() {
econf --with-popt
}
src_compile() { :; }
src_install() {
emake DESTDIR="${D}" PREFIX="/usr" install
# Install README and (Debian) changelog
dodoc README.md debian/changelog
}

2
osx.sh
View File

@ -14,6 +14,8 @@ fi
echo "Creating OS X packages for Bitmessage."
export PYBITMESSAGEVERSION=$1
cd src && python2.7 build_osx.py py2app
if [[ $? = "0" ]]; then

View File

@ -2,7 +2,7 @@
APP=pybitmessage
PREV_VERSION=0.4.4
VERSION=0.4.4
VERSION=0.6.0
RELEASE=1
BUILDDIR=~/petbuild
CURRDIR=`pwd`

View File

@ -0,0 +1,58 @@
srcPath = "C:\\src\\PyBitmessage\\src\\"
qtPath = "C:\\Qt\\4.8.6\\"
openSSLPath = "C:\\OpenSSL-1.0.2e\\"
outPath = "C:\\src\\PyInstaller\\bitmessagemain"
# -*- mode: python -*-
a = Analysis([srcPath + 'bitmessagemain.py'],
pathex=[outPath],
hiddenimports=[],
hookspath=None,
runtime_hooks=None)
# fix duplicates
for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
def addTranslations():
import os
extraDatas = []
for file in os.listdir(srcPath + 'translations'):
if file[-3:] != ".qm":
continue
extraDatas.append(('translations\\'+file, srcPath + 'translations\\' + file, 'DATA'))
for file in os.listdir(qtPath + 'translations'):
if file[0:3] != "qt_" or file[5:8] != ".qm":
continue
extraDatas.append(('translations\\'+file, qtPath + 'translations\\' + file, 'DATA'))
return extraDatas
def addUIs():
import os
extraDatas = []
for file in os.listdir(srcPath + 'bitmessageqt'):
if file[-3:] != ".ui":
continue
extraDatas.append(('ui\\'+file, srcPath + 'bitmessageqt\\' + file, 'DATA'))
return extraDatas
# append the translations directory
a.datas += addTranslations()
a.datas += addUIs()
a.binaries.append(('msvcr120.dll', 'C:\\WINDOWS\\system32\\msvcr120.dll', 'BINARY'))
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
a.binaries + [('libeay32.dll', openSSLPath + 'libeay32.dll', 'BINARY'), ('bitmsghash\\bitmsghash32.dll', srcPath + 'bitmsghash\\bitmsghash32.dll', 'BINARY'), ('bitmsghash\\bitmsghash.cl', srcPath + 'bitmsghash\\bitmsghash.cl', 'BINARY'), ('sslkeys\\cert.pem', srcPath + 'sslkeys\\cert.pem', 'BINARY'), ('sslkeys\\key.pem', srcPath + 'sslkeys\\key.pem', 'BINARY')],
name='Bitmessage.exe',
debug=False,
strip=None,
upx=False,
console=False, icon= srcPath + 'images\\can-icon.ico')

View File

@ -1,5 +1,5 @@
Name: pybitmessage
Version: 0.4.4
Version: 0.6.0
Release: 1%{?dist}
Summary: Send encrypted messages
License: MIT

View File

@ -2,7 +2,7 @@
APP=pybitmessage
PREV_VERSION=0.4.4
VERSION=0.4.4
VERSION=0.6.0
RELEASE=1
ARCH_TYPE=`uname -m`
BUILDDIR=~/slackbuild

View File

@ -1,6 +1,7 @@
import hashlib
from struct import *
from pyelliptic import arithmetic
from binascii import hexlify, unhexlify
@ -10,9 +11,9 @@ def convertIntToString(n):
if a[-1:] == 'L':
a = a[:-1]
if (len(a) % 2) == 0:
return a[2:].decode('hex')
return unhexlify(a[2:])
else:
return ('0'+a[2:]).decode('hex')
return unhexlify('0'+a[2:])
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
@ -55,7 +56,7 @@ def decodeBase58(string, alphabet=ALPHABET):
def encodeVarint(integer):
if integer < 0:
print 'varint cannot be < 0'
logger.error('varint cannot be < 0')
raise SystemExit
if integer < 253:
return pack('>B',integer)
@ -66,7 +67,7 @@ def encodeVarint(integer):
if integer >= 4294967296 and integer < 18446744073709551616:
return pack('>B',255) + pack('>Q',integer)
if integer >= 18446744073709551616:
print 'varint cannot be >= 18446744073709551616'
logger.error('varint cannot be >= 18446744073709551616')
raise SystemExit
class varintDecodeError(Exception):
@ -142,7 +143,7 @@ def encodeAddress(version,stream,ripe):
sha.update(currentHash)
checksum = sha.digest()[0:4]
asInt = int(storedBinaryData.encode('hex') + checksum.encode('hex'),16)
asInt = int(hexlify(storedBinaryData) + hexlify(checksum),16)
return 'BM-'+ encodeBase58(asInt)
def decodeAddress(address):
@ -165,7 +166,7 @@ def decodeAddress(address):
#print 'hexdata', hexdata
data = hexdata.decode('hex')
data = unhexlify(hexdata)
checksum = data[-4:]
sha = hashlib.new('sha512')
@ -185,25 +186,25 @@ def decodeAddress(address):
try:
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9])
except varintDecodeError as e:
print e
logger.error(str(e))
status = 'varintmalformed'
return status,0,0,""
#print 'addressVersionNumber', addressVersionNumber
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber
if addressVersionNumber > 4:
print 'cannot decode address version numbers this high'
logger.error('cannot decode address version numbers this high')
status = 'versiontoohigh'
return status,0,0,""
elif addressVersionNumber == 0:
print 'cannot decode address version numbers of zero.'
logger.error('cannot decode address version numbers of zero.')
status = 'versiontoohigh'
return status,0,0,""
try:
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:])
except varintDecodeError as e:
print e
logger.error(str(e))
status = 'varintmalformed'
return status,0,0,""
#print streamNumber
@ -268,7 +269,7 @@ if __name__ == "__main__":
ripe.update(sha.digest())
addressVersionNumber = 2
streamNumber = 1
print 'Ripe digest that we will encode in the address:', ripe.digest().encode('hex')
print 'Ripe digest that we will encode in the address:', hexlify(ripe.digest())
returnedAddress = encodeAddress(addressVersionNumber,streamNumber,ripe.digest())
print 'Encoded address:', returnedAddress
status,addressVersionNumber,streamNumber,data = decodeAddress(returnedAddress)
@ -277,5 +278,5 @@ if __name__ == "__main__":
print 'addressVersionNumber', addressVersionNumber
print 'streamNumber', streamNumber
print 'length of data(the ripe hash):', len(data)
print 'ripe data:', data.encode('hex')
print 'ripe data:', hexlify(data)

View File

@ -1,5 +1,5 @@
# Copyright (c) 2012-2014 Jonathan Warren
# Copyright (c) 2012-2014 The Bitmessage developers
# Copyright (c) 2012-2016 Jonathan Warren
# Copyright (c) 2012-2016 The Bitmessage developers
comment= """
This is not what you run to run the Bitmessage API. Instead, enable the API
@ -12,8 +12,9 @@ if __name__ == "__main__":
import sys
sys.exit(0)
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler, SimpleXMLRPCServer
import json
from binascii import hexlify
import shared
import time
@ -26,7 +27,7 @@ from pyelliptic.openssl import OpenSSL
from struct import pack
# Classes
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute
from helper_sql import sqlQuery,sqlExecute,SqlBulkExecute,sqlStoredProcedure
from debug import logger
# Helper Functions
@ -43,6 +44,13 @@ class APIError(Exception):
def __str__(self):
return "API Error %04i: %s" % (self.error_number, self.error_message)
class StoppableXMLRPCServer(SimpleXMLRPCServer):
def serve_forever(self):
while shared.shutdown == 0:
self.handle_request()
# This is one of several classes that constitute the API
# This class was written by Vaibhav Bhatia. Modified by Jonathan Warren (Atheros).
# http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/
@ -174,7 +182,14 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
return data
def HandleListAddressBookEntries(self, params):
queryreturn = sqlQuery('''SELECT label, address from addressbook''')
if len(params) == 1:
label, = params
label = self._decode(label, "base64")
queryreturn = sqlQuery('''SELECT label, address from addressbook WHERE label = ?''', label)
elif len(params) > 1:
raise APIError(0, "Too many paremeters, max 1")
else:
queryreturn = sqlQuery('''SELECT label, address from addressbook''')
data = '{"addresses":['
for row in queryreturn:
label, address = row
@ -197,8 +212,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
raise APIError(16, 'You already have this address in your address book.')
sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address)
shared.UISignalQueue.put(('rerenderInboxFromLabels',''))
shared.UISignalQueue.put(('rerenderSentToLabels',''))
shared.UISignalQueue.put(('rerenderMessagelistFromLabels',''))
shared.UISignalQueue.put(('rerenderMessagelistToLabels',''))
shared.UISignalQueue.put(('rerenderAddressBook',''))
return "Added address %s to address book" % address
@ -209,8 +224,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
address = addBMIfNotPresent(address)
self._verifyAddress(address)
sqlExecute('DELETE FROM addressbook WHERE address=?', address)
shared.UISignalQueue.put(('rerenderInboxFromLabels',''))
shared.UISignalQueue.put(('rerenderSentToLabels',''))
shared.UISignalQueue.put(('rerenderMessagelistFromLabels',''))
shared.UISignalQueue.put(('rerenderMessagelistToLabels',''))
shared.UISignalQueue.put(('rerenderAddressBook',''))
return "Deleted address book entry for %s if it existed" % address
@ -448,8 +463,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
shared.config.remove_section(address)
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
shared.config.write(configfile)
shared.UISignalQueue.put(('rerenderInboxFromLabels',''))
shared.UISignalQueue.put(('rerenderSentToLabels',''))
shared.UISignalQueue.put(('rerenderMessagelistFromLabels',''))
shared.UISignalQueue.put(('rerenderMessagelistToLabels',''))
shared.reloadMyAddressHashes()
return 'success'
@ -463,7 +478,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
message = shared.fixPotentiallyInvalidUTF8Data(message)
if len(data) > 25:
data += ','
data += json.dumps({'msgid': msgid.encode('hex'), 'toAddress': toAddress, 'fromAddress': fromAddress, 'subject': subject.encode(
data += json.dumps({'msgid': hexlify(msgid), 'toAddress': toAddress, 'fromAddress': fromAddress, 'subject': subject.encode(
'base64'), 'message': message.encode('base64'), 'encodingType': encodingtype, 'receivedTime': received, 'read': read}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -476,7 +491,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
msgid = row[0]
if len(data) > 25:
data += ','
data += json.dumps({'msgid': msgid.encode('hex')}, indent=4, separators=(',', ': '))
data += json.dumps({'msgid': hexlify(msgid)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -501,7 +516,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
msgid, toAddress, fromAddress, subject, received, message, encodingtype, read = row
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
message = shared.fixPotentiallyInvalidUTF8Data(message)
data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': '))
data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received, 'read': read}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -514,7 +529,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
message = shared.fixPotentiallyInvalidUTF8Data(message)
if len(data) > 25:
data += ','
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 += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -525,7 +540,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
msgid = row[0]
if len(data) > 25:
data += ','
data += json.dumps({'msgid':msgid.encode('hex')}, indent=4, separators=(',', ': '))
data += json.dumps({'msgid':hexlify(msgid)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -541,7 +556,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
message = shared.fixPotentiallyInvalidUTF8Data(message)
if len(data) > 25:
data += ','
data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': '))
data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'receivedTime':received}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -555,7 +570,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
message = shared.fixPotentiallyInvalidUTF8Data(message)
data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': '))
data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -572,7 +587,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
message = shared.fixPotentiallyInvalidUTF8Data(message)
if len(data) > 25:
data += ','
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 += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -587,7 +602,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
msgid, toAddress, fromAddress, subject, lastactiontime, message, encodingtype, status, ackdata = row
subject = shared.fixPotentiallyInvalidUTF8Data(subject)
message = shared.fixPotentiallyInvalidUTF8Data(message)
data += json.dumps({'msgid':msgid.encode('hex'), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':ackdata.encode('hex')}, indent=4, separators=(',', ': '))
data += json.dumps({'msgid':hexlify(msgid), 'toAddress':toAddress, 'fromAddress':fromAddress, 'subject':subject.encode('base64'), 'message':message.encode('base64'), 'encodingType':encodingtype, 'lastActionTime':lastactiontime, 'status':status, 'ackData':hexlify(ackdata)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -680,7 +695,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
shared.workerQueue.put(('sendmessage', toAddress))
return ackdata.encode('hex')
return hexlify(ackdata)
def HandleSendBroadcast(self, params):
if len(params) == 0:
@ -737,7 +752,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
toAddress, toLabel, fromAddress, subject, message, ackdata)))
shared.workerQueue.put(('sendbroadcast', ''))
return ackdata.encode('hex')
return hexlify(ackdata)
def HandleGetStatus(self, params):
if len(params) != 1:
@ -779,7 +794,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
raise APIError(16, 'You are already subscribed to that address.')
sqlExecute('''INSERT INTO subscriptions VALUES (?,?,?)''',label, address, True)
shared.reloadBroadcastSendersForWhichImWatching()
shared.UISignalQueue.put(('rerenderInboxFromLabels', ''))
shared.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
shared.UISignalQueue.put(('rerenderSubscriptions', ''))
return 'Added subscription.'
@ -790,7 +805,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
address = addBMIfNotPresent(address)
sqlExecute('''DELETE FROM subscriptions WHERE address=?''', address)
shared.reloadBroadcastSendersForWhichImWatching()
shared.UISignalQueue.put(('rerenderInboxFromLabels', ''))
shared.UISignalQueue.put(('rerenderMessagelistFromLabels', ''))
shared.UISignalQueue.put(('rerenderSubscriptions', ''))
return 'Deleted subscription if it existed.'
@ -836,9 +851,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
TTL = 2.5 * 24 * 60 * 60
shared.inventory[inventoryHash] = (
objectType, toStreamNumber, encryptedPayload, int(time.time()) + TTL,'')
shared.inventorySets[toStreamNumber].add(inventoryHash)
with shared.printLock:
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', inventoryHash.encode('hex')
print 'Broadcasting inv for msg(API disseminatePreEncryptedMsg command):', hexlify(inventoryHash)
shared.broadcastToSendDataQueues((
toStreamNumber, 'advertiseobject', inventoryHash))
@ -884,9 +898,8 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
TTL = 28 * 24 * 60 * 60
shared.inventory[inventoryHash] = (
objectType, pubkeyStreamNumber, payload, int(time.time()) + TTL,'')
shared.inventorySets[pubkeyStreamNumber].add(inventoryHash)
with shared.printLock:
print 'broadcasting inv within API command disseminatePubkey with hash:', inventoryHash.encode('hex')
print 'broadcasting inv within API command disseminatePubkey with hash:', hexlify(inventoryHash)
shared.broadcastToSendDataQueues((
streamNumber, 'advertiseobject', inventoryHash))
@ -921,7 +934,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
payload, = row
if len(data) > 25:
data += ','
data += json.dumps({'data':payload.encode('hex')}, indent=4, separators=(',', ': '))
data += json.dumps({'data':hexlify(payload)}, indent=4, separators=(',', ': '))
data += ']}'
return data
@ -956,6 +969,9 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
message, = params
shared.UISignalQueue.put(('updateStatusBar', message))
def HandleDeleteAndVacuum(self, params):
sqlStoredProcedure('deleteandvacuume')
return 'done'
handlers = {}
handlers['helloWorld'] = HandleHelloWorld
@ -1006,6 +1022,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
handlers['getMessageDataByDestinationTag'] = HandleGetMessageDataByDestinationHash
handlers['clientStatus'] = HandleClientStatus
handlers['decodeAddress'] = HandleDecodeAddress
handlers['deleteAndVacuum'] = HandleDeleteAndVacuum
def _handle_request(self, method, params):
if (self.handlers.has_key(method)):

1778
src/bitmessagecli.py Normal file
View File

@ -0,0 +1,1778 @@
#!/usr/bin/env python2.7.x
# Created by Adam Melton (.dok) referenceing https://bitmessage.org/wiki/API_Reference for API documentation
# Distributed under the MIT/X11 software license. See http://www.opensource.org/licenses/mit-license.php.
# This is an example of a daemon client for PyBitmessage 0.4.2, by .dok (Version 0.3.0)
import ConfigParser
import xmlrpclib
import datetime
import hashlib
import getopt
import imghdr
import ntpath
import json
import time
import sys
import os
api = ''
keysName = 'keys.dat'
keysPath = 'keys.dat'
usrPrompt = 0 #0 = First Start, 1 = prompt, 2 = no prompt if the program is starting up
knownAddresses = dict()
def userInput(message): #Checks input for exit or quit. Also formats for input, etc
global usrPrompt
print '\n' + message
uInput = raw_input('> ')
if (uInput.lower() == 'exit'): #Returns the user to the main menu
usrPrompt = 1
main()
elif (uInput.lower() == 'quit'): #Quits the program
print '\n Bye\n'
sys.exit()
os.exit()
else:
return uInput
def restartBmNotify(): #Prompts the user to restart Bitmessage.
print '\n *******************************************************************'
print ' WARNING: If Bitmessage is running locally, you must restart it now.'
print ' *******************************************************************\n'
def safeConfigGetBoolean(section,field):
global keysPath
config = ConfigParser.SafeConfigParser()
config.read(keysPath)
try:
return config.getboolean(section,field)
except:
return False
#Begin keys.dat interactions
def lookupAppdataFolder(): #gets the appropriate folders for the .dat files depending on the OS. Taken from bitmessagemain.py
APPNAME = "PyBitmessage"
from os import path, environ
if sys.platform == 'darwin':
if "HOME" in environ:
dataFolder = path.join(os.environ["HOME"], "Library/Application support/", APPNAME) + '/'
else:
print ' Could not find home folder, please report this message and your OS X version to the Daemon Github.'
os.exit()
elif 'win32' in sys.platform or 'win64' in sys.platform:
dataFolder = path.join(environ['APPDATA'], APPNAME) + '\\'
else:
dataFolder = path.expanduser(path.join("~", ".config/" + APPNAME + "/"))
return dataFolder
def configInit():
global keysName
config = ConfigParser.SafeConfigParser()
config.add_section('bitmessagesettings')
config.set('bitmessagesettings', 'port', '8444') #Sets the bitmessage port to stop the warning about the api not properly being setup. This is in the event that the keys.dat is in a different directory or is created locally to connect to a machine remotely.
config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat
with open(keysName, 'wb') as configfile:
config.write(configfile)
print '\n ' + str(keysName) + ' Initalized in the same directory as daemon.py'
print ' You will now need to configure the ' + str(keysName) + ' file.\n'
def apiInit(apiEnabled):
global keysPath
global usrPrompt
config = ConfigParser.SafeConfigParser()
config.read(keysPath)
if (apiEnabled == False): #API information there but the api is disabled.
uInput = userInput("The API is not enabled. Would you like to do that now, (Y)es or (N)o?").lower()
if uInput == "y": #
config.set('bitmessagesettings','apienabled','true') #Sets apienabled to true in keys.dat
with open(keysPath, 'wb') as configfile:
config.write(configfile)
print 'Done'
restartBmNotify()
return True
elif uInput == "n":
print ' \n************************************************************'
print ' Daemon will not work when the API is disabled. '
print ' Please refer to the Bitmessage Wiki on how to setup the API.'
print ' ************************************************************\n'
usrPrompt = 1
main()
else:
print '\n Invalid Entry\n'
usrPrompt = 1
main()
elif (apiEnabled == True): #API correctly setup
#Everything is as it should be
return True
else: #API information was not present.
print '\n ' + str(keysPath) + ' not properly configured!\n'
uInput = userInput("Would you like to do this now, (Y)es or (N)o?").lower()
if uInput == "y": #User said yes, initalize the api by writing these values to the keys.dat file
print ' '
apiUsr = userInput("API Username")
apiPwd = userInput("API Password")
apiInterface = userInput("API Interface. (127.0.0.1)")
apiPort = userInput("API Port")
apiEnabled = userInput("API Enabled? (True) or (False)").lower()
daemon = userInput("Daemon mode Enabled? (True) or (False)").lower()
if (daemon != 'true' and daemon != 'false'):
print '\n Invalid Entry for Daemon.\n'
uInput = 1
main()
print ' -----------------------------------\n'
config.set('bitmessagesettings', 'port', '8444') #sets the bitmessage port to stop the warning about the api not properly being setup. This is in the event that the keys.dat is in a different directory or is created locally to connect to a machine remotely.
config.set('bitmessagesettings','apienabled','true')
config.set('bitmessagesettings', 'apiport', apiPort)
config.set('bitmessagesettings', 'apiinterface', '127.0.0.1')
config.set('bitmessagesettings', 'apiusername', apiUsr)
config.set('bitmessagesettings', 'apipassword', apiPwd)
config.set('bitmessagesettings', 'daemon', daemon)
with open(keysPath, 'wb') as configfile:
config.write(configfile)
print '\n Finished configuring the keys.dat file with API information.\n'
restartBmNotify()
return True
elif uInput == "n":
print '\n ***********************************************************'
print ' Please refer to the Bitmessage Wiki on how to setup the API.'
print ' ***********************************************************\n'
usrPrompt = 1
main()
else:
print ' \nInvalid entry\n'
usrPrompt = 1
main()
def apiData():
global keysName
global keysPath
global usrPrompt
config = ConfigParser.SafeConfigParser()
config.read(keysPath) #First try to load the config file (the keys.dat file) from the program directory
try:
config.get('bitmessagesettings','port')
appDataFolder = ''
except:
#Could not load the keys.dat file in the program directory. Perhaps it is in the appdata directory.
appDataFolder = lookupAppdataFolder()
keysPath = appDataFolder + keysPath
config = ConfigParser.SafeConfigParser()
config.read(keysPath)
try:
config.get('bitmessagesettings','port')
except:
#keys.dat was not there either, something is wrong.
print '\n ******************************************************************'
print ' There was a problem trying to access the Bitmessage keys.dat file'
print ' or keys.dat is not set up correctly'
print ' Make sure that daemon is in the same directory as Bitmessage. '
print ' ******************************************************************\n'
uInput = userInput("Would you like to create a keys.dat in the local directory, (Y)es or (N)o?").lower()
if (uInput == "y" or uInput == "yes"):
configInit()
keysPath = keysName
usrPrompt = 0
main()
elif (uInput == "n" or uInput == "no"):
print '\n Trying Again.\n'
usrPrompt = 0
main()
else:
print '\n Invalid Input.\n'
usrPrompt = 1
main()
try: #checks to make sure that everyting is configured correctly. Excluding apiEnabled, it is checked after
config.get('bitmessagesettings', 'apiport')
config.get('bitmessagesettings', 'apiinterface')
config.get('bitmessagesettings', 'apiusername')
config.get('bitmessagesettings', 'apipassword')
except:
apiInit("") #Initalize the keys.dat file with API information
#keys.dat file was found or appropriately configured, allow information retrieval
apiEnabled = apiInit(safeConfigGetBoolean('bitmessagesettings','apienabled')) #if false it will prompt the user, if true it will return true
config.read(keysPath)#read again since changes have been made
apiPort = int(config.get('bitmessagesettings', 'apiport'))
apiInterface = config.get('bitmessagesettings', 'apiinterface')
apiUsername = config.get('bitmessagesettings', 'apiusername')
apiPassword = config.get('bitmessagesettings', 'apipassword')
print '\n API data successfully imported.\n'
return "http://" + apiUsername + ":" + apiPassword + "@" + apiInterface+ ":" + str(apiPort) + "/" #Build the api credentials
#End keys.dat interactions
def apiTest(): #Tests the API connection to bitmessage. Returns true if it is connected.
try:
result = api.add(2,3)
except:
return False
if (result == 5):
return True
else:
return False
def bmSettings(): #Allows the viewing and modification of keys.dat settings.
global keysPath
global usrPrompt
config = ConfigParser.SafeConfigParser()
keysPath = 'keys.dat'
config.read(keysPath)#Read the keys.dat
try:
port = config.get('bitmessagesettings', 'port')
except:
print '\n File not found.\n'
usrPrompt = 0
main()
startonlogon = safeConfigGetBoolean('bitmessagesettings', 'startonlogon')
minimizetotray = safeConfigGetBoolean('bitmessagesettings', 'minimizetotray')
showtraynotifications = safeConfigGetBoolean('bitmessagesettings', 'showtraynotifications')
startintray = safeConfigGetBoolean('bitmessagesettings', 'startintray')
defaultnoncetrialsperbyte = config.get('bitmessagesettings', 'defaultnoncetrialsperbyte')
defaultpayloadlengthextrabytes = config.get('bitmessagesettings', 'defaultpayloadlengthextrabytes')
daemon = safeConfigGetBoolean('bitmessagesettings', 'daemon')
socksproxytype = config.get('bitmessagesettings', 'socksproxytype')
sockshostname = config.get('bitmessagesettings', 'sockshostname')
socksport = config.get('bitmessagesettings', 'socksport')
socksauthentication = safeConfigGetBoolean('bitmessagesettings', 'socksauthentication')
socksusername = config.get('bitmessagesettings', 'socksusername')
sockspassword = config.get('bitmessagesettings', 'sockspassword')
print '\n -----------------------------------'
print ' | Current Bitmessage Settings |'
print ' -----------------------------------'
print ' port = ' + port
print ' startonlogon = ' + str(startonlogon)
print ' minimizetotray = ' + str(minimizetotray)
print ' showtraynotifications = ' + str(showtraynotifications)
print ' startintray = ' + str(startintray)
print ' defaultnoncetrialsperbyte = ' + defaultnoncetrialsperbyte
print ' defaultpayloadlengthextrabytes = ' + defaultpayloadlengthextrabytes
print ' daemon = ' + str(daemon)
print '\n ------------------------------------'
print ' | Current Connection Settings |'
print ' -----------------------------------'
print ' socksproxytype = ' + socksproxytype
print ' sockshostname = ' + sockshostname
print ' socksport = ' + socksport
print ' socksauthentication = ' + str(socksauthentication)
print ' socksusername = ' + socksusername
print ' sockspassword = ' + sockspassword
print ' '
uInput = userInput("Would you like to modify any of these settings, (Y)es or (N)o?").lower()
if uInput == "y":
while True: #loops if they mistype the setting name, they can exit the loop with 'exit'
invalidInput = False
uInput = userInput("What setting would you like to modify?").lower()
print ' '
if uInput == "port":
print ' Current port number: ' + port
uInput = userInput("Enter the new port number.")
config.set('bitmessagesettings', 'port', str(uInput))
elif uInput == "startonlogon":
print ' Current status: ' + str(startonlogon)
uInput = userInput("Enter the new status.")
config.set('bitmessagesettings', 'startonlogon', str(uInput))
elif uInput == "minimizetotray":
print ' Current status: ' + str(minimizetotray)
uInput = userInput("Enter the new status.")
config.set('bitmessagesettings', 'minimizetotray', str(uInput))
elif uInput == "showtraynotifications":
print ' Current status: ' + str(showtraynotifications)
uInput = userInput("Enter the new status.")
config.set('bitmessagesettings', 'showtraynotifications', str(uInput))
elif uInput == "startintray":
print ' Current status: ' + str(startintray)
uInput = userInput("Enter the new status.")
config.set('bitmessagesettings', 'startintray', str(uInput))
elif uInput == "defaultnoncetrialsperbyte":
print ' Current default nonce trials per byte: ' + defaultnoncetrialsperbyte
uInput = userInput("Enter the new defaultnoncetrialsperbyte.")
config.set('bitmessagesettings', 'defaultnoncetrialsperbyte', str(uInput))
elif uInput == "defaultpayloadlengthextrabytes":
print ' Current default payload length extra bytes: ' + defaultpayloadlengthextrabytes
uInput = userInput("Enter the new defaultpayloadlengthextrabytes.")
config.set('bitmessagesettings', 'defaultpayloadlengthextrabytes', str(uInput))
elif uInput == "daemon":
print ' Current status: ' + str(daemon)
uInput = userInput("Enter the new status.").lower()
config.set('bitmessagesettings', 'daemon', str(uInput))
elif uInput == "socksproxytype":
print ' Current socks proxy type: ' + socksproxytype
print "Possibilities: 'none', 'SOCKS4a', 'SOCKS5'."
uInput = userInput("Enter the new socksproxytype.")
config.set('bitmessagesettings', 'socksproxytype', str(uInput))
elif uInput == "sockshostname":
print ' Current socks host name: ' + sockshostname
uInput = userInput("Enter the new sockshostname.")
config.set('bitmessagesettings', 'sockshostname', str(uInput))
elif uInput == "socksport":
print ' Current socks port number: ' + socksport
uInput = userInput("Enter the new socksport.")
config.set('bitmessagesettings', 'socksport', str(uInput))
elif uInput == "socksauthentication":
print ' Current status: ' + str(socksauthentication)
uInput = userInput("Enter the new status.")
config.set('bitmessagesettings', 'socksauthentication', str(uInput))
elif uInput == "socksusername":
print ' Current socks username: ' + socksusername
uInput = userInput("Enter the new socksusername.")
config.set('bitmessagesettings', 'socksusername', str(uInput))
elif uInput == "sockspassword":
print ' Current socks password: ' + sockspassword
uInput = userInput("Enter the new password.")
config.set('bitmessagesettings', 'sockspassword', str(uInput))
else:
print "\n Invalid input. Please try again.\n"
invalidInput = True
if invalidInput != True: #don't prompt if they made a mistake.
uInput = userInput("Would you like to change another setting, (Y)es or (N)o?").lower()
if uInput != "y":
print '\n Changes Made.\n'
with open(keysPath, 'wb') as configfile:
config.write(configfile)
restartBmNotify()
break
elif uInput == "n":
usrPrompt = 1
main()
else:
print "Invalid input."
usrPrompt = 1
main()
def validAddress(address):
address_information = api.decodeAddress(address)
address_information = eval(address_information)
if 'success' in str(address_information.get('status')).lower():
return True
else:
return False
def getAddress(passphrase,vNumber,sNumber):
passphrase = passphrase.encode('base64')#passphrase must be encoded
return api.getDeterministicAddress(passphrase,vNumber,sNumber)
def subscribe():
global usrPrompt
while True:
address = userInput("What address would you like to subscribe to?")
if (address == "c"):
usrPrompt = 1
print ' '
main()
elif (validAddress(address)== False):
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
label = userInput("Enter a label for this address.")
label = label.encode('base64')
api.addSubscription(address,label)
print ('\n You are now subscribed to: ' + address + '\n')
def unsubscribe():
global usrPrompt
while True:
address = userInput("What address would you like to unsubscribe from?")
if (address == "c"):
usrPrompt = 1
print ' '
main()
elif (validAddress(address)== False):
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
uInput = userInput("Are you sure, (Y)es or (N)o?").lower()
api.deleteSubscription(address)
print ('\n You are now unsubscribed from: ' + address + '\n')
def listSubscriptions():
global usrPrompt
#jsonAddresses = json.loads(api.listSubscriptions())
#numAddresses = len(jsonAddresses['addresses']) #Number of addresses
print '\nLabel, Address, Enabled\n'
try:
print api.listSubscriptions()
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
'''for addNum in range (0, numAddresses): #processes all of the addresses and lists them out
label = jsonAddresses['addresses'][addNum]['label']
address = jsonAddresses['addresses'][addNum]['address']
enabled = jsonAddresses['addresses'][addNum]['enabled']
print label, address, enabled
'''
print ' '
def createChan():
global usrPrompt
password = userInput("Enter channel name")
password = password.encode('base64')
try:
print api.createChan(password)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def joinChan():
global usrPrompt
while True:
address = userInput("Enter channel address")
if (address == "c"):
usrPrompt = 1
print ' '
main()
elif (validAddress(address)== False):
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
password = userInput("Enter channel name")
password = password.encode('base64')
try:
print api.joinChan(password,address)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def leaveChan():
global usrPrompt
while True:
address = userInput("Enter channel address")
if (address == "c"):
usrPrompt = 1
print ' '
main()
elif (validAddress(address)== False):
print '\n Invalid. "c" to cancel. Please try again.\n'
else:
break
try:
print api.leaveChan(address)
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
def listAdd(): #Lists all of the addresses and their info
global usrPrompt
try:
jsonAddresses = json.loads(api.listAddresses())
numAddresses = len(jsonAddresses['addresses']) #Number of addresses
except:
print '\n Connection Error\n'
usrPrompt = 0
main()
#print '\nAddress Number,Label,Address,Stream,Enabled\n'
print '\n --------------------------------------------------------------------------'
print ' | # | Label | Address |S#|Enabled|'
print ' |---|-------------------|-------------------------------------|--|-------|'
for addNum in range (0, numAddresses): #processes all of the addresses and lists them out
label = str(jsonAddresses['addresses'][addNum]['label'])
address = str(jsonAddresses['addresses'][addNum]['address'])
stream = str(jsonAddresses['addresses'][addNum]['stream'])
enabled = str(jsonAddresses['addresses'][addNum]['enabled'])
if (len(label) > 19):
label = label[:16] + '...'
print ' |' + str(addNum).ljust(3) + '|' + label.ljust(19) + '|' + address.ljust(37) + '|' + stream.ljust(1), '|' + enabled